又踩到Dubbo的坑,但是这次我笑不出来
作 者:肥朝
原文链接:;utm_medium=referral
前言直入主题 , 线上应用发现 , 偶发性出现如下异常日志 。
文章插图
文章插图
当然由于线上具体异常包含信息量过大 , 秉承让肥朝的粉丝没有难调试的代码的原则 , 我特意抽取了一个复现的demo放在了git,让你不在现场 , 一样享受到排查的快乐!但是最近 , 太多假粉伸手党拿到地址就跑 , 因此我把地址藏在本文某个角落 , 因此认真看文的才能找到!(重点)
由于工作性质的原因 , 上班时间根本抽不出时间做其他事 , 修bug , 都只能下班时间来做 , 因此周六就到公司搬砖了 。
文章插图
什么是ConcurrentModificationException?中文意思就是 , 并发修改异常 。 也就是我们常说的fail-fast(快速失败) 。 当然肥朝更认为 , 快速失败是一种思想 , 比如Spring会在启动的时候做大量的检查 , 什么bean找不到 , 依赖注入错误等等 , 都会把一些显而易见的错误检查出来 , 防止在项目跑着跑着期间再失败 , 也就是提前检查 。 无论是业务开发 , 还是基础组件开发 , 亦或是生活中 , 这个思想都是可以用到的 。
【又踩到Dubbo的坑,但是这次我笑不出来】那么 , 言归正传 , 这个异常到底什么意思啊 。 简单说就是 , 当一个集合在遍历的时候 , 他的元素也正在被修改 。 刚学java那会 , 我们边遍历边删除就会出现这个异常 。 ConcurrentModificationException的原理这些网上太多 , 肥朝就暂且不提 。 那么我们来看下异常栈 。
文章插图
文章插图
好了 , 我们已经找到了RpcContext.getContext().getObjectAttachments()正在遍历 。 那么 , 只要找到谁在修改他就行了啊 , 就这?
文章插图
难点分析很明显 , 这里面并不存在遍历的同时修改元素 , Dubbo的代码还不至于有这个明显的bug 。 出现ConcurrentModificationException , 就有可能是 , A线程在遍历 , B线程在修改 。
但是肥朝 , 你说了这么多 , 我还是没发现这个问题有什么难的啊!
这个问题难点主要在于 , 在Dubbo里面 , RpcContext是对应一个线程的 , 你可以简单理解为ThreadLocal的增强版 。 也就是说 , A线程拿出来的 , 和B线程拿出来的RpcContext都不是同一个 , 何来并发修改同一个之说?当然官方文档给了我一个启示 。
文章插图
会不会有同学在线程开启前拿到RpcContext,然后在新线程中 , 做set操作(图中的get操作是没有问题的) 。
于是 , 似乎豁然开朗的我 , 顺着这条线索 , 周六加了一天班 , 把代码翻了个遍 , 最后发现没有找到 。
索然无味还是柳暗花明?并发这东西 , 要么不出问题 , 一旦出问题都是很难找 。 观察了线上日志 , 重现概率很小 , 就一小段日志 , 并且业务方很忙 , 也没时间配合你查问题 。 于是只能顺着源码 , 把Dubbo的整个请求到响应的过程在脑海中快速过几遍 , 看看哪个环节有可能出问题 , 做了无数的假设 。 随着一次次的假设失败 , 在即将身体索然无味之际 , 还真发现了一些蛛丝马迹!(注意 , 本文所用到的 , 都是dubbo2.7.6)
- 三星S21全面曝光,五年前挖的坑,自己出来了,国产手机进去了
- 分布式天花板?阿里百万架构师的ZK+Dubbo笔记,颠覆认知
- 我又踩坑了!如何为 HttpClient 请求设置 Content-Type 标头?
- dubbo实战之一:准备和初体验