大家是否遇到过这样的场景,老板在群里 @ 你,向你反馈线上有 BUG。一般老板反馈的问题,都属于重要且紧急的需求,需要快速解决。这样的高优需求,即让你神经紧绷,又打破了你原有的计划,常常弄的人很累。靠充分的测试能够解决一部分问题,但是线上的场景永远比测试的场景更加丰富,难免会有遗漏 BUG 被带上线,造成业务损失。

IMG_8755

可靠地、实时地、准确地提前发现问题

一个好的告警系统会提前发现问题,发出警告。你就有可能在老板 @ 你之前,就把问题给解决。

告警系统要提前发现问题的三个关键是:

  • 可靠性。告警系统本身要可靠,不能时灵时不灵。
  • 实时性。要快,慢了就没有意义了。
  • 准确性。狼没来,喊狼来了,喊了三次后就没人信了。

IMG_8756 2

接下来,我会先带大家看下我们的告警系统设计的全貌,再从可靠性、实时性、准确性三方面来详细介绍。

实时告警:整体实现

实时告警功能主要的工作量在 node.js 层,其整体实现如下:

  1. 每分钟批量执行一次定时任务
  2. 向Druid发出请求,获取线上实时的聚合数据
  3. 判断聚合数据是否超出阈值,如果超出则执行下一步
  4. 发送短信、微信、邮件通知开发者

image-20210201164950636

可靠性:每台机器启动一个定时任务,会执行多次

一台机器运行定时任务,只有一个定时任务,指标超出阈值也只会发出一次告警。

但是,为了保障可靠性,我们就要避免单点风险,也就是至少要启动两台机器来运行定时任务,即使其中一台机器挂了,告警功能也能正常运行。但是,引入多台机器问题就来了。

二台机器运行定时任务,有两个定时任务,会发出两次告警。分布式 node.js 集群,会有多台机器运行定时任务,超出阈值就会发出多次告警,引入了重复告警的新问题。

定时任务:Redis锁保障只执行一次

我们需要的是多台机器,只执行一次定时任务,有啥办法来保障呢?我们想了两个方案。

  • 共识方案:一个集群中的多台机器之间,选出一台机器来执行定时任务。
  • 抢锁方案:一个集群中的多台机器同时向 redis 发出请求,谁抢到 redis 锁谁执行定时任务。

共识方案,在 Java 中有 zookeeper 之类的分布式调度工具可以使用,底层共识算法会选举出一台机器作为主(leader),其他机器作为从(follower)。可以只让主机器执行定时任务。但是在 node.js 中,我们并未找到类似于 zookeeper 的框架,所以就放弃了。

抢锁方案,是我们采用的方案。其原理是通过共享缓存的唯一性,来保障定时任务执行的唯一性,依赖的是 redis 的可靠性。在实际的业务中,我们认为 redis 比较稳定,单台就够了。如果以后发现问题,再改用多台 redis 也不迟。node.js 中有 bull、redlock 的分布式锁工具可以用。

  • bull 的功能主要是分布式队列,用来实现分布式锁有些大材小用。
  • redlock 是 redis 官方提出的分布式锁算法,也有 node.js 的实现,正好符合我们的需求。

因此,在告警功能的可靠性上,我们使用了 node.js 集群,避免了单点故障。同时,使用 redis 锁,保障一个告警定时任务,只会执行一次。

image-20210201173000423

实时性:关键是“快”

实时性的关键词是“快”,从用户异常发生到开发者收到异常告警的时间越快越好。

其中关键点是:

  • Web 发生异常后,马上上报,SDK 层不缓存。
  • 使用时间序列数据库 Druid,减少海量数据聚合耗时。
  • 每分钟 node.js 执行一次告警定时任务。

image-20210201220044515

大家可以看到,从发生时间到告警时间,中间有 8 个时间点,用哪个时间点好呢?

在 8 个时间点中,最为关键时间点有 2 个:

  • 发生时间(橙色):用户行为轨迹需要对用户异常日志、正常日志按时间进行排序。异常日志是立刻上报的,正常日志是用户主动上报的,这就只能用发生时间进行排序,其他时间肯定都不对。
  • 接收时间(蓝色):开发者搜索某天某个用户发生的异常,如果用户本地时间是错的,可能就会搜不到。但我们可以相信,服务端的接收时间是准确的,按照接收时间搜索,肯定能搜到。

那么告警指标的统计以那个时间点为准呢?具体的统计方式应该怎么设计呢?

实时性:统计方式

从两方面考虑,我们的统计时间点用的是接收时间。一方面,服务端时间的准确性是我们自己控制的,是可信的。另一方面,考虑有海外用户会跨时区,时区问题处理起来会比较麻烦,统一用服务端时间更好。

但是,这里还是有一些细节需要大家注意,从接收到可被读取之间,还有经过 kafka 消息队列和 druid 聚合计算的时间。理论上 kafka 只要不发生消息堆积,是能马上被 druid 进行消费的,druid 本身又有实时计算模块,聚合计算也是非常快的。但是,我们考虑到可能会有偶发的一些超时情况,可能会导致统计数据偏少,从而导致误报。因此,拍脑袋给 kafka 和 druid 预留了 1 分钟的缓冲时间,算作对实时性的一种妥协吧。

image-20210201232622880

准确性:线上异常的总量监控指标不可靠?

现在,我们来聊聊最后一个特性,准确性,也就是告警指标是否可不可靠。

我们假设一个场景,有位开发者看到异常总数这个指标快速上升,他心中一紧,这线上是啥情况啊?

image-20210202104744665

如果恰巧,前端页面刚刚有上线,大概率是前端问题。

如果恰巧,后端同时发出宕机告警,大概率是后端问题。

如果恰巧,业务刚刚在冲量,那么引起页面异常总数指标快速上升的原因,大概率只是因为 PV 涨了而已。

同理,在0~6点是业务范围量少,到了 8 点~10点,流量会有很大的上涨,同时也会影响异常总量上涨。

准确性:异常PV比代表平均每个PV发生异常的数量

在异常总数和PV同步增长的情况下,异常PV的比值其实会保持不变。其原因是平均用户每次访问,页面发生异常数量是一样的。因此,在我们的业务中,相对于异常总数,我们认为异常PV比的变化更重要。

当业务异常PV比大于预设的阈值,或者环比上升较快,那么就可以判断线上出现新的异常了。并向业务发出告警。

image-20210202114133055

准确性:全面的监控指标

为了更全面的监控线上应用,我们设定了一系类的监控指标,其中深蓝色的是核心指标,它的准确性会更高一些。

截屏2021-02-02 14.31.43


fitfish
1.6k 声望950 粉丝

前端第七年,写一个 RN 专栏。