问题描述

  • 线上突然出现Dubbo超时调用,时间刚好为Consumer端设置的超时时间。
  • 有好几个不同的接口都报超时了
  • 第1次调用超时,第2次(或第3次)重试调用非常快(正常水平)
  • Dubbo调用超时的情况集中出现了3次,每次都是过一会自动恢复

排查

排查日志

看到调用超时,首先就拿着traceId去服务提供方查日志。

奇怪的是,在服务提供方的业务日志里面,只有正常的调用日志(耗时正常),没有超时调用的日志。

从正常的调用日志里面看,一切都是正常的,看不出所以然。

给人的感觉就是超时那次请求的调用没有达到服务提供方。

此时系统活动情况

通过系统历史监控,我们发现除了gc比平时稍微高一点外(也在正常水位),没有其他的异常;CPU、内存、网络等指标都在正常范围。

查看Dubbo线程活动情况

第2次系统集中超时报警的做的第一件事就是登录到那台服务器查看dubbo线程活动情况:看下能不能找到阻塞在哪一行代码。很遗憾,所有的dubbo线程都没有阻塞,都是正常的WAITING状态。

并没有明显表明阻塞在某段代码,这可难倒我们了:如果没有阻塞的话,为什么dubbo调用方会报超时?继续看代码

该接口是否存在阻塞的代码?

硬着头皮重新看代码每一个分支,突然发现底层的一个方法中有http调用!会不会是这个http调用导致的超时?如果是的话,那么不同的接口调用超时的情况就说的通了,因为上层大部分接口都会调用这个底层方法。

怀揣着激动的心,仔细看了http调用的逻辑:用的是JDK提供的HttpURLConnection,其中只用了HttpURLConnection#getContentLength方法,并且也在finally代码块中将这个连接关闭了。好像也不是这个引起的,起初还以为getContentLength会把文件给下载下来,但是看了接口文档以后发现只会去读头信息中的ContentLength。

    /**
* Returns the value of the {@code content-length} header field.
* <P>
* <B>Note</B>: {@link #getContentLengthLong() getContentLengthLong()}
* should be preferred over this method, since it returns a {@code long}
* instead and is therefore more portable.</P>
*
* @return the content length of the resource that this connection's URL
* references, {@code -1} if the content length is not known,
* or if the content length is greater than Integer.MAX_VALUE.
*/
public int getContentLength() {
long l = getContentLengthLong();
if (l > Integer.MAX_VALUE)
return -1;
return (int) l;
}

代码阻塞的情况可能性也不大,因为重试请求不会超时:如果代码阻塞,那么重试请求大概率也会超时。

数据访问层是否有异常情况

既然代码没有阻塞,那么有没有可能是数据访问层的异常造成的呢?毕竟不止一个接口存在超时的问题,如果是底层数据访问层的异常导致,那么也说得通。

重点排查了mysql,但结果是令人失望的:并没有慢SQL;并且dubbo超时期间,mysql实例的CPU和内存水位都是正常的。

除了mysql、redis实例本身指标正常外,基于上面同样的理由:如果数据访问层有问题,那么重试基本上也会超时。所以数据访问层导致超时的线索也被排除。

有没有可能是Dubbo层面的问题

排查再次陷入僵局,逼迫着我们重新梳理排查思路:

  1. 除了代码阻塞
  2. 除了数据访问层异常
  3. 除了超时请求,其他请求的日志都是正常的

那么还有可能会导致超时呢?会不会是Dubbo本身异常导致的?

此时有一个关键的线索进入我们的视野:超时的那次请求去哪儿了?

在服务提供方的日志里面没有超时请求的的日志,只有重试请求成功的业务日志。太奇怪了,就算超时总的留下日志的吧,日志都不留,欺负我胖虎吗?!

到这里想到超时的请求可能是一个突破口,于是开始看Dubbo的相关的源码和文档。

从官方文档中的服务端调用链一层层往下查

AllChannelHandler源码中看到了令人兴奋的注释:



兴奋之余,为了避免理解偏差,还特地用百度翻译了一下



没错,如果线程池已经满了,那么服务端不会返回,直到客户端超时!这不是正式我们碰到的问题吗?!

并且此时还没有进入业务代码,所以没有打印业务日志,这样就可以解释为什么没有服务提供方没有超时请求的日志了。

别激动,这里明明有返回threadpool is exhausted异常信息,怎么能说没有返回呢?

别急,这是另外一个项目引用的dubbo,版本是2.6.2。

回到出问题的那个项目,查看dubbo版本:2.8.6,查看AllChannelHandler源码:是的在2.8.6版本中,并没有返回这个错误



问题好像找到了,OK,剩下的就是验证了。

验证

准备

  • 将DubboServerHandler线程池的最大线程数调到5
  • 使用Apache Bench进行压测:200请求、并发10个线程

case1:复现问题

  • Dubbo使用2.8.8版本
  • 预期:部分请求超时报错,重试耗时正常
  • 压测结果符合预期:部分接口报错超时,并且重试请求耗时正常

case2:验证猜想

  • Dubbo使用2.6.2版本
  • 预期:部分请求报错线程池耗尽threadpool is exhausted,并且重试大概率也会报该错误
  • 压测结果符合预期

至此,基本判定线上Dubbo调用超时的问题就是因为线程池耗尽引起的。

这个超时问题前前后后查了一周左右,排查过程中试了很多排查方向,为了叙述方便就没有展开。


