【任务12】数据统计与拦截:统计网站每日访问IP,PV,UV
本问题对应的项目链接:从0设计一个web项目
本次打卡要求:
1、在你的首页留一个小空间,展示今日与昨日的PV,IP,UV
2、提供三个接口,getPV, getIP, getUV,并且给出这三个接口的使用文档,就是你把我当作第三方,我想调用你这个接口。
3、简单说一下你是如何统计 pv, ip, 和 uv 的。
本问题对应的项目链接:从0设计一个web项目
本次打卡要求:
1、在你的首页留一个小空间,展示今日与昨日的PV,IP,UV
2、提供三个接口,getPV, getIP, getUV,并且给出这三个接口的使用文档,就是你把我当作第三方,我想调用你这个接口。
3、简单说一下你是如何统计 pv, ip, 和 uv 的。
共以下 5 个回答
接口文档
getIp() getUv() getPv()
请求方式get
参数 startday 开始日期(String) endday 结束日期(String)
开始日期和结束日期都为空时默认返回当天信息
开始日期和结束日期有一项为空时返回不为空那天的信息
日期格式为yyyy-MM-dd
统计方式
使用redis进行存储,其中uv用键值对存储,ip和pv用set存储
在通过登陆拦截后,判断请求的地址是否为主页地址,如果是就添加到redis数据库中
查看其他同学的打卡记录,发现还是存在一些问题,记录下来并进行改进
1. 访问页面时,即使不登录uv的数量也增加
2. redis数据结构hyperloglog
3. 数据持久化问题
GitHub地址:https://github.com/lei342345795/FromZerotoExpert
获取网站PV、UV、IP统计 接口文档
GET /monitor/getPVGET /monitor/getUVGET /monitor/getIP请求参数
返回数据
网站UV、PV、IP数
结果示例
如何统计
github 地址(点击即可)
统计网站的IP,PV,UV
IP:独立ip地址。一天之内同一个ip地址多次访问网站,只记为1。
PV:页面的浏览次数。用户每刷新或打开一次页面,记为1次。
UV:独立访问者。一天之内同一个访客的多次访问,记为1次。
开始网站访问数据统计前,我还是先填下之前留下的一些坑,
对于之前的登录拦截实现,我是用后端的一个接口isLogin结合前端的路由拦截(前端指定的路由跳转都会去向isLogin这个接口去请求看用户是否位于登录态,准确的来说,前端所做的工作叫路由守卫。)判断用户是否具有跳转资格,这样的确可以实现这个功能,但后端还是存在安全隐患,因为用户可以不通过我们的前端代码,而是直接写一个请求跳过isLogin接口直接向后端指定接口发送请求做一些操作,虽然那个接口可能有数据验证等,但不能确保用户不合规范的操作可能对系统数据造成一定影响。我之所以采用这种方法进行登录拦截,其根本思想就是想拦截所有发送给后端的请求再做验证,但我是在前端实现,有些不安全。做登录拦截时对拦截器的概念还不清楚,为什么有拦截器的存在,拦截器就是面向切面编程,AOP的思想,想一想,比如处理每一个请求前,都要做统一权限验证,处理每一个请求后都要做同一的资源清理,如果不用拦截器,也可以,但你需要在处理每一个请求前后加上统一的代码,就会有大量的代码冗余,统计网站的数据功能可能也会借助拦截器实现。之前我做登录拦截时可能是想自己实现一个拦截器,却在前端实现,Spring给我们提供了拦截器,我们只需继承并修改它就行了。实现的话:需要一个拦截器和一个配置类,将拦截器注册就行了。
自定义拦截器类MyInterceptor
配置类CrosConfig(因为我的项目是前后端分离,所以需要跨域,也要在这个类中注册我们自定义的拦截器类)
怎么统计当天的ip、uv、pv并持久化
借助redis,为什么使用redis,redis的数据主要在缓存中,查询速度快,不使用mysql,因为mysql的数据主要在磁盘中。
题外话,上网查查mysql和redis的区别和使用场景
查询速度:mysql的数据主要存储在磁盘中,虽然mysql使用了先写日志的策略和位于内存中的buffer pool来加快数据的操作,但还是比不上redis对数据的操作,因为redis的数据在内存中,redis只有在数据持久化的时候才会使用磁盘。对于网站的ip、uv、pv,它们访问修改比较频繁,并且数据的大小不大,可以放在内存中,加快速度。
区别:mysql的数据是以表的数据来存储的,属于关系型数据库,redis的数据是以键值对的形式存储,属于非关系数据库,redis的键只能是字符串类型,但值有多种类型,主要的五大类型是字符串、哈希、列表、集合、有序集合。
怎么用redis实现:
一个键值对:键类型为字符串类型,键值为当天日期;值类型为哈希类型,值的值为三个哈希对:、、。过期时间设置为当天过期。
一个键值对:键值为ip,值类型为集合,值的值为客户端ip地址。
在拦截器中实现:每有一个请求被拦截,pv就加一,然后去键为ip的集合中看看ip是否存在,不存在ip就加一,并把ip加入到集合中。
持久化:
设置一个定时任务,定时执行,比如凌晨就把当天统计的网站访问数据写入到mysql中,就行了。因为往期的网站访问数据都在mysql表中,就好操作了。
你可以定义一个实体类,然后根据业务实现接口,比如查哪一天的网站访问数据(ip、uv、pv),提供给外部。
怎么展示当天的网站浏览数据:
方法一:前端定时请求。简单,当增加了服务器的压力,比如网站的访问数据一直没变化,你却一直给网站发请求要它的访问数据。
方法二:因为当天的网站数据是实时变化,这个场景非常适合用设计模式中的观察者模式(当被观察者自身数据发送变化时,被观察者主动告知观察者新的数据),当被观察者是服务器,服务器需要主动向客户端推送信息,这时需要用到WebSocket全双工通信协议。如果你想获得当天网站的实时访问数据,却不想去网站,网站可以提供一个接口,你访问这个接口,网站就可以把你注册为观察者,网站就可以直接向你告知自身访问数据的变化。
代码有些杂,放上WenSocket协议的后端实现(一个WebSocket类)。
用谷歌浏览器一直没登录上,必须需要自己该浏览器配置吗?
另外就是,你的统计有一些建议:
1、为了防止定时任务没有统计到数据,写入数据库中,过期时间可以多一点勒。
2、另外就是,UV不一定就要用户登录哦,比如一些网站在统计UV时,即使用户没登录也是可以统计的哦。
好像是我实现前后端websocket协议连接和跨域的一些问题,我再改改,今天用其它电脑测试下,不能登录,我的电脑好像可以登录
1.PV,UV,IP的展示:http://123.57.142.33:8088/FromZerotoExpert/
2.接口的使用文档
①getIP
功能:统计当天的IP数。
参数:无
返回值类型:String
说明:此接口利用redis数据库将IP数据实时展示在前端页面。无需操作,定时任务自动调用展示。
②getUV
功能:统计当天的UV数。
参数:无
返回值类型:String
说明:此接口利用redis数据库将UV数据实时展示在前端页面。无需操作,定时任务自动调用展示。
③getPV
功能:统计当天的PV数。
参数:无
返回值类型:String
说明:此接口利用redis数据库将PV数据实时展示在前端页面。无需操作,定时任务自动调用展示。
④getRangeWebInformation
功能:统计n天前的IP, PV, UV数据总量。范围查询。
参数类型:Integer
参数含义:n天前的数据
返回值类型:Map<String, String>
说明:此接口利用前端页面的输入框和查询按钮进行n天前的数据总量统计查询。返回结果{“ip”:ip, “uv”:uv, “pv”:pv}。
3.实现方式
①统计PV
设置PV拦截器,当请求经过拦截器时,将redis记录的pv数加一,其中redis记录pv的key为web:pv:+当前时间(年月日),redis记录的key为PV数,初始为0,每当请求被拦截时加一。使用的redis数据类型为string类型。
②统计UV
设置UV拦截器,当请求被拦截时,给客户端设置一个Cookie当作唯一标识,名为pv_cookie_id,值是一个唯一的字符串。并通过当前时间,计算出到今天23:59:59的剩余时间,当作Cookie的持久化存储时间,当同一客户端的请求在当天再次访问时,Cookie检测存在,不进行redis的存储,只有Cookie不存在时,才将Cookie的唯一字符值传入到redis存储,当第二天时,所有Cookie都失效,UV会被重新统计。其中redis记录uv的key为web:uv:+当前时间(年月日),redis记录的值为Cookie中存储的唯一标识,redis使用的数据类型为HyperLogLog。
③统计IP
设置IP拦截器,当请求被拦截时,根据request解析用户ip,并在redis中进行存储,其中redis记录ip的key为web:ip:+当前时间(年月日),redis记录的值为用户ip,redis使用的数据类型为HyperLogLog。
④注册拦截器
注册拦截器需要注意,除了放行静态资源,还需要把一些重定向的中转接口,数据的查询接口,存储接口等放行,不然一些定时任务的触发会被拦截器拦截,使统计信息不够准确。
4.持久化存储
每天晚上23:59会执行定时任务把所有redis数据存储至mysql数据库,对于当天的数据查询均在redis中操作,对于以前的数据查询,均在mysql中进行操作。
gitHub地址:https://github.com/Second6s9/FromZerotoExpert/tree/dev
gitHub防覆盖地址:https://github.com/Second6s9/FromZerotoExpert/tree/day12
服务器地址:http://123.57.142.33:8088/FromZerotoExpert/
这页面可以啊
哈哈哈,我还觉得有点丑呐。。。
PV: 用户每次刷新网页 + 1; redis中存:fU:pv:当前日期 = 0; 使用 字符串
UV: 通过用户浏览器中的 Cookie:【fzteUser = 用户账号】来确认一个访客;redis中存:fU:uv:当前日期 = 用户账号;使用 HyperLogLog
如何将这些数据保存起来?
–> 就存在redis中,设置有效期为 30 天
–> 定时任务:每日0点以后将 redis 中的这些数据转移到 mysql 中,同时 mysql 删除30天以前的数据 (10.10升级到了这种):使用了 Spring 中的定时任务功能
部分实现代码如下:
前端会的很少,所有只能写出下面这种代码:
ps: 这个 PV 帮我找到了jedis连接池一直不够的原因:拦截器中忘记屏蔽【发送心跳信息】和【显示在线人数】两个接口了,每次访问这两个接口还要用 jedis 验证一遍唯一登录凭证,感觉没有必要;屏蔽之后最大连接数 100 就稳稳的了。
服务器链接:http://124.223.184.187:8088/FromZerotoExpert
Github仓库链接:https://github.com/jiangyklala/FromZerotoExpert/tree/dev
如果用户没有登录,你会把它作为UV+1统计进去不
如果用户没有登录,那么在访问/FromZeroToExpert时请求会被拦截器拦截,然后跳转到登录界面。但是在拦截器中我的代码是先统计UV,IP,PV后再验证登录凭证,这里有问题:应该先验证再统计😛
得改成没有登录也能统计哦。
是这,我想反了,已改😭