Dubbo 源码剖析与实战
何辉
平安壹钱包架构师
4711 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
开篇词 (1讲)
Dubbo 源码剖析与实战
15
15
1.0x
00:00/00:00
登录|注册

02|异步化实践:莫名其妙出现线程池耗尽怎么办?

你好,我是何辉。今天我们来探索 Dubbo 框架的第一道特色风味,异步化实践。
Dubbo 以前作为一个高性能的 RPC 框架,现在已然上升成为了一个微服务框架,但本质还是用来提供 RPC 服务的,这就势必存在同步调用和异步调用的方式。
同步调用方式比较简单直接,但我们也常常遇到因为调用量增加,原本不出幺蛾子的功能突然爆发问题的情况。比如:
关于 Socket 的 BIO 程序,随着调用量的增加,为什么用着用着就出现了一些性能问题呢?
某部分非常复杂又有点耗时的功能,测试环境验证得好好的,一放到有着高流量的产线运行,为什么莫名其妙就出现线程池耗尽问题呢?
这么说有点抽象,我们结合具体代码来看看。相信你肯定写过这样的代码:
@DubboService
@Component
public class AsyncOrderFacadeImpl implements AsyncOrderFacade {
@Override
public OrderInfo queryOrderById(String id) {
// 这里模拟执行一段耗时的业务逻辑
sleepInner(5000);
OrderInfo resultInfo = new OrderInfo(
"GeekDubbo",
"服务方异步方式之RpcContext.startAsync#" + id,
new BigDecimal(129));
return resultInfo;
}
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Dubbo框架的异步化实践是本文的重点,通过介绍同步调用和异步调用的方式,探讨了在高流量情况下可能出现的线程池耗尽问题。文章通过具体代码示例展示了如何优化同步调用的耗时功能服务为异步形式,并提出了两种思路来解决异步化时无法返回结果的问题。作者深入浅出地介绍了Dubbo框架的异步化实践,为读者提供了解决线程池耗尽问题的思路和方法。文章还详细讨论了Dubbo框架在源码层面的异步实现原理,以及适用的异步应用场景。通过对Dubbo框架异步化实践的深入剖析,读者能够快速了解异步化的技术特点和应用场景。文章还提出了有趣的多任务场景题,引导读者深入理解CompletableFuture中的异步调用方法。同时,通过分析异常日志,读者可以学习如何从异常信息中收集关键信息并分析问题。整体而言,本文内容丰富,涵盖了Dubbo框架异步化实践的方方面面,对读者快速了解异步化技术具有重要参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Dubbo 源码剖析与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(23)

  • 最新
  • 精选
  • 张申傲
    个人认为,理解异步化有个前提,就是要区分两个线程:请求处理主线程&业务异步子线程: - 请求处理主线程:由 Dubbo 框架提供,主要用于接收 RPC 请求。线程池大小默认为200。 - 业务异步子线程:由业务自定义,可设置线程池大小、队列长度、拒绝策略等,用于异步执行业务逻辑。 异步化的核心思想在于,将本来需要由主线程来执行的耗时操作,交给异步子线程来执行,使得主线程可以快速执行完成,避免 Dubbo 线程池被耗尽导致服务不可用。站在调用方的角度来看,实际请求的执行时间并没有缩短,但是服务整体的吞吐量是有很大的提升的。

    作者回复: 你好,张申傲:非常 nice,你总结的非常到位,很有自己独到的见解,点赞~

    2023-01-30归属地:北京
    3
    14
  • java小霸王
    拦截处只需要调用 java.util.concurrent.CompletableFuture#get(long timeout, TimeUnit unit) 方法就可以很轻松地拿到异步化结果了。 这里拦截处是谁去调用呀,另外的线程轮训吗,还是一开始请求的线程阻塞。

    作者回复: 你好,java小霸王:这里为了简单理解,我抽象为了拦截处。等你学到后面的“发布、订阅、调用”章节的时候,掌握的知识点更多的时候,你通过断点的时候,你会发现 org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest 方法的 Future 对象在 whenComplete 方法中持有了 channel(即最终是 NettyClient)的引用。

    2022-12-27归属地:广东
    3
    3
  • Six Days
    请问一下asyncContext.write(resultInfo); 这里将resultInfo 写入Future 之后,Dubbo框架什么时候调用Future.get 获取计算结果?

    作者回复: 你好,Six Days:【asyncContext.write(resultInfo); 】执行之后是将结果写入到了 Future 当中,但是还有另外一个底层在调用这个 Future#get 的结果,这个调用的地方就是在【org.apache.dubbo.remoting.exchange.support.header.HeaderExchangeHandler#handleRequest】方法中的【handler.reply(channel, msg);】代码处,【handler.reply(channel, msg);】返回的对象就是 Future 对象,然后调用 Future 对象的 whenComplete 方法,调用完后若没有结果就会等待,有结果的话就会立马进入 whenComplete 方法的回调逻辑中。

