写点什么

Google Guava 中 EventBus 导致线程阻塞

作者:红袖添香
  • 2023-06-19
    北京
  • 本文字数:5939 字

    阅读完需:约 19 分钟

Google Guava中 EventBus 导致线程阻塞

前言

事件起因源自一条报警

这个之前 SRE 添加的报警机制,报警经常出现在业务高峰,中途过十几分钟又会恢复正常;

暂时无线上问题,最近事情比较多,也没有时间跟踪报警(当报警非常多的时候,就和没有报警是一个效果)


排查步骤


最近临近大促,报警日渐频繁,需要排除系统风险


先找到一台机器,碰碰运气


这里不得不说,arthas 是个非常好使的工具 -- 真香


查询所有 BLOCKED 状态线程信息


arthas@1]$ thread --state BLOCKEDThreads Total: 963, NEW: 0, RUNNABLE: 137, BLOCKED: 29, WAITING: 541, TIMED_WAITING: 256, TERMINATED: 0
复制代码

列出结果:

ID          NAME                                                                 GROUP                              PRIORITY               STATE                  %CPU                   DELTA_TIME              TIME                   INTERRUPTED            DAEMON                 33962       pool-34-thread-1855                                                  main                               5                      BLOCKED                0.06                   0.000                   0:2.019                false                  false                  33814       pool-34-thread-1836                                                  main                               5                      BLOCKED                0.0                    0.000                   0:1.455                false                  false                  33819       pool-34-thread-1841                                                  main                               5                      BLOCKED                0.0                    0.000                   0:1.789                false                  false                  33821       pool-34-thread-1843                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.977                false                  false                  33822       pool-34-thread-1844                                                  main                               5                      BLOCKED                0.0                    0.000                   0:1.857                false                  false                  33963       pool-34-thread-1856                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.983                false                  false                  33964       pool-34-thread-1857                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.861                false                  false                  33966       pool-34-thread-1859                                                  main                               5                      BLOCKED                0.0                    0.000                   0:1.287                false                  false                  33967       pool-34-thread-1860                                                  main                               5                      BLOCKED                0.0                    0.000                   0:1.555                false                  false                  34224       pool-34-thread-1875                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34225       pool-34-thread-1876                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34226       pool-34-thread-1877                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34227       pool-34-thread-1878                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34228       pool-34-thread-1879                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34229       pool-34-thread-1880                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34230       pool-34-thread-1881                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34231       pool-34-thread-1882                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34232       pool-34-thread-1883                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34233       pool-34-thread-1884                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34234       pool-34-thread-1885                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34235       pool-34-thread-1886                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34236       pool-34-thread-1887                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34237       pool-34-thread-1888                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34238       pool-34-thread-1889                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34239       pool-34-thread-1890                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34240       pool-34-thread-1891                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34241       pool-34-thread-1892                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34242       pool-34-thread-1893                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                  34243       pool-34-thread-1894                                                  main                               5                      BLOCKED                0.0                    0.000                   0:0.000                false                  false                                                                                                                                                                                                                                                                                                  
复制代码


排查具体线程 - 查看调用栈


[arthas@1]$ thread 33814
复制代码

查询结果

"pool-34-thread-1836" Id=33814 BLOCKED on com.google.common.eventbus.Subscriber$SynchronizedSubscriber@45b1236e owned by "pool-34-thread-1857" Id=33964    at com.google.common.eventbus.Subscriber$SynchronizedSubscriber.invokeSubscriberMethod(Subscriber.java:153)    -  blocked on com.google.common.eventbus.Subscriber$SynchronizedSubscriber@45b1236e    at com.google.common.eventbus.Subscriber$1.run(Subscriber.java:80)    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)    at java.lang.Thread.run(Thread.java:748)
复制代码

注意:上述两步不一定能拿到预期结果,

1. 因为 dubbo 的软负载,路由的不一定是当前机器;

2. 案发现场比较难抓,很有可能抓的时候,线程状态已经变了;

多试几次,总会有想要的结果

事后分析


配置监控观察信息

按 POD 分组


根据上一步的堆栈信息查看代码



EventBus 问题(摘自:Guava 问题排查文档)


先简单介绍下 EventBus,在业务逻辑中存在一种订阅发布的场景,跨服务的订阅发布可以使用 mq 等实现,内存中就可以使用 Guava EventBus 来实现:

单个时间 Event 可以被多个订阅方法去处理。

为了理解其中的实现逻辑,预计查看了 EventBus 实现类:

首先 EventBus 会获取所有注册的 @Subscribe 方法:

会根据 @Subscribe 的方法去创建一个 Subscriber:

创建的 Subscriber 有两类,这里有一个判断条件 isDeclaredThreadSafe(method):

如果这个 Subscriber 申明了 @AllowConcurrentEvents 注解,则 new Subscriber(bus, listener, method),如果没有申明了则 new SynchronizedSubscriber(bus, listener, method);

继续查看了 SynchronizedSubscriber:

SynchronizedSubscriber 在调用业务逻辑的时候,会使用 synchronized 块加锁,也就是说给了多线程但实际依旧是多线程抢占锁然后顺序执行,这样映射上面查看的 30 个线程池中只有一个线程在执行,其他的均 BLOCKED,BLOCKED 的线程都在等待锁。

继续验证此问题,诊断了 invokeSubscriberMethod 执行的耗时:

trace com.google.common.eventbus.Subscriber invokeSubscriberMethod
复制代码


从结果来看,invokeSubscriberMethod 执行时间要 200ms+,也 200ms 的开销也就是线程抢占所等待的时间,到此问题已经确认。


结论


Google guava 中的 AsyncEventBus 组件默认是同步处理,使用多线程但未设置 @AllowConcurrentEvents 会出现多线程之间的抢锁情况;

项目中,每类订阅者 Subscriber 是单例的,所以默认的 SynchronizedSubscriber 中 synchronized(this),会导致该订阅者的所有任务是串行的;

生产者是多线程执行,就会导致订阅者阻塞


解决方案


因为订阅者执行任务,没有依赖关系可以最简单粗暴 添加 @AllowConcurrentEvents

个人建议:业务代码中异步执行事件,一定要做到原子化(方便扩展,遇到性能问题可以通过水平扩容解决);

及时关注异常 - 千里之堤毁于蚁穴


参见文章:https://zhuanlan.zhihu.com/p/516710052 解决方案


参考文章


线程状态:

https://blog.csdn.net/hellozhxy/article/details/94739510


arthas 使用文档

https://arthas.aliyun.com/doc/thread.html


Guava 问题排查文档

https://zhuanlan.zhihu.com/p/516710052


发布于: 刚刚阅读数: 9
用户头像

红袖添香

关注

大雨落幽燕,白浪滔天 2018-08-10 加入

还未添加个人简介

评论

发布
暂无评论
Google Guava中 EventBus 导致线程阻塞_Java_红袖添香_InfoQ写作社区