前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >我是一个Dubbo数据包...

我是一个Dubbo数据包...

原创
作者头像
龟仙老人
发布于 2022-06-08 11:33:41
发布于 2022-06-08 11:33:41
3120
举报
文章被收录于专栏:捉虫大师捉虫大师

hello,大家好呀,我是小楼!

今天给大家带来一篇关于Dubbo IO交互的文章,本文是一位同事写的文章,用有趣的文字把枯燥的知识点写出来,通俗易懂,非常有意思,所以迫不及待找作者授权然后分享给大家:


一些有趣的问题

Dubbo是一个优秀的RPC框架,其中有错综复杂复杂的线程模型,本篇文章笔者从自己浅薄的认知中,来剖析Dubbo的整个IO过程。在开始之前,我们先来看如下几个问题:

  • 业务方法执行之后,数据包就发出去了吗?
  • netty3和netty4在线程模型上有什么区别?
  • 数据包到了操作系统socket buffer,经历了什么?
  • Provider打出的log耗时很小,而Consumer端却超时了,怎么可以排查到问题?
  • 数据包在物理层是一根管道就直接发过去吗?
  • Consumer 业务线程await在Condition上,在哪个时机被唤醒?
  • ……

接下来笔者将用Dubbo2.5.3 作为Consumer,2.7.3作为Provider来讲述整个交互过程,笔者站在数据包视角,用第一人称来讲述,系好安全带,我们出发咯。

有意思的旅行

1、Dubbo2.5.3 Consumer端发起请求

我是一个数据包,出生在一个叫Dubbo2.5.3 Consumer的小镇,我的使命是是传递信息,同时也喜欢出门旅行。

某一天,我即将被发送出去,据说是要去一个叫Dubbo 2.7.3 Provider的地方。

这一天,业务线程发起发起方法调用,在FailoverClusterInvoker#doInvoke我选择了一个Provider,然后经过各种Consumer Filter,再经过Netty3的pipeline,最后通过NioWorker#scheduleWriteIfNecessary方法,我来到了NioWorker的writeTaskQueue队列中。

当我回头看主线程时,发现他在DefaultFuture中的Condition等待,我不知道他在等什么,也不知道他要等多久。

我在writeTaskQueue队列排了一会队,看到netty3 IO worker线程在永不停歇的执行run方法,大家都称这个为死循环。

最后,我很幸运,NioWorker#processWriteTaskQueue选择了我,我被写到操作系统的Socket缓冲区,我在缓冲区等待,反正时间充足,我回味一下今天的旅行,期间我辗转了两个旅行团,分别叫主线程和netty3 IO worker线程,嗯,两个旅行团服务都不错,效率很高。

索性我把今天的见闻记录下来,绘制成一张图,当然不重要的地方我就忽略了。

1.png
1.png
2、操作系统发送数据包

我在操作系统socket缓冲区,经过了很多神奇的事情。

  1. 在一个叫传输层的地方给我追加上了目标端口号、源端口号
  2. 在一个叫网络层的地方给我追加上了目标IP、源IP,同时通过目标IP与掩码做与运算,找到“下一跳”的IP
  3. 在一个叫数据链路层的地方通过ARP协议给我追加上了“下一跳”的目标MAC地址、源MAC地址

最有意思的是,我们坐的都是一段一段缆车,每换一个缆车,就要修改目标MAC地址、源MAC地址,后来问了同行的数据包小伙伴,这个模式叫“下一跳”,一跳一跳的跳过去。这里有很多数据包,体型大的单独一个缆车,体型小的几个挤一个缆车,还有一个可怕的事情,体型再大一点,要分拆做多个缆车(虽然这对我们数据包没啥问题),这个叫拆包和粘包。期间我们经过交换机、路由器,这些地方玩起来很Happy。

当然也有不愉快的事情,就是拥堵,目的地缆车满了,来不及被拉走,只能等待咯。

3、在Provider端的经历

