记一次线上 bug 的排查分析过程及总结

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 记一次线上 bug 的排查分析过程及总结

前言

大家好,我是路由器没有路

在线上运行的系统中,问题的出现是不可避免的。如何快速、准确地排查问题,是每个技术人员都需要掌握的技能。

本文将分享一个线上问题排查的过程和总结,希望对大家有所帮助。

问题表现

我们的商城系统有一个问题:有用户反馈打开商城首页有偶现打不开的情况。

前端查了下反馈说接口超时导致,但是查 cgi 接口进程还在,也无相关报错日志。

假设导致问题的原因

我们首先对问题进行假设,以便更好地进行排查。我们认为问题可能是由以下几种情况导致的:

  1. 网络异常导致
  2. 底层服务异常导致
  3. 底层服务慢查询导致
  4. 服务查询数据量大,回包给 cgi 有问题导致
  5. 接口有 panic 然后重启导致

小心验证

接下来,我们需要对每一种假设进行验证,以排除不符合实际的情况。

  1. 在网络正常的情况下,自己进商城首页试了下,确实也偶现该问题;接着让运维查了下网络带宽,也是正常的;排除网络异常导致。
  2. 查了下底层服务日志,日志无异常报错,进程也无重启情况,排除服务异常导致。
  3. 运维有工具监控慢查询的情况,有慢查询的话会有统计和告警的,查了下慢查询日志没有对应的慢查询;排除慢查询导致。
  4. 查了下该服务对应的 pl 日志,即框架平台日志(平台会有记录每次请求的包大小情况),看请求的包大小在正常阈值范围内,也无报包太大的错误,排除回包太大导致。
  5. 在日志系统查了下该接口的日志,并没有发现有 panic 或者其它异常的报错信息;此时一筹莫展,想着再看下部署机器上的接口进程是否都正常,仔细看发现有个别机器上的接口服务进程有重启过的情况,问了下同事也没有重启和发布的情况;猜测是代码有 bug 导致,于是再仔细 review 下代码的实现,果真是代码有 bug 导致的。

Bug 分析

通过排查,我们发现问题是由代码 bug 导致的

具体来说,平台框架代码有这么个实现:请求进来时 main goroutine 有一个 defer recover panic 的实现,如下所示:

main.png

正常如果接口有 panic 的情况会被框架 recover 掉,然后打印相应的错误日志,即在日志系统可以查到对应的报错信息。

但是在排查问题时,之所以没有查到有对应 panic 的日志的原因是:在 cgi 接口业务代码的一个方法中起了多个 goroutine,即开了并发处理一些业务数据汇总的逻辑,而这些新开的 goroutine 中并没有做 defer recover,也就是说当这些 goroutine 中发生异常 panic 的话,整个接口就会崩溃退出。

而这里的 bug 恰恰就是开多个 goroutine 并发写一个共享对象 map(mapFloorId2Idx) 产生竞争导致的(会报 fatal error: concurrent map writes 的异常信息),然后这里程序会重启的原因是有后台监控进程监控到并拉起。实现代码如下:

entry.png

大家可以想下为什么框架外层的 main goroutine 捕获不到其它 goroutine 的 panic?

原因很简单,就是 Golang 中的 goroutine 没办法跨协程 recover,从而导致程序崩掉

Golang 在发生 panic 时,是先找到本 goroutine,再在这个 goroutine 里的 defer 函数里看看有没有 recover

解决方案

通过分析,我们找到了问题的根本原因:开多个 goroutine 并发写一个共享对象 map 产生竞争导致的。为了解决这个问题,我们需要做两个方面的工作:

  1. 在多 goroutine 操作共享的对象 map 时上锁
  2. 内部的 goroutine 函数要 defer recover

总结

在排查线上问题时,我们需要做到以下几点:

  • 快速响应:一旦发现问题,需要立即响应,不能拖延;
  • 有条理地排查:需要有一定的排查思路和方法,按照假设进行验证,逐步排除不符合实际的情况;
  • 细心仔细:需要仔细查看日志、代码等信息,不能遗漏任何细节;
  • 总结归纳:需要将排查过程和结果进行总结归纳,以便在以后的工作中能够更好地应对类似问题。

通过本次排查,我们不仅解决了一个具体的问题,更重要的是积累了一定的排查经验和技巧,这对我们今后的工作将会有很大的帮助。

