本文记录一次线上全链路压测出现的Dubbo线程池队列满的问题。

1 问题描述

线上做全链路压测,其中涉及三个系统,调用关系A->B->C,均是dubbo调用。压测的时候C出现CPU满导致服务响应超时的情况,进而导致B以及A接口均超时。停止压测后,B->C的流量依然未有明显降低,系统收敛慢,影响线上业务。

2 问题分析

2.1 调用来源分析

首先分析停止压测后,这些B对C的调用的来源是来自哪里。
根据全链路的traceId,发现这些调用是来自系统A的调用,但是既然是停止压测了,那A怎么还会有这些对B的调用?
从时间上看,A系统的这条traceId的日志要比B系统的这条traceId的日志早整整12分钟。也就是说,压测期间的A对B的调用,在B系统上被延迟了12分钟后执行。
这个时候初步可以判断和dubbo的线程池队列有关。

2.2 日志分析

在A系统上搜日志,发现有线程池耗尽的异常日志:

1

2

3

4

5

Caused by: java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUSTED! Thread Name: DubboServerHandler-XX.XX.XX.XX:XXXX, Pool Size: 200 (active: 200, core: 200, max: 200, largest: 200), Task: 39522284 (completed: 39422084), Executor status:(isShutdown:false, isTerminated:false, isTerminating:false), in dubbo://XX.XX.XX.XX:XXXX!

at com.alibaba.dubbo.common.threadpool.support.AbortPolicyWithReport.rejectedExecution(AbortPolicyWithReport.java:53)

at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)

at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)

at com.alibaba.dubbo.remoting.transport.dispatcher.all.AllChannelHandler.received(AllChannelHandler.java:56)

通过IP看,是来自于系统B,说明A收到了B的dubbo线程池满的错误信息。

2.3 Dubbo线程池原理

dubbo的provider有2种线程池:

  • IO处理线程池。(直接通过netty等来配置)
  • 服务调用线程池。

netty线程模型
Dubbo线程模型

boss线程:
accept客户端的连接;
将接收到的连接注册到一个worker线程上
个数:
通常情况下,服务端每绑定一个端口,开启一个boss线程
worker线程:
处理注册在其身上的连接connection上的各种io事件
个数:
默认是:核数+1
注意:
一个worker线程可以注册多个connection

Dubbo的事件派发策略
默认是all:

1

dispatcher dispatcher string 可选 dubbo协议缺省为all 性能调优 协议的消息派发方式,用于指定线程模型,比如:dubbo协议的all, direct, message, execution, connection等 2.1.0以上版本

  • all:
    所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。 即worker线程接收到事件后,将该事件提交到业务线程池中,自己再去处理其他事。

Dubbo事件派发策略-all

  • direct:
    worker线程接收到事件后,由worker执行到底。
  • message:
    只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO线程上执行
  • execution:
    只请求消息派发到线程池,不含响应(客户端线程池),响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行
  • connection:
    在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

服务调用调用线程池

对于服务调用线程池,dubbo默认使用的是固定大小线程池,官方文档-dubbo:protocol:

1

threadpool threadpool string 可选 fixed 性能调优 线程池类型,可选:fixed/cached 2.0.5以上版本

源代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

* 此线程池启动时即创建固定大小的线程数,不做任何伸缩,来源于:<code>Executors.newFixedThreadPool()</code>

*

* @see java.util.concurrent.Executors#newFixedThreadPool(int)

* @author william.liangf

*/

public class FixedThreadPool implements ThreadPool {

public Executor getExecutor(URL url) {

String name = url.getParameter(Constants.THREAD_NAME_KEY, Constants.DEFAULT_THREAD_NAME);

int threads = url.getParameter(Constants.THREADS_KEY, Constants.DEFAULT_THREADS);

int queues = url.getParameter(Constants.QUEUES_KEY, Constants.DEFAULT_QUEUES);

return new ThreadPoolExecutor(threads, threads, 0, TimeUnit.MILLISECONDS,

queues == 0 ? new SynchronousQueue<Runnable>() :

(queues < 0 ? new LinkedBlockingQueue<Runnable>()

: new LinkedBlockingQueue<Runnable>(queues)),

new NamedThreadFactory(name, true), new AbortPolicyWithReport(name, url));

}

}

可以看到这里队列的默认大小是0.拒绝策略:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {

protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);

private final String threadName;

private final URL url;

public AbortPolicyWithReport(String threadName, URL url) {

this.threadName = threadName;

this.url = url;

}

@Override

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

String msg = String.format("Thread pool is EXHAUSTED!" +

" Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +

" Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!" ,

threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),

e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),

url.getProtocol(), url.getIp(), url.getPort());

logger.warn(msg);

throw new RejectedExecutionException(msg);

}

}

超过队列大小会直接丢弃请求,并打印错误日志,与之前看到的A系统中的异常日志吻合。

dubbo处理业务请求示意图:
Dubbo线程模型

奇怪的是,dubbo官方默认队列大小是0,为何会出现这么多请求延迟的问题?原来是公司的dubbo版本做了定制,将队列大小默认设置为10W。

