1. 引子
在你的团队里面:
- 是不是经常都是用户发现了问题通知到你们,你们才知道,甚至系统实例宕机了你们都还不晓得?
- 是不是系统告警消息太多而淹没了重要的消息,从而团队里面都未能及时发现系统问题?
- 当发生了问题,是不是往往如热锅蚂蚁,不知道怎么才能快速让业务功能恢复正常?
- 你是否有想过解决团队"神经"总比用户慢半拍的问题,即如何比业务方(用户)先发现问题,从而快速解决问题。
这里有3条心经:
- 首先,异常监控告警不能少:如果没有监控告警,不可能比业务先发现问题。
- 其次,降级措施必须有:即使业务先发现问题通知到你,你也能让业务快速的恢复正常。并且线上紧急事故处理原则是怎样能快速恢复业务就怎样做。
- 最后,告警和反馈消息需能及时看到:需要对告警消息进行分类,避免重要消息被淹没。最好是对于重要消息做电话告警。
还需要说的是,添加告警监控点和降级措施完全建立在研发的经验和习惯上,如果没有一个可行的措施,还是不能解决根本问题。下面给出一个解决方案:
2. 系统稳定性保障
2.1 对现有系统做可靠性自查
- 对外服务接口是否添加性能波动和异常告警
- 对外服务接口降级措施是否正确
- 调用第三方接口是否添加监控
- 调用第三方接口是否可以降级
- redis是否有监控
- 缓存数据获取失败是否有降级
- mysql读写失败是否有降级
2.2 业务关键点保障性措施
2.2.1 对外提供的服务接口
- 告警
- 需要添加tp999监控阈值告警
- 非业务异常日志捕获告警,如空指针异常、数组列表越界、类加载异常
- 业务异常数量限制告警
- 降级
- 限流:在系统高负载的情况下,为了降低服务器的压力,限制一定的流量进来。待扩容后可以放开流量限制。
- 扩容:在系统高负载或即将达到负载告警点时,增加更多的服务节点。
- 熔断:当接口不能正确处理请求时,阻断请求,直接返回错误结果,待问题修复后关闭熔断。熔断应该分粗粒度和细粒度的熔断。
- 粗粒度:熔断主接口,这种方式使对外接口完全关闭。
- 细粒度:熔断子接口,针对非重要而又容易造成系统瓶颈的接口。
- 重启
- 回滚
- 日志
- 打印异常日志
- 对耗时比较大的请求,打印请求参数值
2.2.2 第三方接口调用
- 告警
在第三方接口添加告警的目的是提前知道第三方接口的状况,以便快速做出应对措施。- 添加tp999监控阈值告警
- 添加非业务异常告警。
- 添加业务异常数量限制告警。如果接口返回的数据不是期望的,可以加上告警。
- 降级
- 熔断:如果第三方接口耗时持续居高不下或者大量超时,为了避免大流量进来造成雪崩,可采取该方案。
- 超时时间:第三方接口必须设置超时,超时时间不宜过长,容易造成雪崩。
- 日志
- 打印异常日志
- 对耗时比较大的请求,打印请求参数值
2.2.3 Redis接口添加监控
Redis作为缓存,数据获取速度很快,这并不是不需要添加监控的理由,殊不知,Redis慢查询、网络问题都可能造成数据获取速度变慢,甚至是链接失败等待。
- 告警
- 用到的redis命令都应该加上tp999监控告警
- 需要redis命令执行异常告警
- 降级
- 灾备
- 兜底:把少量重要的数据做本地缓存,必须要获取的数据可以转从其他数据库获取。
2.2.4 MySQL读写接口
- 告警
- 接口异常告警
- 降级
- 灾备
- 重试
- 转存,不能做回滚的情况下,为了防止重要数据丢失,可以考虑把数据转发到其它地方,或者暂存本地内存或磁盘,在适当的时间再把数据写回数据库
3. 快速定位根因
收到告警后都需要开发者能够快速的定位到问题并给出解决问题的方案,这里针对如何快速定位问题,做一点介绍。
3.1 问题定位可用的工具和数据
- 内部的监控平台:查看与问题相关的指标
- 数据库监控:查看数据库中可能与问题相关的指标
- 日志
- 业务日志
- gc日志
- vm error日志
- 网络IO日志
- 操作系统监控日志
- 配置
- 应用配置
- 业务配置
- 启动参数
- 环境变量
- arthas(trace接口耗时或者watch参数返回值和异常信息)
- 代码最近修改记录
- 线程栈
- dump并分析内存镜像
3.2 定位问题的方式
以上是可以用于分析问题的数据信息,现场还是要根据具体问题具体分析,从问题的可疑点逐步选择以上的数据进行分析。这里分享下解决问题的方式:
-
顺藤摸瓜,罗列出造成问题的原因可能有哪些,可以在心中罗列,也可以罗列到一个文档中。
对于经验不足的研发者,遇到问题往往无所适从,罗列的可疑点太少甚至背道而驰,但这没关系,可疑点本来就需要顺藤摸瓜一个一个的挖掘出来。你也可以说是摸着石头过河。 -
根据罗列的可疑点,从可疑度高的逐个进行排查。
这一点,切勿因为麻烦而在原地纠结、或者想当然的无证据的排除个别可疑点。 在平常帮助同事解决bug的时候,经常遇到这种情况,根因问题就被无证据、想当然地排除在验证名单外了,从而导致花了很多时间也没解决问题。
下面举例说下常见的问题:
3.2.1 业务问题
- 数据不显示或者显示错误:检测相关配置是否正确
- 功能缺陷:是否功能还未开发,或者是否是业务逻辑不完整
3.2.2 异常类问题
- 通过查看业务日志文件找到异常位置
- 如果没有捕获异常或者日志不够详细,并且问题可以复现,通过arthas watch命令查看接口的入参和异常信息,trace命令可以跟踪接口调用栈定位异常抛出点。
3.2.3 耗时类问题
- 通过自定义的监控平台查询调用链上耗时接口
- 无法定位耗时接口,可以通过arthas trace命令跟踪接口中每一步的耗时情况
- 查看gc日志,看看是否发生full gc、频繁young gc以及GC耗时是否过长,young gc正常在20毫秒左右。
- 查看CPU负载,查看CPU高的线程堆栈
- 查看磁盘IO、网络负载、网络日志(比如TCP重传频率高)
3.2.4 其它疑难杂症
3.2.4.1 耗时类疑难杂症
- 滥用线程池:资源未隔离、未捕获异常造成线程结束进而造成线程池频繁创建新的线程
- 接口内频繁创建线程
- 滥用同步锁或者锁粒度太大
- 死锁:进程内死锁,系统间死锁(循环调用,状态共享)
- 同步队列消费线程慢,造成写线程阻塞
3.2.4.2 数据类疑难杂症
- 数据被篡改:数据库链接未正确释放造成链接共享
- 数据不正确:ThreadLocal未清除造成不正确的数据共享
4. 总结
- 快速感知问题:异常监控告警不能少,降级措施必须有,告警和反馈消息能及时看到
- 快速找出根因:顺藤摸瓜,罗列所有可能的原因,从疑点最大的开始逐个排查,排查问题皆需要有据可查,切勿想当然排除可疑点。