天天看点

记一次Golang程序崩溃问题分析思路

问题描述

问题是这样的,线上一个推送服务以http接口方式支持内部其他服务的推送需求。众所周知,对于大多数APP来说push都是保证DAU的重要途径。所以,推送服务的稳定性至关重要。

昨天开始,我负责的推送接口接连报了很多次502状态码,对应的推送请求也失败了。所以我用了半天的时间都在分析接口失败的原因。

分析日志

查看线上问题的最好方式就是分析日志,线上服务都会打很多trace日志。我的推送服务通过全局trace_id把请求串联起来,所以可以快速跟踪请求是否正常执行。

分析日志我发现几个现象:

  1. 返回502状态码的请求的nginx日志upstream_time是0,request_time也是0。并且没有对应的服务日志。
  2. 由于上游服务是golang程序,基于GIN框架的web服务。每次启动会有启动日志,我发现问题阶段有大量的启动日志。
  3. 上游服务在问题阶段有大量的redis连接超时报错。
  4. 替换了有问题的redis proxy机器后,服务报错都没有了。
记一次Golang程序崩溃问题分析思路

经过分析可以推断出,由于Redis代理机器故障,导致推送服务连接Redis超时,不明原因导致推送服务挂掉。此时内部服务有推送需求,连接上推送服务下游nginx反向代理,但是反向代理转发到推送服务时由于服务处于不可用状态才会返回502状态码。

复现

原因推断出来后,就要复现问题。在测试环境中我把Redis的连接超时调短,推送服务很快down,并抛出panic。

记一次Golang程序崩溃问题分析思路

同时也出现了连接Redis失败报错。

记一次Golang程序崩溃问题分析思路

结合报错信息,我断定是自己代码中的问题。应该是在连接失败时,还是用Redis连接对象conn,并调用Close()函数关闭连接。 此时,可加上defer语句主动close连接。修复bug后在复现问题并没有导致程序down,因此问题解决。

总结

此次问题分析思路,是典型的线上服务问题定位方法。通过解决这个问题,可以总结重要经验,也可以帮助读者解决类似问题。

继续阅读