用Arthas"庖丁解牛"

179 阅读4分钟

生产环境的bug开发环境无法复现怎么办?关键位置没有打印日志信息不足怎么办?莫慌,骚年。让强大的Arthas法师来carry,带你去生产环境"遨游"闯关。

刚接触Arthas,就被它能够watch方法的输入参数和返回值的功能震惊到了。这简直太酷炫了,让你可以像本地单步调试一样,跟踪到每一步的执行结果和获取当前的变量数值。以前要定位线上问题,信息不足就需要加日志打印,定位问题,可能需要反复重启应用。用了Arthas,根本不需要加日志打印,重启应用这些操作。花了大概一个周末下午,在本地跑了下官方demo,熟悉了下常用操作。脑子里对Arthas能够做什么,能解决什么,怎么解决,已经有了大概的了解。后面需要用的时候,就能派上大用场了(学了就一定有bug会找上门的=.=)。

下面介绍一个特意找上门的bug。

背景:同一个聊天交友类产品,对外以一个主品牌以及多个新品牌进行发布。服务端是共用一套数据的,但是所有对外展示的信息,涉及到品牌相关的,需要进行文案替换。在同一个群组里,主品牌和新品牌的用户可以互相聊天。

问题现象: 1、线上某个群组里面,同一条聊天消息涉及到需要替换文案的内容时,主品牌侧的用户有的显示正常,有的显示为新品牌文案。 2、不同的群聊天消息,同一个用户有的展示正常,有的异常。 3、新品牌侧的用户看到的群聊天消息文案替换正常。

先贴下相关的代码(用Arthas的jad直接反编译的源码):

群聊消息下发方法

群聊消息下发方法

write方法文案替换逻辑代码

文案替换逻辑代码

PublishMessage是下行消息类, replaceMsgMap是提前生成好的各个新品牌对应文案,主品牌使用原始消息文案。

乍一看,文案替换逻辑没啥毛病(但问题就在这,大家可以先思考下),感觉自己又要去面对一个扑朔迷离的玄学bug了(永远不要把程序bug归结为灵异事件)。代码看不出问题,本地单步调试鼓捣了一早上,也没复现出来,看来只能在生产环境定位了,Arthas要登场了。

由于生产环境的消息转发量很大,直接attach进程风险太高,且不利于单条消息观察定位。所以选择预发布环境进行attach,请求量可控,数据和线上一致,也只有读操作,不会影响到生产环境。

用Arthas的watch命令,观察write方法的输入参数。

  • -x 表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是1
  • -b 表示观察方法调用前

可以看到,publishMessage、userSession参数的值都显示出来了。接着就可以在预发布触发消息下行进行数据观察了。

建了个测试群,除了自己一个主品牌的测试用户还有另外一个新品牌用户。最初开始发送了几条群聊消息都正常,后面又拉了一个新品牌用户以及主品牌测试用户,复现的概率就高了许多。观察了下同一条群聊消息发给每个群成员的publishMessage值,发现如果先遍历到了新品牌用户,再遍历到主品牌用户时,publishMessage的文案居然是新品牌文案!!!心里猛的一惊,是了,就是这个低级错误造成的bug,大家应该也猜到原因了。

下面揭晓下这个问题产生的原因:

  • 遍历群成员传递的publishMessage形参,每次改变payload都会影响到被传递进去的publishMessage实参
  • replaceMsgMap里只存储了新品牌文案
  • 主品牌根据appName获取对应文案时为空,则不设置payload,使用最传递进来的publishMessage的payload
  • 遍历群组成员时,顺序是随机性的

如果某个主品牌用户在新品牌用户之后被遍历到,那么publishMessage的payload字段就会被设置为新品牌文案。而主品牌在replaceMsgMap里找不到对应文案,就不更新payload了,复用了上一次被遍历用户的payload,就会出现文案显示异常。

知道原因后就好处理了,replaceMsgMap里把主品牌的文案也加进去,每次遍历到主品牌,也更新payload字段,保证文案正常显示。

整个定位过程,无需增加log日志,线上应用也无需重启,便能获取足够的信息进行问题排查定位,是不是贼好用了。花个半天时间,摸索鼓捣下,要用时就能省下不少工夫。详细使用文档见->官方文档