短链接day7

短链接监控

统计短链接PV访问

mybatis中@Param的作用:

@Param的作用就是给参数命名,比如在mapper里面某方法A(int id),当添加注解后A(@Param("userId") int id),也就是说外部想要取出传入的id值,只需要取它的参数名userId就可以了。将参数值传如SQL语句中,通过#{userId}进行取值给SQL的参数赋值。

【详解】@Param注解的用法-优快云博客

编写插入语句时,一直不知道是不是sql语句哪里错了,一直报错null,然后直接就去gitee复制的,

 /**
     * 记录基础访问监控数据
     */
    @Insert("INSERT INTO " +
            "t_link_access_stats (full_short_url, date, pv, uv, uip, hour, weekday, create_time, update_time, del_flag) " +
            "VALUES( #{linkAccessStats.fullShortUrl}, #{linkAccessStats.date}, #{linkAccessStats.pv}, #{linkAccessStats.uv}, #{linkAccessStats.uip}, #{linkAccessStats.hour}, #{linkAccessStats.weekday}, NOW(), NOW(), 0) " +
            "ON DUPLICATE KEY UPDATE pv = pv +  #{linkAccessStats.pv}, uv = uv + #{linkAccessStats.uv}, uip = uip + #{linkAccessStats.uip};")
    void shortLinkStats(@Param("linkAccessStats") LinkAccessStatsDO linkAccessStatsDO);

结果这个代码就会出现一个问题,唯一索引存在时,不会更新,而是直接插入,检查后发现是因为没有加入gid的值,更改后代码为:

 /**
     * 记录基础访问监控数据
     */
    @Insert("INSERT INTO " +
            "t_link_access_stats (gid,full_short_url, date, pv, uv, uip, hour, weekday, create_time, update_time, del_flag) " +
            "VALUES(#{linkAccessStats.gid},#{linkAccessStats.fullShortUrl}, #{linkAccessStats.date}, #{linkAccessStats.pv}, #{linkAccessStats.uv}, #{linkAccessStats.uip}, #{linkAccessStats.hour}, #{linkAccessStats.weekday}, NOW(), NOW(), 0) " +
            "ON DUPLICATE KEY UPDATE pv = pv +  #{linkAccessStats.pv}, uv = uv + #{linkAccessStats.uv}, uip = uip + #{linkAccessStats.uip};")
    void shortLinkStats(@Param("linkAccessStats") LinkAccessStatsDO linkAccessStatsDO);

统计短链接UV访问

根据cookie判断是否是同一个用户

 AtomicBoolean:

uvFirstFlag是一个AtomicBoolean类型的变量,它是线程安全的。AtomicBoolean提供了原子操作,可以确保多个线程之间的可见性和一致性。在addResponseCookieTask任务中,uvFirstFlag被设置为Boolean.TRUE,而在另一个线程中通过uvFirstFlag.get()来读取它的值。由于AtomicBoolean的特性,这个读取操作是安全的,不会受到其他线程的影响。因此,不会出现脏数据的问题。uvFirstFlag的值在多个线程之间是同步和可靠的。

Java并发基础:原子类之AtomicBoolean全面解析-优快云博客

解释:

  • Arrays.stream(cookies):将cookies数组转换为一个流(Stream),以便进行链式操作。
  • .filter(each->Objects.equals(each.getName(),"uv")):过滤流中的元素,只保留那些其getName()方法返回"uv"的Cookie对象。
  • .findFirst():返回流中的第一个元素(如果有的话),这里指的是第一个名为"uv"的cookie。
  • .map(Cookie::getValue):将找到的cookie对象映射为其值(即cookie的内容)。
  • .ifPresentOrElse(each, {...}, addResponseCookieTask):这是一个Optional类的方法,用于处理上一步map操作的结果。如果Optional包含值(即找到了名为"uv"的cookie),则执行第一个lambda表达式(将cookie的值添加到Redis集合中,并设置uvFirstFlag标志);如果不包含值(即没有找到名为"uv"的cookie),则执行第二个参数addResponseCookieTask,这是一个Runnable,用于添加一个新的"uv" cookie到HTTP响应中。

 如果cookies数组为空,则直接执行addResponseCookieTask.run(),即添加一个新的"uv" cookie到HTTP响应中。