避坑指南

  1. 合理设置Dubbo线程池大小。默认是200
  2. 合理设置超时时间。如果真出现了Dubbo调用超时的情况,合理的超时时间能够避免服务调用方被打爆
  3. Dubbo接口必须有返回值。从AllChannelHandler#received的源码和注释中可以看到只有有返回值的接口才会返回线程池耗尽的错误信息;其它的情况则不会将错误信息返回给调用方,直到调用方超时。

不可忽视的Dubbo线程池的更多相关文章

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

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

  2. 动态线程池框架 DynamicTp v1.0.6版本发布。还在为Dubbo线程池耗尽烦恼吗?还在为Mq消费积压烦恼吗?

    DynamicTp 简介 DynamicTp 是一个基于配置中心实现的轻量级动态线程池管理工具,主要功能可以总结为 动态调参.通知报警.运行监控.三方包线程池管理等几大类. 经过几个版本迭代,目前最新 ...

  3. dubbo 线程池

    在dubbo调用过程中被调用方有两个线程池:io线程池,业务线程池. 这也是dubbo调优的点. 配置信息: <dubbo:protocol name="dubbo" dis ...

  4. dubbo线程池的拒绝策略

    jdk自带的原生的拒绝策略抛出的异常信息不够详细,而dubbo对拒绝策略进行了改写,抛出的信息更具有参考价值,值得我们借鉴. jdk自带的原生拒绝策略抛出的信息: // ThreadPoolExecu ...

  5. dubbo线程池作用于接口而不是方法

    记一次线上dubbo服务超时和线程池满问题排查 可能调用的接口没问题,但是该服务中的其他接口占用完了线程池,导致调用超时被拒绝处理.

  6. Dubbo学习笔记8:Dubbo的线程模型与线程池策略

    Dubbo默认的底层网络通讯使用的是Netty,服务提供方NettyServer使用两级线程池,其中 EventLoopGroup(boss) 主要用来接受客户端的链接请求,并把接受的请求分发给 Ev ...

  7. 记一次线上dubbo服务超时和线程池满问题排查

    线上某dubbo服务A调用dubbo服务B的接口X方法,调用端A日志中出现了很多超时的情况,提供端B该接口X超时时间设置为60s: 查看提供端B的日志,报了很多线程池满的异常: Caused by: ...

  8. HIPPO-4J 1.3.0 正式发布:支持 Dubbo、RibbitMQ、RocketMQ 框架线程池

    文章首发在公众号(龙台的技术笔记),之后同步到个人网站:xiaomage.info Hippo-4J 距离上一个版本 1.2.1 已经过去一个月的时间.在此期间,由 8 位贡献者 提交了 170+ c ...

  9. 简单RPC框架-业务线程池

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

随机推荐

  1. ubuntu Nginx+tomcat 部署web项目

    最近学习了一下java web方面的知识,最后终于把项目部署到了阿里云服务器上,还是遇到了一些难点,记录总结一下 首先就是网上资料中,jdk都比较老了,最新的jdk14,没有了jre,这样导致了tom ...

  2. C++核心编程 4 类和对象-封装

    C++面向对象的三大特性:封装.继承.多态 C++认为万事万物皆为对象,对象上有其属性和行为 封装 意义:1.将属性和行为作为一个整体,表现生活中的事物 语法: class 类名{   访问权限:属性 ...

  3. flask操作(增删改查操作)

    增加数据 from .models import Goods from app.extensions import db goods1 = Goods(name='魅族18s', price=3400 ...

  4. Golang通脉之包的管理

    在工程化的开发项目中,Go语言的源码复用是建立在包(package)基础之上的. 包(package)是多个Go源码的集合,是一种高级的代码复用方案,Go语言提供了很多内置包,如fmt.os.io等. ...

  5. C#与java TCP通道加密通信

    背景说明 公司收费系统需要与银行做实时代收对接,业务协议使用我们收费系统的标准.但是银行要求在业务协议的基础上,使用银行的加密规则. 采用MD5计算报文摘要,保证数据的完整性 采用RSA256对摘要进 ...

  6. [Beta]the Agiles Scrum Meeting 10

    会议时间:2020.5.25 21:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 issue yjy 暂无 tq 暂无 wjx 实现创建.显示博客作业功能 增加博客作业功能 dzx 实 ...

  7. Django+Vue跨域配置与经验

    一.原理 同源?同源策略? 同源的定义是:两个页面的协议.端口和域名都相同 同源的例子: 不同源的例子: 同源策略SOP(Same origin policy)是一种浏览器约定,它是浏览器最核心也最基 ...

  8. [no code][scrum meeting] Alpha 6

    项目 内容 会议时间 2020-04-13 会议主题 后端技术细节分析 会议时长 30min 参会人员 PM+后端组成员 $( "#cnblogs_post_body" ).cat ...

  9. IDEA + maven 零基础构建 java agent 项目

    200316-IDEA + maven 零基础构建 java agent 项目 Java Agent(java 探针)虽说在 jdk1.5 之后就有了,但是对于绝大多数的业务开发 javaer 来说, ...

  10. 2021.7.15考试总结[NOIP模拟16]

    ZJ模拟D2就是NB.. T1 Star Way To Heaven 谁能想到这竟是个最小生成树呢?(T1挂分100的高人JYF就在我身边 把上边界和下边界看成一个点和星星跑最小生成树,从上边界开始跑 ...