好不容易,我来到了目的地,我坐上了一个叫“零拷贝”号的快艇,迅速到了netty4,netty4果然富丽堂皇,经过NioEventLoop#processSelectedKeys,再经过pipeline中的各种入站handler,我来到了AllChannelHandler的线程池,当然我有很多选择,但是我随便选了一个目的地,这里会经历解码、一系列的Filter,才会来的目的地“业务方法”,NettyCodecAdapter#InternalDecoder解码器很厉害,他可以处理拆包和粘包。

2.png
2.png

在AllChannelHandler的线程池中我会停留一会,于是我也画了一张图,记录旅程。

3.png
3.png

自此,我的旅行结束,新的故事将由新的数据包续写。

4、Provider端产生了新的数据包

我是一个数据包,出生在一个叫Dubbo2.7.3 Provider的小镇,我的使命是去唤醒命中注定的线程,接下来我会开始一段旅行,去一个叫Dubbo2.5.3 Consumer的地方。

在Provider业务方法执行之后

  • 由业务线程经过io.netty.channel.AbstractChannelHandlerContext#writeAndFlush
  • 再经过io.netty.util.concurrent.SingleThreadEventExecutor#execute 执行addTask
  • 将任务放入队列io.netty.util.concurrent.SingleThreadEventExecutor#taskQueue
  • 我便跟随着io.netty.channel.AbstractChannelHandlerContext$WriteTask等待NioEventLoop发车,等待的过程中,我记录了走过的脚步。
4.png
4.png

在这里,我看到NioEventLoop是一个死循环,不停地从任务队列取任务,执行任务AbstractChannelHandlerContext.WriteAndFlushTask,然后指引我们到socket缓冲区等候,永不知疲倦,我似乎领略到他身上有一种倔强的、追求极致的匠人精神。

经过io.netty.channel.AbstractChannel.AbstractUnsafe#write,我到达了操作系统socket缓冲区。在操作系统层面和大多数数据包一样,也是做缆车达到目的地。

5、到达dubbo 2.5.3 Consumer端

到达dubbo 2.5.3 Consumer端,我在操作系统socket缓冲区等了一会,同样是坐了“零拷贝”号快艇,到达了真正的目的地dubbo 2.5.3 Consumer,在这里我发现,NioWorker#run是一个死循环,然后执行NioWorker#processSelectedKeys,通过NioWorker#read方式读出来,我就到达了AllChannelHandler的线程池,这是一个业务线程池。

我在这里等待一会,等任务被调度,我看见com.alibaba.dubbo.remoting.exchange.support.DefaultFuture#doReceived被执行了,同时Condition的signal被执行了。我在远处看到了一个被阻塞线程被唤醒,我似乎明白,因为我的到来,唤醒了一个沉睡的线程,我想这应该是我生命的意义。

至此,我的使命也完成了,本次旅程结束。

总结netty3和netty4的线程模型

我们根据两个数据包的自述,来总结一下netty3和netty4的线程模型。

1、netty3写过程
5.png
5.png
2、Netty4的读写过程
6.png
6.png

说明:这里没有netty3的读过程,netty3读过程和netty4相同,pipeline是由IO线程执行。

总结:netty3与netty4线程模型的区别在于写过程,netty3中pipeline由业务线程执行,而netty4无论读写,pipeline统一由IO线程执行。

netty4中ChannelPipeline中的Handler链统一由I/O线程串行调度,无论是读还是写操作,netty3中的write操作时由业务线程处理Handler链。netty4中可以降低线程之间的上下文切换带来的时间消耗,但是netty3中业务线程可以并发执行Handler链。如果有一些耗时的Handler操作会导致netty4的效率低下,但是可以考虑将这些耗时操作放在业务线程最先执行,不放在Handler里处理。由于业务线程可以并发执行,同样也可以提高效率。

一些疑难问题排查

