lgwebdream / FE-Interview

🔥🔥🔥 前端面试,独有前端面试题详解,前端面试刷题必备,1000+前端面试真题,Html、Css、JavaScript、Vue、React、Node、TypeScript、Webpack、算法、网络与安全、浏览器
https://lgwebdream.github.io/FE-Interview/
Other
6.87k stars 895 forks source link

Day380:实现一个系统,统计前端页面性能、页面 JS 报错、用户操作行为、PV/UV、用户设备等消息,并进行必要的监控报警。方案如何设计,用什么技术点,什么样的系统架构,难点会在哪里? #1215

Open Genzhen opened 3 years ago

Genzhen commented 3 years ago

每日一题会在下午四点在交流群集中讨论,五点小程序中更新答案 欢迎大家在下方发表自己的优质见解

二维码加载失败可点击 小程序二维码

扫描下方二维码,收藏关注,及时获取答案以及详细解析,同时可解锁800+道前端面试题。


一、需求背景

1.1 为什么要做

为了实现收集功能,我们需要一个前端监控平台,它能够收集数据、处理数据、存储数据、查询数据。其实就有很多现成的平台或者开源项目我们可以直接使用。

1.2 行内通用方案

前端技术发展至今,相信大家已经对前端监控的这件事情非常熟悉,或多或少都会在我们的项目中用上它。比如搭建使用开源项目 sentry、付费平台阿里的 ARMS、甚至小程序配套的前端监控服务。

  1. sentry,sentry 主要提供的功能是收集错误。支持大多流行语言的客户端和服务端,不支持小程序,但是目前有大公司根据 sentry 的上报数据结构,自己实现了小程序 SDK 并开源,目前关注度和流行度都偏低。除开错误,它的其它类型的前端监控能力相对来说很弱。
  2. 阿里 ARMS,ARMS 提供的功能与支持的客户端比较齐全,小程序也支持。只是需要付费。总体来说提供的功能还是比较全面、符合国内的环境。
  3. 小程序自带监控 微信小程序不断的在完善内部的监控,各方面的功能也慢慢丰富了起来,但是只能支持小程序本身。

在使用这些开源或者平台前端监控服务的时候,始终有一些不足。比如:

1.3 定制化

如果完全从 0 到 1 来打造一套前端监控系统,成本也是很高的。甚至在早期,都可能没人愿意用,系统是否能立项或者持续发展下去也是一个问题。于是从一些开源项目中去寻找,去找一个方便改造也有一定功能模块的项目。可以基于它的代码,进行长期的改造和迭代。慢慢的改造成为一个更适合公司内部环境的一个前端监控系统。

二、系统架构

2.1 基本构成

为了实现前端监控,第一要素是要收集客户端数据,为了方便客户端集成监控系统、我们需要首先开发封装一个统一的 SDK、去帮助我们收集数据。

SDK 收集了数据,我们还需要通过服务端接口来接收,在服务端,使用 node+EggJs,node 适合 i/o 密集型场景,符合前端技术栈。eggjs 简单易用、文档友好,大部分使用 node 的前端程序员都应该能很快上手。

服务端收集到数据并进行一些处理之后,我们需要存储到我们的数据库之中。在数据库方面,使用 mongo 做持久化存储,mongo 文档模型数据库,数据扩展方便,类 json 结构方便和 node 配合使用,天生适合日志系统。使用 redis 做数据缓存,redis 简单易用的高性能 key-value 数据库,市场上占据主流,被大部分人都熟知。

最后,还需要一个管理台,做数据查询与管理。管理台使用 Vue+ElementUI,简单快速。

客户端 SDK 收集数据上报,node 服务端获取到数据后,先存在 redis 中,node 服务会根据消费能力去拉取 redis 数据处理分析后存储到 mongo 之中,最后我们通过管理后台展示处理好的应用数据。

2.2 技术的可能的一些难点

可能整个系统比较复杂的就是如何高效合理的进行监控数据上传。除了异常报错信息本身,还需要记录用户操作日志,如果任何日志都立即上报,这无异于自造的 DDOS 攻击。那就需要考虑前端日志的存储,日志如何上传,上传前如何整理日志等问题。

对于日志的处理上报有这样的处理方案

我们并不单单采集异常本身日志,而且还会采集与异常相关的用户行为日志。单纯一条异常日志并不能帮助我们快速定位问题根源,找到解决方案。但如果要收集用户的行为日志,又要采取一定的技巧,而不能用户每一个操作后,就立即将该行为日志传到服务器,对于具有大量用户同时在线的应用,如果用户一操作就立即上传日志,无异于对日志服务器进行 DDOS 攻击。因此,我们先将这些日志存储在用户客户端本地,达到一定条件之后,再同时打包上传一组日志。