3 解决方案

对于当前的业务场景,在下游服务异常时需要快速失败,于是将队列大小改为比较小的值。

4 参考资料

  • Dubbo源代码分析八:再说Provider线程池被EXHAUSTED
  • dubbo线程模型
  • http://ifeve.com/dubbo-threadmodel/

欢迎关注微信公众号获取更多Java相关资源:SimpleJava

记一次线上压测Dubbo线程池队列满的问题相关推荐

  1. 记几次 [线上环境] Dubbo 线程池占满原因分析(第三次:GC STW)

    [线上环境] Dubbo 线程池占满原因排查系列 记几次 [线上环境] Dubbo 线程池占满原因分析(第一次:HttpClient) 记几次 [线上环境] Dubbo 线程池占满原因分析(第二次:C ...

  2. 13个QA带你了解线上压测的知识点

    摘要:设计一个线上压测系统能让我们学习到多少东西?这13个问题看你能否搞定. 本文分享自华为云社区<设计一个线上压测系统能让我们学习到多少东西?13个问题看你能否搞定>,作者:breakD ...

  3. dubbo 服务压测_不可忽视的Dubbo线程池

    问题描述 线上突然出现Dubbo超时调用,时间刚好为Consumer端设置的超时时间. 有好几个不同的接口都报超时了 第1次调用超时,第2次(或第3次)重试调用非常快(正常水平) Dubbo调用超时的 ...

  4. dubbo官方文档_不可忽视的Dubbo线程池

    问题描述 线上突然出现Dubbo超时调用,时间刚好为Consumer端设置的超时时间. 有好几个不同的接口都报超时了 第1次调用超时,第2次(或第3次)重试调用非常快(正常水平) Dubbo调用超时的 ...

  5. dubbo日志关闭_不可忽视的Dubbo线程池避坑指南

    推荐阅读: 阿里巴巴4面Java岗位:算法+性能调优+并发+多线程+数据库 Dubbo+Kafka+MyBatis+reids+Spring+多线程等,学完就去面试BAT 问题描述 线上突然出现Dub ...

  6. 用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很厉害?

    前言 micrometer 中自带了很多其他框架的指标信息,可以很方便的通过 prometheus 进行采集和监控,常用的有 JVM 的信息,Http 请求的信息,Tomcat 线程的信息等. 对于一 ...

  7. 如何使用Arthas定位线上 Dubbo 线程池满异常

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | 公众号「Kirito的技术分享」 前言 本文是 ...

  8. Arthas | 定位线上 Dubbo 线程池满异常

    作者 | 徐靖峰  阿里云高级开发工程师 前言 Dubbo 线程池满异常应该是大多数 Dubbo 用户都遇到过的一个问题,本文以 Arthas 3.1.7 版本为例,介绍如何针对该异常进行诊断,主要使 ...

  9. Dubbo线程池耗尽问题

    场景:dubbo 线程池耗尽,报错. Caused by: java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUS ...

最新文章

  1. Microsoft Exchange 2010 SP1 (一)安装准备
  2. 【Linux】 任务调度/计划 cron
  3. Eclipse搭建Android5.0应用开发环境 “ndk-build”:launchingfailed问题解决
  4. qconbeijing2014
  5. linux profile of env
  6. python中ioerror怎么解决_Python IOError错误异常原因|python基础教程|python入门|python教程...
  7. 牛客网——今年的第几天
  8. 机器学习笔记(五)——朴素贝叶斯分类
  9. html编写气泡对话框,HTML+CSS入门 纯CSS手写圆角气泡对话框
  10. 如何下载python3.6版本-python最新版本免费下载-python 3.6.3正式版下载__飞翔下载
  11. SQLServer之修改DEFAULT约束
  12. 逻辑回归:详细建模流程与例子代码
  13. 华为网络设备查看设备温度状态检查命令方法
  14. 用python计算工资工资_php项目中用python来预测薪资(工资)
  15. 第九届蓝桥杯(国赛)——阅兵方阵
  16. qduoj 分辣条1 (搜索+剪枝)
  17. SDH与SONET(整理)
  18. 【Pygame基本使用】
  19. python正则 两边固定 中间任意字符
  20. 从程序员到测试工程师

热门文章

  1. python处理shp和栅格文件的相关库shapefile、gdal等
  2. 用python计算绩点的代码_【Python】计算GPA
  3. element-ui 左侧边栏el-menu组件: 路由跳转 - 案例篇
  4. SEO优化篇 - 搜索引擎抓取href=#!锚点
  5. 免费zblog mip主题aymFreeTwo
  6. 黑鱼资源网完整版织梦整站源码打包分享
  7. 区块链JAVA数字交易所官方商业版开发级全套三端纯源码
  8. 一款打包免签分发平台源码+搭建说明
  9. android datepicker 参数设置,如何在Android DatePicker中设置最小和最大日期?
  10. db2中null和空值的区别_MySQL数据库的表中 NULL和空值 到底有什么区别呢?