    2023-03-16归属地:广东
    2
  • 咸鱼王
    之前只用过在reference标签上设置async="true" sent="false"的方式来实现无返回值的异步,这种应该是属于消费端的异步吧? 没想到还支持将provider的dubbo线程和业务处理线程分开,释放dubbo线程,还可以返回结果,开了眼了。

    作者回复: 你好,在雨中:【async="true" sent="false"】结合起来就是异步形式,发送请求时并不会等待消息发送出去,而是将消费放入到队列中就完事了。至于后续要在消费方拿到结果的话,可以想办法从 RpcContext 中拿到 Future 对象并调用 Future#get 方法拿到返回值。

    2023-03-10归属地:江西
    2
  • 张洋
    老师,看了下这块的源码,其实dubbo还是通过ThreadLocal(InternalThreadLocal可以在父子线程中共享数据)来存储context,ATTACHMENT 这些属性,感觉还是通过ThreadLocal的方案,不过是dubbo自己做了一层封装吧,不知道这样理解对不

    作者回复: 你好,张洋:你理解的没错,dubbo 在衔接上下文的时候,本质还是进行了 ThreadLocal 传递。

    2023-01-03归属地:广东
    2
  • SunshineBoy
    老师,举例时可以把各种IO模型处理方式的弊端加上吗?毕竟内存是有限的

    作者回复: 你好,SunshineBoy:不好意思,主要是每篇篇幅有限,我给你找了一篇写的有点生活形象点的例子,附上链接如下: https://blog.csdn.net/lqy971966/article/details/118157808?spm=1001.2101.3001.6650.9&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-9-118157808-blog-100849453.pc_relevant_3mothn_strategy_and_data_recovery&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-9-118157808-blog-100849453.pc_relevant_3mothn_strategy_and_data_recovery&utm_relevant_index=10

    2022-12-30归属地:广东
    2
  • Geek_1cc6d1
    provider异步的意义是啥?避免dubbo线程耗尽么,直接把dubbo线程数量搞大点不就行了?

    作者回复: 你好,Geek_1cc6d1:这个问题在前面的问题回答过,我直接将内容引用过来了: 其次可以这么理解,提供方的异步化旨在提高吞吐量,说白了就是用自己的线程池去承处理业务,以此来释放Dubbo框架本身的线程去处理其他耗时比较短的请求,就有点类似于脏活累活交给一个特定的线程池让它自己扛,能扛多少是多少,既轻松又快的活Dubbo自己干,见缝插针似的尽可能提升提供方的整个吞吐。 最后如果是这种慢请求打到单机瓶颈了那还真得好好看看优化,如果是那种耗时非常短的请求打到单机瓶颈了,那你得后续考虑加大Dubbo本身线程核心数据或扩容或者有损限流降级等等之类的了。

    2022-12-30归属地:广东
    1
  • 胡月🌈
    改造后,拦截地方feature.get也会阻塞住,消耗线程资源吧。这样岂不是每个拦截的地方消耗的线程资源并没有减少。大量请求过来的时候,线程还是不够用。

    作者回复: 你好,胡月:得先感谢你的认真思考,提出了个比较好的问题。 其次可以这么理解,提供方的异步化旨在提高吞吐量,说白了就是用自己的线程池去承处理业务,以此来释放Dubbo框架本身的线程去处理其他耗时比较短的请求,就有点类似于脏活累活交给一个特定的线程池让它自己扛,能扛多少是多少,既轻松又快的活Dubbo自己干,见缝插针似的尽可能提升提供方的整个吞吐。 最后如果是这种慢请求打到单机瓶颈了那还真得好好看看优化,如果是那种耗时非常短的请求打到单机瓶颈了,那你得后续考虑加大Dubbo本身线程核心数据或扩容或者有损限流降级等等之类的了。

    2022-12-22归属地:湖南
    7
    1
  • 就是那个刘涛
    请教老师一个问题: 异步操作的时候,业务子线程没有执行完毕之前,dubbo主线程是不是暂停等待子线程的结果呢?如果实在这样的话,主线程不还是被占用着吗?

    作者回复: 你好,举个简单的例子:你通过 nio 写一个主线程接收,子线程处理并 write 响应,你可以看看,主线程是不是在等待子线程。 你把通信收发想象为400米接力赛,接力棒比喻为通信句柄,只要拿着接力棒就能说话,就能写数据,写完扔了释放就完事了。

    2023-08-29归属地:河南
  • 驽马一二三四五六七八九十驾
    老师,请问一下,在HeaderExchangeHandler#handleRequest中为future设置了一个异步回调方法,当线程异步执行完成后,是Dubbo的内部线程去执行这个异步回调方法么?与Dubbo处理请求的线程是属于同一个线程池的么?

    作者回复: 你好,驽马一二三四五六七:感知到消息进来的是 netty 线程池,处理业务逻辑的,可以是 dubbo 业务线程池,也可以是 netty 自带的线程池。至于你说的问题,需要考虑使用的是什么Dispatcher类型。

    2023-08-02归属地:福建
收起评论
显示
设置
留言
23
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部