AtomicBoolean uvFirstFlag=new AtomicBoolean();
        Cookie[] cookies = ((HttpServletRequest) request).getCookies();
        try {
            Runnable addRespronseCookieTask=()->{
                String uv = UUID.fastUUID().toString();
                Cookie uvCookie = new Cookie("uv", uv);
                uvCookie.setMaxAge(60*60*24*30);
//得到后缀                
uvCookie.setPath(StrUtil.sub(fullShortUrl,fullShortUrl.indexOf("/"),fullShortUrl.length()));
                ((HttpServletResponse) response).addCookie(uvCookie);
                uvFirstFlag.set(Boolean.TRUE);
                stringRedisTemplate.opsForSet().add("short-link:stats:uv:"+fullShortUrl,uv);
            };
            if(ArrayUtil.isNotEmpty(cookies)){
                Arrays.stream(cookies)
                        .filter(each->Objects.equals(each.getName(),"uv"))
                        .findFirst()
                        .map(Cookie::getValue)
                        .ifPresentOrElse(each->{
                            Long added=stringRedisTemplate.opsForSet().add("short-link:stats:uv:"+fullShortUrl,each);
                            uvFirstFlag.set(added!=null&&added>0L);
                        },addRespronseCookieTask);
            }else{
                addRespronseCookieTask.run();
            }
            ...
        }catch (Throwable ex){
            log.error("短链接访问量统计异常",ex);
        }

统计短链接IP访问

String remoteAddr=LinkUtil.getIp((HttpServletRequest) request);
            Long uipAdded = stringRedisTemplate.opsForSet().add("short-link:stats:uip:" + fullShortUrl, remoteAddr);
            //uipAdded 的返回值是一个 Long 类型的值,表示成功添加到集合中的新元素的数量,而 uipFirstFlag 则用于判断 remoteAddr 是否是首次被添加到集合中。
            boolean uipFirstFlag=uipAdded!=null&&uipAdded>0L;
    /**
     * 获取请求的 IP 地址
     * @param request
     * @return
     */
    public static String getIp(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");

        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }

        return ipAddress;
    }

统计短链接地区访问

IP-API.com - Geolocation API

  • 支持国内外IP地址
  • 但是使用API限制较多

IP定位-API文档-开发指南-Web服务 API | 高德地图API

  • 仅支持国内IP地址
  • 使用API限制宽松

最终决定使用高德 IP API 定位接口。

@Value :

@Value 是 Spring 框架中的一个非常有用的注解,它主要用于将配置文件中的值注入到 Spring 管理的 Bean 的字段中。这个注解通常与 Spring 的表达式语言(Spring Expression Language, SpEL)一起使用,来访问配置文件中的值、环境变量、系统属性等。

统计短链接操作系统访问

LinkOsStatsDO linkOsStatsDO = LinkOsStatsDO.builder()
                        .os(LinkUtil.getOs((HttpServletRequest) request))
                        .cnt(1)
                        .fullShortUrl(fullShortUrl)
                        .gid(gid)
                        .date(new Date())
                        .build();
                linkOsStatsMapper.shortLinkOsState(linkOsStatsDO);
/**
     * 获取用户访问操作系统
     *
     * @param request 请求
     * @return 访问操作系统
     */
    public static String getOs(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        if (userAgent.toLowerCase().contains("windows")) {
            return "Windows";
        } else if (userAgent.toLowerCase().contains("mac")) {
            return "Mac OS";
        } else if (userAgent.toLowerCase().contains("linux")) {
            return "Linux";
        } else if (userAgent.toLowerCase().contains("unix")) {
            return "Unix";
        } else if (userAgent.toLowerCase().contains("android")) {
            return "Android";
        } else if (userAgent.toLowerCase().contains("iphone")) {
            return "iOS";
        } else {
            return "Unknown";
        }
    }

统计短链接浏览器访问

同上

统计短链接高频IP访问

统计短链接访客类型访问

统计短链接访问设备访问

统计短链接访问网络访问

开发访问单个短链接监控统计功能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值