有遇到一些比较典型的疑难问题,例如当Provider答应的didi.log耗时正常,而Consumer端超时了,此时有如下排查方向,didi.log的Filter其实处于非常里层,往往不能反映真实的业务方法执行情况。

  1. Provider除了业务方向执行外,序列化也有可能是耗时的,所以可以用arthas监控最外侧方法org.apache.dubbo.remoting.transport.DecodeHandler#received,排除业务方法耗时高的问题
  2. Provider中数据包写入是否耗时,监控io.netty.channel.AbstractChannelHandlerContext#invokeWrite方法
  3. 通过netstat 也能查看当前tcp socket的一些信息,比如Recv-Q, Send-Q,Recv-Q是已经到了接受缓冲区,但是还没被应用代码读走的数据。Send-Q是已经到了发送缓冲区,但是对方还没有回复Ack的数据。这两种数据正常一般不会堆积,如果堆积了,可能就有问题了。
7.png
7.png
  1. 看Consumer NioWorker#processSelectedKeys (dubbo2.5.3)方法是否耗时高。
  2. 直到最终整个链路的所有细节……问题肯定是可以解决的。

尾声

在整个交互过程中,笔者省略线程栈调用的一些细节和源代码的细节,例如序列化与反序列化,dubbo怎么读出完整的数据包的,业务方法执行前那些Filter是怎么排序和分布的,netty的Reactor模式是如何实现的。这些都是非常有趣的问题……

