场景以及目的
场景:
如果我们开发了一个网站, 其中有一些企业页面,上面可能会有类似访问量
,或者人气值
之类的字段。
那么很有可能,会遇到部分企业用户恶意刷新页面,以提高该访问量的值,对网站本身造成压力。
同时因页面生成代价过大或者访问量提高后,为了优化,或者做某种CDN,我们很可能会对该页面进行整页缓存。
那么如何在这两种场景下,对页面进行访问次数的统计?
这里以AspNetMvc程序为例,给出一个简单方案。
目的:
- 当整页缓存(OutPutCache或某种CDN方案)时,进行该页面的访问次数统计;
- 更灵活的定义用户有效访问次数(某时间段内,每ip仅算一次有效访问)。
解决方案:触发统计+数据暂存+定时汇总
页面访问次数,最开始的处理流程可能是这样的:
- 用户访问一个企业首页;
- 服务器收到Http请求,进入一个控制器的Action中调用方法更新数据库中的访问次数字段;
- 返回页面,页面上的访问次数比上一次显示的值大。
加了整页缓存后,上面的流程不再有效。
服务器端不再重新运行一遍action中的代码。
在页面缓存仍然有效的情况下,直接返回缓存的页面。
这时候,我们必然需要另一个接口去触发统计的需求。
这里要注意整页缓存后,页面上的用户登陆状态必然也需要异步加载。
既然无论如何会触发一个ajax请求,不如直接用这个ajax请求触发页面访问次数的统计。
新的流程:
- 用户访问一个企业首页;
- 页面缓存有效的情况下,直接返回缓存的页面;缓存失效,则重新提取数据给出页面,但移除访问次数累加的相关代码;
- 返回页面,页面上的访问次数不包含本次访问;
- 浏览器加载完页面后,触发ajax去请求用户的登陆状态,同时进行访问次数的统计。
考虑到每次进行访问次数的累加都直接操作数据库的话,会造成过多的数据库io。
我们可以先把相关数据放在cache中,然后定时处理。
假设请求用户登陆状态的接口名为hello
。
当触发hello请求后,我们根据url_referer,判断hello请求来自哪个页面。
并根据url中的参数信息,识别出是哪个企业的首页需要累加访问次数。
同时可以关联访问者的ip,设置5分钟内该ip的多次访问在cache中只算一次。
可扩展性考虑:
- 统计的触发可以不使用ajax,采用一个隐藏的img标签,其src指向独立的统计接口即可,这样对于浏览器未启用js的情况也适用。而且方便部署独立的统计服务器。
- 统计数据的暂存,可以不放到.Net内置的Cache中,转而使用分布式缓存,比如memcache。
- 定时器简单的情况下可以在web程序内使用Timer,但这样必须考虑iis回收的情况。另一种方案是结合memcache的情况下,可以写个独立的服务,定时操作memcache中暂存的数据进行汇总。
参考实现
这里给出一个简单实现。
因本身项目的架构,hello接口在另一个二级域名下,ajax部分使用了jsonp:
hello 接口的后端基本实现参考《jsonp跨域调用数据的前后台说明》。
其中hello接口业务代码中的访问统计部分会调用如下方法(这里针对多个页面进行访问次数统计,使用正则判断url类别):
简单设置定时器,在Global中:
统计汇总的方法如下(隐去了数据库表名):