1 背景回顾
8月16日下午14:56左右,研发同学A提交了导出生产环境数据sql并审批通过(实际未严格审批),15:15正式执行该sql,因sql过大造成数据库服务器栈溢出,从而触发db主备切换,导致后端服务大量发生数据库超时与不可写问题,并收到大量报警。中台服务很快恢复,但前台服务半小时左右仍未恢复,请求仍未响应,处于瘫痪状态,直到前台负责人B重启后,恢复正常。
按照上面的指示,相关同学发起了事故复盘,从前端、前台、中台、运维都参与其中,但上面对前台相关研发同学C的复盘结果 (图1) 不太认可,认为不够透彻未深究到真实原因,遂直接推荐笔者接替追踪,挖出根因,并给出解决方案 (图2)。
图1 前台研发同学前期的复盘结果
图2 事发九天后临时受命
忙完其他优先级更高的工作,笔者实际是事发后第13天开始正式介入追踪的。鉴于公司提供的监控条件有限,原有思路分两个方向:
思路方向 | 概述 | 投入最少人力 | 优点 | 缺点 |
---|---|---|---|---|
回溯 | 再次依赖现有日志、pod与系统运行质量的图形化监控等,查找所有疑点,挖出真因 | 1 | 投入的人力物力时间成本较少 | 关键证据不易收集或无法收集,将无法形成追踪的闭环,从而无法查清根因。 |
复现 | 依照线上环境再搭建新的相关服务的整套环境和相同数据,模拟线上触发,同步去观察并收集证据和追查 | 1 | 类似还原现场,有充足的时间和现场手段来追踪 | 研发人力紧缺,需运维全力支持,投入资源来线下搭建整套环境,涉及相关人员的协调沟通,周期较长。 |
前台研发人员已经安排到其他优先级更高的需求中去了,无人可调,笔者只能尝试第一个方向“回溯”,参考前面研发的结论,但并不抱太大的希望。
2 蛛丝马迹与水落石出
2.1 同一域名http连接池的配置
隶属jar | 类路径 | 方法名 |
---|---|---|
aaa-bbb-ccc-22.2.21-RELEASE | com.ddd.framework.eee.core.FFF | getConnManager |
可见,依赖的公司框架中,默认设置单路由(域名)连接池最大连接数30,所有路由对应的总计连接数60。
2.2 特定响应码的处理 ![](https://i-blog.csdnimg.cn/blog_migrate/50590b9c0549f20ec27fb37b966756df.png)
隶属工程 | 类路径 | 方法名 |
---|---|---|
aaa-bbb-ccc-service | com.ddd.eee.fff.api.service.Ggg | execute |
继续追踪源码可见,前台服务在依赖公司框架基础上, 原有研发人员自行封装了http请求发送工具类Ggg,在execute方法的处理中,针对400以上的响应状态码做了提前返回并报错的处理,但未释放持有的http连接。
2.3 耗尽连接的追踪
可见,在跨度接近2分钟的时间范围内,由tomcat默认初始化10个线程中的8个线程,发起对xx中台的调用30次,得到错误状态码,将该路由下的连接池耗尽。
2.4 连接耗尽后的申请机制
隶属jar | 类路径 | 方法名 |
---|---|---|
httpcore-4.a.b | org.apache.http.pool.AbstractConnPool | getPoolEntryBlocking |
2.5 无连接可用的追踪
由上可见,从15:18:32.645开始直到15:22:27.320,200个线程全部因无连接可用而全部挂起。
2.6 重启发生时的追踪
15:41 前台负责人B逐步重启前台服务,以上截图中的堆栈,再次印证了底层连接池无连接可用的申请机制。
2.7 grafana监控追踪
2.7.1 前台service的态势
2.7.2 xx中台的态势 ![](https://i-blog.csdnimg.cn/blog_migrate/8ca4eda8d8c4d8868975beb4f620a480.png)
综合前面几个小节的追踪数据,与以上两服务监控的态势及时间吻合。
3 根因
综上,经过四天的连续追踪,查清并形成了如下闭环的根因:以其中一个pod为例,15:16:06.184开始,前台service接收流量按业务逻辑,需访问xx中台时,获取的响应状态码为503或502,在自身封装Http的工具类中做调用返回处理时,未对其持有的http连接进行释放(底层依赖的公司http连接池),直到耗完隶属于xx中台域名对应的连接池最大连接数量30。随后,因依赖的公司http连接池未配置从池中获取连接的超时时间,导致后续请求无连接可申请就直接挂起,直至陆续创建并耗尽tomcat默认200个线程,15:22:27.320服务彻底不可用。
4 反思与改进
4.1 公司内部公共框架的可靠提供机制
公共框架的提供者,针对该类型框架的已知典型风险,应该有默认规避设置,并以文档的形式,告知对接方存在或者接入时因关注的风险。
4.2 公司内部公共框架的可靠引入机制
- 各业务团队对公共框架的引入者,尽可能的去了解待引入的框架是否存在代表性易发的风险及其应对措施,并与提供者沟通确认,防止重复解决甚至错误解决。
- 引入者还须重点关注所引框架(及其底层),以及引入者自身对该框架的包装,所对应的异常处理机制、资源回收机制是否健全可靠。
4.3 线程池、连接池的图形化监控及告警
连接池、线程池异常,往往带来服务的瘫痪不可用,需要及时可用的监视、告警、及业务研发团队的应对。
4.4 关键资源的熔断、降级
保障关键服务的高可用,避免雪崩效应。
4.5 关键服务的自愈机制
K8S环境,关键服务不仅需要多实例,探针也不应该缺失,提升服务恢复的及时性。
4.6 更全面的事故现场保留机制
一旦发生服务不可用的严重生产事故,对应的研发团队面临着既矛盾又复杂的压力挑战,即既要做到第一时间恢复服务的可用,减少故障的恢复时间,又要做到第一时间切入现场,收集固定各种信息证据,为定位处理故障提供准确的依据。
希望公司的基础架构团队,或者其他有在此领域涉猎进取的技术团队,能够建立健全事故现场保留机制,从而为事故的定位处理降本增效,避免证据单一或者缺失,造成需要线下重现而投入的环境搭建、人力、时间等较大代价和带来的滞后性。
4.7 故障响应团队的必要性
服务不可用故障的突发性、复杂性,往往不是一个业务团队研发人员第一时间所能定位、处理并恢复的,再加上平常各成员各司其职,往往缺失对事故苗头的敏感性、警觉性,易造成事故介入的较大延迟、事故损失的扩大等后果。如果能够建立故障响应团队,代替自发性的追踪定位处理,明确事故的紧急重要性、合理的分工,或许会带来故障响应的更高效率。
4.8 其他
见《0816事故报告》中提到的教训、行动、思考等章节,不再赘述。
前台service在此事故中自身的缺陷,已于9月8日修复并上线,且公司框架团队、本业务线中台团队的研发同学先后与笔者确认了与他们相关的缺陷的修复方案及思路。