本文已收录 https://github.com/lkxiaolou/lkxiaolou 欢迎star。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
突发流量引发的Dubbo拥堵,该怎么办?
来源 | https://blog.51cto.com/nxlhero/2515849
java进阶架构师
2020/11/13
1K0
突发流量引发的Dubbo拥堵,该怎么办?
dubbo provider是如何启动的
Netty支持多种服务端的server实例,包括mina、netty等,如下所示:
luoxn28
2020/07/14
4030
源码分析Dubbo事件派发机制
从官网我们得知,Dubbo协议是使用单一长连接来进行网络传输,也就是说服务调用方持久与服务提供者建立一条连接,所有的服务调用调用信息通过。 一条TCP连接进行传输,在网络层至少要考虑如下问题:
丁威
2019/06/10
9840
换个角度聊聊Netty
Netty3出现了太多的内存垃圾,创建了过多对象,在大的服务端压力下会表现比较糟糕,做了太多的内存拷贝,在堆上创建对象,堆缓冲区,当往socket写内容时就需要做内存拷贝,拷贝到直接内存,然后交给socket所以做了太多内存拷贝。
春哥大魔王
2019/11/09
8910
换个角度聊聊Netty
新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
Netty 是一个广受欢迎的异步事件驱动的Java开源网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。
JackJiang
2018/11/05
7.2K0
[自己做个游戏服务器二] 游戏服务器的基石-Netty全解析
Netty的大名我想做java 的基本都知道,因为他实在太出名了,现在很多著名的软件都是使用netty作为通讯基础,今天就聊聊Netty,希望能讲清楚,如果懒得看理论,可以直接拉到后面看Hello world。把代码抄下来,运行一下看看。
香菜聊游戏
2021/10/19
1.8K0
[自己做个游戏服务器二] 游戏服务器的基石-Netty全解析
微服务架构之服务冶理Dubbo-Netty流程
服务引用时流程会走到DubboProtocol#refer方法,之前篇章中没有提及Netty环节,本节补上
公众号_松华说
2019/07/16
1K0
微服务架构之服务冶理Dubbo-Netty流程
Dubbo技术知识总结之五——Dubbo远程调用
上篇《Dubbo技术知识总结之四——Dubbo集群容错》的七个步骤中,前四个步骤是 Cluster 层的工作。远程调用是后续步骤 5, 6, 7 的内容,同时也是 Cluster 层以下的工作。该部分对 Dubbo 远程调用的基础与实现进行总结,包括 Dubbo 协议,编解码器,Dubbo 线程模型。
剑影啸清寒
2020/07/09
1.3K0
理解Netty
有了Netty,你可以实现自己的HTTP服务器,FTP服务器,UDP服务器,RPC服务器,WebSocket服务器,Redis的Proxy服务器,MySQL的Proxy服务器等等。
周三不加班
2019/09/03
7540
理解Netty
netty3与netty4的区别
今天遇到一个人问我netty3与netty4有什么区别。因为我之前使用netty做过网络程序开发,心里还是有点谱的。很自然地就说到了一些主要区别 一些术语的变化,如Upstream变为了Inbound,Downstream变为了Outbound netty3对每个读或写的操作,还会额外创建一个新的ChannelBuffer对象,这带来了很大的GC压力,为了缓解频繁申请回收Buffer时的GC压力,引入了池化的ByteBufs,当然在使用完Buffer后要注意需使用BufUtil.release释放。 那人再
jeremyxu
2018/05/10
5.1K0
Netty 应用与原理
在 I/O 操作中有这么两组概念,其中同步/异步 要和线程中的同步线程/异步线程要区分开,这里指的是同步IO / 异步IO
浪漫主义狗
2024/04/27
2530
Netty 应用与原理
Netty[Netty从入门到精通]
应用层:Http协议、电子文件传输、文件服务器等 表示层:解决我们不同系统之间语法的通讯 会话层:建立与应用程序之间的通讯 传输层:提供了端口号和接口协议TPC/UDP
高大北
2022/09/02
1.1K0
Netty[Netty从入门到精通]
Netty网络编程第七卷
传统的RPC框架或者基于RMI等方式的远程服务(过程)调用采用了同步阻塞IO,当客户端的并发压力或者网络时延增大之后,同步阻塞IO会由于频繁的wait导致IO线程经常性的阻塞,由于线程无法高效的工作,IO处理能力自然下降。
大忽悠爱学习
2022/05/06
9870
Netty网络编程第七卷
深入Netty事件流程分析(上)
前面我们已经深入分析Netty中的核心组件,接下来我们开始来深入理解Netty各个组件处理事件的运作流程,通过事件流程的分析,我们可以思考Netty框架是如何设计组件之间的协作来配合完成基于Reactor模式且具备可伸缩性的Web服务,由于Netty事件流程比较多且杂,上篇主要分析事件轮询器初始化,启动类初始化组件以及服务端的端口绑定事件.
keithl
2020/05/07
1.2K0
Netty框架研究
起因 以前也用Netty做到异步网络编程,用过之后也一直没想过要把Netty拿起来重新研究一翻,直到上周工作中遇到一个棘手的问题。 在我们的项目中基于netty-socketio,我们实现了一个基于WebSocket的浏览器与服务端的请求回应机制。 这里贴一下该机制的大概的代码逻辑,真实项目比这复杂得多。 import com.corundumstudio.socketio.AckRequest; import com.corundumstudio.socketio.Configuration; impor
jeremyxu
2018/05/10
1.7K0
1万2千字长文助力春招 | Netty面试篇
Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。Netty是基于nio的,它封装了jdk的nio,让我们使用起来更加方法灵活。
王知无-import_bigdata
2020/03/04
6490
Netty 源码深度解析(九) - 编码概述1 抽象类 MessageToByteEncoder2 抽象类 MessageToMessageEncoder一个java对象最后是如何转变成字节流,写到s
编码器实现了ChannelOutboundHandler,并将出站数据从 一种格式转换为另一种格式,和我们方才学习的解码器的功能正好相反。Netty 提供了一组类, 用于帮助你编写具有以下功能的编码器:
JavaEdge
2018/12/19
1.6K0
Netty相关知识汇总
1)、TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
用户3467126
2019/10/08
9720
Netty相关知识汇总
netty在大厂内部的优化实践
PS:底层原理的优化,体现在对源码的理解,系统参数决定了你执行的情况,操作系统是对外的平台,平台慢了话,netty程序,java程序在快是没用的。系统的参数调整后,netty也需要进行优化,下一步就是JAVA程序调优。
IT架构圈
2021/02/01
2K0
netty在大厂内部的优化实践
Netty技术知识点总结
Netty 服务端与客户端都是由 Bootstrap 引导程序开始的,对于服务端,引导类是 ServerBootstrap,对于客户端,引导类是 Bootstrap。
剑影啸清寒
2020/07/10
9980
相关推荐
突发流量引发的Dubbo拥堵,该怎么办?
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档