在使用ant design文档的过程当中发现,antd使用了一个叫作logRocket的录屏框架,因而立马将logRocket用在本身的项目当中,测试它的功能。node
logRocket网站将采集到的数据,按照人员和session进行分类,观看各人员的操做回放,能够发现系统中某些操做的不便之处,而且能够发现哪些人员是你的重度用户。git
可是logRocket的数据存储在他们的服务器,而且从logRocket回放里,能看到系统中的各类重要数据。若是数据被别有用心之人获取,后果将很严重。github
若是咱们须要基于一个开源框架,并将数据存在本身的服务器中,限制人员查看的权限,这样就尅消除以前的隐患。web
下面我要介绍的就是今天的主角rrweb框架,全称record and replay the web。它由三个库组成:redis
每次刷新页面时,rrweb会将页面中的dom元素所有转换成文档数据,并给每一个dom元素分配一个惟一id。后面当页面发生变化时,只对变化的dom元素进行序列化。当重放页面时,会将数据反序列化并插入到页面中,而原先增量的dom变化,如属性或者文本变化,则根据id找到对应dom元素修改;而子节点的增长或减小,根据父元素id进行dom变动。mongodb
1.直接使用rrweb记录每次的序列化录屏数据,首先保存到localStorage中,当数据量超过阈值或者超过期间限制,再由sendbeacon发送数据到node,并保存到mongo中。数据库
2.首先遇到的问题是sendbeacon发送数据竟然出现了丢失,缘由是数据超过65536时,将会发送失败,因为sendbeacon是由后台进程单独发送,没法获取失败状态,因此要进行降级处理,当数据过大时,使用fetch请求发送。后端
3.因为公司中后台系统的用户分布在世界各地,海外的网络延迟较高,须要解决压缩数据大小的问题,这里使用的是lz-string库。一开始想要在每次存储在localStorage时进行压缩,后来发现压缩后的数据有特殊字符,JSON.parse高频率出错,后改成在每次发送数据到后端以前压缩,并在node端进行解压。api
4.一开始的数据库选型为时序数据库influxdb,因为某些不可抗拒缘由改成了mongodb。服务器
5.在项目上线后选择了一个小项目进行测试,发现存储和播放效果良好,代码以下
import rrweb from 'rrweb'; rrweb.record({ emit(event) { storagePush(event); }, });
存进数据库中的数据结构为
{ timestamp: 1563418490795, name:'小明', event:... }
方便按照用户和时间范围进行查找数据,内容以下
6.可是每次都要播放一成天的数据,第一播放接口获取的数据量巨大,第二播放时间漫长,抓不住重点,一旦数据有误致使后续录屏都播放不了。
查看rrweb源码发现checkoutEveryNms属性能够按照时间进行session切分,因而代码变成了这样
rrweb.record({ emit(event, checkout) { if(checkout)rrwebSessionSet(); storagePush(event); }, checkoutEveryNms: 1000 * 60 * 10 });
每一次checkoutEveryNms到期时,emit里的第二个参数checkout都会为true,这样就能够知道新的session开始,给session分配一个惟一值,存到数据库中的数据结构改成这样
{ timestamp: 1563418490795, name:'小明', session:xxxxxxxxxxx, event:... }
有了session概念以后,某我的某一天的操做就能够按照session进行选择
播放页面以下
7.小项目测试完毕后,但愿引入一个大项目进行测试,因而开放了一个uv上千、pv几十万的大项目,采集一天的数据后,发现存储数据正常,而播放页面已经获取不到数据,查看mongo的stats发现一天存储量达到了1500万条,每一条数据基本在几十KB到几M之间。
首先对不一样的项目进行分表存储,并将索引设置为后台处理,这个方案使用后播放页面变得正常,但人员列表接口仍是很慢。
因而在每次存储mongo时,存一份人员和日期的数据到redis中,目前系统已经正常运行,全部接口能在1s内返回全部数据。