踩坑MySQL主从延迟背景下,生产消费未正确处理此类场景,数据消费第一次失败报警,问题得以暴露。
背景
前几天我就收到了这个报警,报警内容:
[恢复][部署平台][我负责的服务PPP][接口响应状态码异常][接口:接口a响应状态码:500看图:https://grafana.be.company.com/d/i4jgtCN7c/kan-ban-ppp?editPanel=141&orgId=1]
系统: 部署平台
策略名称: 接口响应状态码异常
报警描述: 接口:接口a响应状态码:500看图:https://grafana.be.company.com/d/i4jgtCN7c/kan-ban-ppp?editPanel=141&orgId=1
时间: 2021-12-07 10:25:58
持续时间: 3m11s
服务节点: 服务节点全链名
合并数: 1
合并主机:
这个接口是a模块数据变更后,B系统向A系统拉取a模块数据的查询接口。
问题分析排查
首先查日志,根据个人习惯与团队设施操作即可,一般我们使用ELK技术栈或者Linux文本技术工具。
可以看到使用的消息中间件侧的消息索引,这个可以 用来定位消息。
使用的消息中间件侧看到消息进行了重试,第一次失败,而第二次成功。重试容错,对业务侧没有影响。
下一步需要确认下第一次报错的原因。初步在本地用postman调用生产这个入参的接口:
调用多次,平均耗时<100ms。当时基于此得出结论:接口没有问题,监控为误报【为多次排查埋下伏笔,这也是一个错误的惯性思维】。
20211207初步处理
初步得出结论:以后出现问题继续观察下,此问题无继续排查下去的必要。
20211209再次分析
问题看起来非偶发,需要重新排查。
发现NPE,根据异常栈可直接定位到报错代码行数。
疑点
- 对应代码初上线时并无问题,说明是后面某个变更导致的新问题。
- 出现了多条这种数据(消息重试第二次才会成功),说明不是巧合,是必现问题。
排查
突然想到项目中前几周上了读写分离的插件,这个动作立即与当时负责的RD确认,MR记录还存在。
到这里问题经过变得清晰:
- 消息生产时,数据写入了主库节点。
- 消费者通过生产者服务拉数据时,此时MySQL从节点正在异步写数据,所以此时查出的数据为空。
问题解决
- 将此NPE异常封装下,错误返回到接口的Response中,消费者可拿到响应码做判断、重试即可。
- 如果消息组件支持延迟消费,可以通过配置的方式处理此问题(RocketMQ支持了延迟消费功能)。
反思
- 同类问题多次报出,说明不是巧合。
- 业务可用性只是问题判断的依据之一,应用中的异常、报警指标也是问题的判断依据,需要兼顾这几个方面。
- 每个问题都值得重视。