那么,如何进行前端日志存储呢?我们不可能直接将这些日志用一个变量保存起来,这样会挤爆内存,而且一旦用户进行刷新操作,这些日志就丢失了,因此,我们自然而然想到前端数据持久化方案。

目前,可用的持久化方案可选项也比较多了,主要有:Cookie、localStorage、sessionStorage、IndexedDB、webSQL 、FileSystem 等等。那么该如何选择呢?我们通过一个表来进行对比:

存储方式 cookie localStorage sessionStorage IndexedDB webSQL FileSystem
类型 key-value key-value NoSQL SQL
数据格式 string string string object
容量 4k 5M 5M 500M 60M
进程 同步 同步 同步 异步 异步
检索 key key key, index field
性能 读快写慢 读慢写快

综合之后,IndexedDB 是最好的选择,它具有容量大、异步的优势,异步的特性保证它不会对界面的渲染产生阻塞。而且 IndexedDB 是分库的,每个库又分 store,还能按照索引进行查询,具有完整的数据库管理思维,比 localStorage 更适合做结构化数据管理。但是它有一个缺点,就是 api 非常复杂,不像 localStorage 那么简单直接。针对这一点,我们可以使用 hello-indexeddb 这个工具,它用 Promise 对复杂 api 进行来封装,简化操作,使 IndexedDB 的使用也能做到 localStorage 一样便捷。另外,IndexedDB 是被广泛支持的 HTML5 标准,兼容大部分浏览器,因此不用担心它的发展前景。

按照上报的频率(重要紧急度)可将上报分为四类

收集到日志后,立即触发上报函数。仅用于 A 类异常。而且由于受到网络不确定因素影响,A 类日志上报需要有一个确认机制,只有确认服务端已经成功接收到该上报信息之后,才算完成。否则需要有一个循环机制,确保上报成功。

将收集到的日志存储在本地,当收集到一定数量之后再打包一次性上报,或者按照一定的频率(时间间隔)打包上传。这相当于把多次合并为一次上报,以降低对服务器的压力。

将一次异常的场景打包为一个区块后进行上报。它和批量上报不同,批量上报保证了日志的完整性,全面性,但会有无用信息。而区块上报则是针对异常本身的,确保单个异常相关的日志被全部上报。

在界面上提供一个按钮,用户主动反馈 bug。这有利于加强与用户的互动。

或者当异常发生时,虽然对用户没有任何影响,但是应用监控到了,弹出一个提示框,让用户选择是否愿意上传日志。这种方案适合涉及用户隐私数据时。

即时上报 批量上报 区块上报 用户反馈
时效 立即 定时 稍延时 延时
条数 一次全部上报 一次 100 条 单次上报相关条目 一次 1 条
容量
紧急 紧急重要 不紧急 不紧急但重要 不紧急

即时上报虽然叫即时,但是其实也是通过类似队列的循环任务去完成的,它主要是尽快把一些重要的异常提交给监控系统,好让运维人员发现问题,因此,它对应的紧急程度比较高。

批量上报和区块上报的区别:批量上报是一次上报一定条数,比如每 2 分钟上报 1000 条,直到上报完成。而区块上报是在异常发生之后,马上收集和异常相关的所有日志,查询出哪些日志已经由批量上报上报过了,剔除掉,把其他相关日志上传,和异常相关的这些日志相对而言更重要一些,它们可以帮助尽快复原异常现场,找出发生异常的根源。

用户提交的反馈信息,则可以慢悠悠上报上去。

WuChenDi commented 3 years ago

hi,老哥你好,自研的话,有办法定位到源码报错位置吗,我试过 node 起一个服务用来还原源文件,但是line与column不准确

17biubiu commented 2 years ago

hi,老哥你好,自研的话,有办法定位到源码报错位置吗,我试过 node 起一个服务用来还原源文件,但是line与column不准确

这个你得结合sourcemap吧?用开源的库source-map

WuChenDi commented 2 years ago

hi,老哥你好,自研的话,有办法定位到源码报错位置吗,我试过 node 起一个服务用来还原源文件,但是line与column不准确

这个你得结合sourcemap吧?用开源的库source-map

是的呢,做法就是这样,能定位到错误文件,但是line与column不准确