案例页访问统计
访问统计功能上线
后端实现想法记录一下:
- 基本上是利用HttpServletRequest的get系列方法,来获取请求中包含的headers,参数,以及cookie信息,并转化为javabean,json或者写入数据库
方案一:通过Servlet filter实现
- 结果:pass
- 原因:无法针对每一个案例页面分别统计。
- url-pattern的wild匹配模式只支持zhuyu.xyz/*,和*end这两种,我想匹配的真实请求都是zhuyu.xyz/xxxservlet?action=xxx,满足不了。
方案二:应用层增加一个方法M并绑定请求A
- 请求A保证只与案例页面的访问关联,这样每次访问该页面就会调用方法M,然后M不停的累计、到了一定程度可以把count写库。
- 结果:pass
- 原因:既然统计了,就索性将用户访问的关键数据都记录吧,于是数据库增加一张表+javabean老老实实加起来。
方案三:增加数据库表,通过javabean关联,并创建应用层方法。
- 结果:OK
几个注意的点:
- 请求数量统一,将查库写库合并到一个请求了
- 根据cid查询count是比较快的,但是并没有必要每次都写入库。这里设置了一些条件,有相同的访问(不自觉地利用了缓存特性……)就先加到一个Stack集合里,当访问累计达到2或者第二次访问时间差大于60s,就写入(这个判断条件可以根据访问频率来优化,这里也可以通过定时器来处理)
- mybatis的一级缓存问题。当我自己用浏览器A第一次访问时,发送的请求时是1,此时后端执行的Sqlsession假设为a并进行了缓存,当我再次访问并达到了写入边界进行写库时,按理说写库的Sqlsession应该也是a,那么写库之后涉及到update操作,会自动更新掉LocalCache,第三次访问的时候,返回的count结果应该是最新的,但并不是。
- 解决办法就是,判断写库操作,如果true,就新建一个CaseCountServiceImp对象,利用它再次查询count并将结果更新,返回前端,并将原有的CaseCountServiceImp对象引用到这个新的对象,相当于避开了查询缓存。
局部代码如下:
if(addCount(req,resp)){ //新建一个对象并调用方法,以避开缓存num1 CaseCountService newCount = new CaseCountServiceImp(); visitCount = newCount.selectCount(cid); //更新引用,以之为新的缓存参数num2,否则还是按照缓存num1进行查询 caseCountService = newCount; System.out.println("更新库后的访问统计数为:"+visitCount); }
依旧存在的问题:
- 重复刷新问题,后续可以在后端增加过滤
- cookie还有些问题
- 其实还是没有办法避免机器人
最后贴一篇研究mybatis缓存的文章,写的很清晰:
聊聊MyBatis缓存机制/2018/01/19
代码显示有问题啊,语法高亮呢?
host,remoteAddress,cookie,referer这四个关键信息都有问题,后续再排查下
搞定。同时优化了统计逻辑。严格限制了非登录状态的无意义刷新,用户登陆状态下的间隔刷新。重复访问不会被计入。
nginx的proxy_pass会导致后端从header拿到的ip变成127.0.0.1,会导致referer为null。在nginx.conf中,找到转发对应的location配置,在下面添加
proxy_set_header X-Real-IP $remote_addr;
,proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
,proxy_set_header Real_referer $http_referer;
,然后再在后端通过req.getHeaders("X-Real-IP")
和req.getHeaders("Real_referer")
获取到真实的ip和referer关于访问统计策略,最后利用了cookie在前后端传递记录,并通过ajax辅助查询。未登陆状态严格筛选,同ip同useragent被认定为相同访问并过滤;已登陆状态,则限制访问间隔,超过1h才会count+1。