相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
SQL 监控 网络协议
线上故障如何快速排查?来看这套技巧大全
有哪些常见的线上故障?如何快速定位问题?本文详细总结工作中的经验,从服务器、Java应用、数据库、Redis、网络和业务六个层面分享线上故障排查的思路和技巧。较长,同学们可收藏后再看。
线上故障如何快速排查?来看这套技巧大全
|
存储 缓存 监控
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
小伙伴们,有没有遇到过程序突然崩溃,然后抛出一个OutOfMemoryError的异常?这就是我们俗称的OOM,也就是内存溢出 本文来带大家学习Java OOM的三大经典场景以及解决方案,保证让你有所收获!
5358 0
美团面试:说说OOM三大场景和解决方案? (绝对史上最全)
|
消息中间件 Java 关系型数据库
后端接口性能优化分析
后端接口性能优化分析
413 0
|
运维 监控 数据库
线上服务故障处理原则
墨菲定律 任何事情都没有表面看起来那么简单 所有事情的发展都会比你预计的时间长 会出错的事情总会出错 如果担心某个事情发生,那么它更有可能发生 墨菲定律暗示我们,如果担心某种情况会发生,那么它更有可能发生,久而久之就一定会发生。
2375 0
|
NoSQL Java 应用服务中间件
大厂面试必备:如何轻松实现分布式Session管理?
这篇文章介绍三种分布式Session的实现方案:基于JWT的Token、基于Tomcat的Redis和基于Spring的Redis。JWT方案通过生成Token存储用户信息,实现无状态、可扩展的会话管理,但可能增加请求负载且数据安全性较低。Tomcat与Redis结合,通过配置Tomcat和Redis,实现Session集中管理和高性能存储,但配置相对复杂。Spring整合Redis适用于SpringBoot和SpringCloud项目,集成方便,扩展性强,但同样依赖外部Redis服务。每种方法有其优缺点,适用场景不同。作者小米是一个技术爱好者,欢迎关注其微信公众号“软件求生”获取更多技术内容
652 4
|
8月前
|
消息中间件 NoSQL 架构师
招行面试:亿级秒杀,超卖问题+少卖问题,如何解决?(图解+秒懂+史上最全)
45岁资深架构师尼恩在读者交流群中分享了如何系统化解决高并发下的库存抢购超卖少买问题,特别是针对一线互联网企业的面试题。文章详细解析了秒杀系统的四个阶段(扣库预扣、库存扣减、支付回调、库存补偿),并通过Redis分布式锁和Java代码示例展示了如何防止超卖。此外,还介绍了使用RocketMQ延迟消息和xxl-job定时任务解决少卖问题的方法。尼恩强调,掌握这些技术不仅能提升面试表现,还能增强实际项目中的高并发处理能力。相关答案已收入《尼恩Java面试宝典PDF》V175版本,供后续参考。
|
SQL 运维 监控
如何排查线上问题的?
在当今的互联网时代,线上问题对企业的业务连续性和用户体验产生的影响越来越大。无论是网站崩溃、应用性能下降,还是服务中断,这些问题都可能对企业的声誉和用户满意度造成严重影响。因此,快速、准确地排查并解决线上问题变得至关重要。本文将介绍一些高效的线上问题排查方法,帮助您在面对线上问题时,迅速定位并解决问题。我们将在接下来的内容中详细讨论如何利用日志分析、监控系统、代码审查等手段,以及如何制定有效的应急预案。通过这些策略的实施,您将能够提高线上问题的解决速度,减少对业务的影响,并提高用户满意度。
293 2
|
10月前
|
消息中间件 NoSQL Java
Spring Boot整合Redis
通过Spring Boot整合Redis,可以显著提升应用的性能和响应速度。在本文中,我们详细介绍了如何配置和使用Redis,包括基本的CRUD操作和具有过期时间的值设置方法。希望本文能帮助你在实际项目中高效地整合和使用Redis。
604 2
|
SQL 关系型数据库 MySQL
破防了,谁懂啊家人们:记一次mysql问题排查
某天用户反馈线上产品报错,本文记录了这次mysql问题排查和修复的过程,希望给大家参考。
|
Rust JavaScript 前端开发
【一起学Rust | 框架篇 | Frui框架】rust一个对开发者友好的GUI框架——Frui
【一起学Rust | 框架篇 | Frui框架】rust一个对开发者友好的GUI框架——Frui
2851 0