cover_image

稳定性之接口监控

维尼 玩物少年们
2020年09月10日 02:00

本期分享内容目录:

  • 前言

  • 背景

  • 探索监控需求

  • 拆分监控业务

  • 设计监控架构

  • 接口监控实战

  • 监控整体总结


前言:



接口监控是确保系统稳定性的一项重要措施。


背景:

监控的方式有以下几种:

  • 系统监控

  • 日志监控

  • 接口监控(Dubbo\Http

    - 其中接口监控是一种更细粒度的监控方式,主要针对于应用端接口进行监控。

接口监控的优势:

  1. 它可以监察接口请求超时异常的问题。

  2. 可以通过校验监察接口返回结果异常问题。

  3. 它是一种主动获取系统信息的监控,通过主动调用接口,并对返回结果进行校验。故在异常问题侦查方面,它优于另外两种监控。

本文将详细介绍接口监控的设计方案:

通过实时监控线上核心接口,我们可以快速发现核心接口的异常问题。

在一些高并发情况下,我们可以通过实时监控监察到一些不稳定的接口。如:接口请求超时,请求RT突刺等

这些不稳定的接口需要我们在后续的工作进行优化,提高整个系统的稳定性。

探索监控需求

监控顾名思义,针对客观存在的一切物体和现象,进行监测和控制。


将“监控“二字放入我们当前需求中,就可以理解为:


1.监视程序是否正常运行


2.检测运行结果跟我们预期的是否一致,如果不一致则做出对应的反应


以下是大致的监控业务流程图:

图片

图中监控的开头与结尾都是一个黑盒,中间的校验流程也是一个黑盒。

许多的未知逻辑等待我们去探索

接下来我们将通过问答的方式开展业务探索

提问

  1. 监控的对象应该是什么?

  2. 以什么标准对返回的结果进行校验?

  3. 结果与预期不一致时,我们该如何处理?

解答

监控对象

程序对外提供的接口。

当前工程分布式架构使用的是 Dubbo,接口主要划分两大模块: Dubbo接口 && Http接口

校验规则(grovvy)

这一块我们就想到了规则引擎设计理念,在java程序中引入Grovvy脚本,对返回结果进行断言。

针对相同的校验规则,我们将通过提取出抽象化通用方法,该方法包含接口的通用校验逻辑。

异常结果处理机制

这里我们可以使用钉钉提供的自定义机器人,通过调用钉钉机器人请求API,拼接好对应的信息报文反馈给目标对象即钉钉接口监控异常报警群。

拆分监控需求

完成上面的业务梳理后,我们再深入拆分整体的业务逻辑,看看我们需要做些什么工作。

接下来,还是按照上述的问题进行深入探讨,这里补上一个环节 - 怎么去监控?

监控方式:

通过开源工程xxl-job 定时轮询查询监控任务并执行,任务的执行间隔设置为 5秒,30秒,60秒。

监控对象属性:

  1. 监控基本参数。

  2. 监控接口的类型分别有 Dubbo接口 & Http接口

  3. 每一个接口都有自己的业务域,如:首页监控,直播监控,卖家详情页,我的......

  4. 既然要做监控,责任必须明确到人,监控对象所属人。

  5. 每个监控对象的返回结果都是有差异的,那么针对其返回结果的断言也是不一样的,每一个对象都有其特有的Grovvy脚本。

校验规则:

监控对象:Dubbo接口 & Http接口

接口返回对象模板格式固定,那么同一类的返回接口肯定存在相同的校验规则。

抽象化基本校验规则

例如:

Http 接口:返回code码需要为200,返回结果集不为空。

Dubbo 接口:返回结果为一个自定义的包装对象,我们需要进行拆包动作,对包装对象的code码进行校验。

但两者的返回结果对象内容各异

针对这一类情况,我们的处理方式支持提前设定好的Grovvy脚本进行校验。

异常结果处理机制

  1. 每一个业务域都需要进行接口监控,我们通过钉钉群的形式上报监控报警。

  2. 为了更明确职责,异常上报需要区分群组。不同的业务域的异常报警,需要上报到不同的钉钉群。

  3. 一些偶现的异常报警,频繁的上报也会令人疲惫。那么异常报警需要有报警阀门这一概念。在一定时间内,报警数超过指定数值,才会上报异常。

异常报警处理机制,需要模板化。

模板格式:

  1. 指定上报钉钉群机器人配置信息

  2. 设定报警阀值

  3. 单个接口监控必须指定一个监控报警模板

设计监控架构

监控触发报警阀值

图片

xxl-job 会通过轮询的机制,定时向服务器集群调用监控任务。

报警阀值的触发机制,就需要通过分布式缓存服务记录单位时间内的报警数。

当前设计中主要使用 Redis作为分布式缓存服务,并使用Zset数据结构

通过Zset数据结构的score的排序特性,我们可以动态的清除无效报警计数,实现一个滑动窗口的报警计数。

CODE

String INTERFACE_MONITOR_ALARM = "interfaceMonitor:alarm:%s";
/** * <p> * 在对应时间范围超过阈值则返回True * * @param actionKey 接口唯一标识 * @param period 监控报警时间段 * @param maxCount 监控报警阀值 * @return */public boolean isOperationAllowed(String actionKey, int period, int maxCount) { String key = String.format(INTERFACE_MONITOR_ALARM, actionKey); long nowTs = System.currentTimeMillis(); //记录行为 stringRedisTemplate.opsForZSet().add(key, nowTs + "", nowTs); //移除时间窗口之前的行为记录,剩下的都是时间窗口内的 stringRedisTemplate.opsForZSet().removeRangeByScore(key, 0, nowTs - period * NumberConstant.ONE_THOUSAND); //设置 zSet 过期时间,避免冷用户持续占用内存 stringRedisTemplate.expire(key, period + 1L, TimeUnit.SECONDS); return stringRedisTemplate.opsForZSet().zCard(key) > maxCount;}

监控时序图

图片

ER图

图片

监控报警模板

接口名:接口参数:返回结果:检查脚本:所属业务:错误描述:接口描述:@owner

参考例子:

图片

接口监控实战

监控模型图

图片

线上操作流程

1.优先创建报警模板

图片

2.配置接口监控

图片

3.配置xxl-job定时任务

图片

4.感受一下效果

图片

监控整体总结

产品总结:

在该产品上线的短短2个月时间里,监察到线上异常16次,为产品稳定性和避免生产环境故障的发生发挥了关键作用。

  1. 线上核心接口的异常问题,该产品都能第一时间通过报警的形式反馈至钉钉群中。有效避免因发布新功能上线而导致的异常问题,规避风险。

  2. 接口出现抖动问题,都将通过报警的方式体现,问题得到记录,警醒我们去优化一些不稳定的接口,防患于未然。为后面迎接更大的挑战做好准备。

技术总结:

  1. Dubbo接口泛化:监控接口参数通过模板模式实现转化:JSON字符串转换成实体对象。

  2. 规则驱动:采用Grovvy脚本充当每一个接口监控的断言,动态校验业务返回结果。

  3. 服务器集群触发异常报警时,使用Redis的Zset数据结构,实现报警阀值报警业务逻辑。

生产环境异常情况总结:

  1. 某一时段出现慢SQL问题,导致线上核心接口查询出现异常。

  2. 新接口发布上线,有大量请求调用该接口,但接口性能不能满足实际生产需求,导致其他接口调用出现超时现象。

  3. 某接口在一时段出现抖动情况,通过排查接口抖动原因,并对接口进行优化。



本文作者:

维尼  高级Java工程师

他的历史分享:数据源切换历险记

继续滑动看下一个
玩物少年们
向上滑动看下一个