Dubbo源码深度解析(四)

         接上篇博客《Dubbo源码深度解析(三)》,上篇博文,主要讲的是DubboBootstrap#start()方法中调用到的其他方法,以及讲到ServiceConfig#export()方法的调用链路。其中讲到最核心的方法为ServiceConfig#doExportUrlsFor1Protocol(),还没讲完,今天接着讲,上篇讲到了这里:

361441beebec41b8a9e36f381424c991.png

        看看PROXY_FACTORY#getInvoker()方法,发现ProxyFactory也是由ExtensionLoader加载的,代码如下:

f8620f0056754ac7977c137329ba5cdb.png

e7b8014443084d9e8866d9176d25d607.png

9f672c3cbdeb4bb7ba3c6434efeadfa3.png

bca33932c1934cc591952d4019fd3ea5.png

       看看ProxyFactory接口,代码如下:

f5a914fb79134eb583435476f9df660d.png

        看看ProxyFactory的实现类,代码如下:

0c9272be5d42418497b0d5377fca4e1e.png

eb58394a04ab48b1b33f67737dc6adb6.png

f36f4e3aaae4431e988f7367eda469b6.png

        发现这三个实现类均没有被@Adaptive注解修饰,因此最终会调用到ExtensionLoader#createAdaptiveExtensionClass(),动态生成ProxyFactory接口实现的字节码对象,看看该方法:

ab6fff1b3d7c421e950adf178294b11c.png

        看看AdaptiveClassCodeGenerator#generate()方法,代码如下:

472b98a55e1547cdb71593012e91e150.png

        看看AdaptiveClassCodeGenerator#generateMethod()方法,代码如下:

06eb7d26d5804d84b67abc44f7876ccc.png

a392958827394047a5f2f0d36772725c.png        看看最后生成的ProxyFactory的实现类,实现类如下:

a9c4e2b2924a4b63b73ade08f2339289.png

        因此,调用PROXY_FACTORY#getInvoker()方法,实际上是调用ProxyFactory$Adaptive#getInvoker()方法,如果传入的url中,proxy属性(可以在@DubboService注解上指定)不为空,则获取属性值,如果为空,则用默认的值"javassist",最终调用ExtensionLoader#getExtensionLoader(ProxyFactory.class).getExtension("javassist")方法,获取具体的实现类,而名字叫"javassist"的ProxyFactory接口的实现类,是JavassistProxyFactory,最终调用的是JavassistProxyFactory#getInvoker()方法,代码如下:

14b0dcc637374dd9b900272b7b9e1a7b.png

        由上图可知,Wrapper#getWrapper()方法也很重要,看看该方法,代码如下:

98b933da5b744df78863430c3a08f253.png

a4b3dd02cc2d4342948e4f7d647f5551.png

        调用Wrapper#getWrapper()方法,传入的接口是HelloService.class,最终生成的返回的Wrapper对象实际上是继承了Wrapper(它是抽象类),Wrapper0,大概长这个样子,代码如下:

87a21629db7e4cfba2d7411f3ed430c0.png

        看看HelloService接口,代码如下:

95db7a72d518495bb609c94067707e50.png

        到这里很清晰了:调用Wrapper#getWrapper()方法,最终生成的是Wrapper类的实现类的对象,实现类的命名为:

8d8b0d6780c342a5b036be0150b6e8a7.png

        生成的实现类,最终会缓存到Wrapper的WRAPPER_MAP属性中,代码如下:

f2e4b31f5b0a4a169348060e3d032f2d.png

        最终调用JavassistProxyFactory#getInvoker()方法,返回的是AbstractProxyInvoker的实现类,代码如下:

39d222ae921645b7837c429b29756135.png

        再回到ServiceConfig#doExportUrlsFor1Protocol()方法中,调用PROXY_FACTORY.getInvoker()方法的地方,代码如下:

cc8dfe0a5bb246269a2b60dbf4676bab.png

        可以知道,返回的invoker对象是AbstractProxyInvoker的子类,又对invoker做了一层包装,即DelegateProviderMetaDataInvoker对象,调用其有参构造,传入invoker对象。再调用PROTOCOL#export()方法,传入DelegateProviderMetaDataInvoker对象,而PROTOCOL属性又和PROXY_FACTORY属性类似,如下:

0e80375485c744009ce3b0dff62f94c6.png

        看看Protocol接口,代码如下:

1d408a95566e4394aab9b78f0bf49a27.png

       发现Protocol接口的子类也都没有被@Adaptive注解修饰,因此也会动态生成Protocol接口的实现类,代码如下(截取部分):

c66ea7815fd64133a90bba75a53e0d88.png

        最终也是寻找一个name叫"registry"的Protocol接口的实现类,看看org.apache.dubbo.rpc.Protocol文件的内容,可知实现类是InterfaceCompatibleRegistryProtocol

7cd2a76e89fc483cadf62d84903dfc77.png这个说法对,但不全对,最终肯定InterfaceCompatibleRegistryProtocol,还记得我在《Dubbo源码深度解析(二)》中讲Dubbo的SPI机制吗?里面提到了包装类型,这里就用到了,先看看Protocol接口的的实现类,如下:

4bfc32687d904c81ae4745b7edd5d23e.png

        看看其中一个包装类,如ProtocolFilterWrapper,代码如下:

1097f144bc0847ba958259426954d4f6.png

        再回过头看看Protocol$Adaptive#export()方法,最终调用的是 Protocol extension = (Protocol) ExtensionLoader#getExtensionLoader(Protocol.class).getExtension("registry")方法,代码如下:

        上图中的instance对象肯定是InterfaceCompatibleRegistryProtocol对象,但是还调用了ExtensionLoader#injectExtension()方法,并传入了instance对象,实际上是给instance对象进行属性注入,看看该方法,代码如下:

        再看看排序,即WrapperComparator.COMPARATOR类,代码如下:

6bc97d1fed6441cf85af148eb1036bed.png

        可只是按照正序排列,也就是order越小,在越靠前,解析到了两个包装类,分别是ProtocolFilterWrapper和ProtocolListenerWrapper,代码如下:

89dd60ea1fe94b11a1e5672903150d15.png

9d2a95519b3040ccbe27d0e2c7f3f4e7.png

        因此,缓存的包装类的顺序也是ProtocolFilterWrapper、ProtocolListenerWrapper,但是又调用了Collections.reverse()方法,因此最终的顺序是:ProtocolListenerWrapper、ProtocolFilterWrapper(先顺序排列再反转,相当于直接是倒序排),再进行循环,代码如下:

3c28496891b8406c939cea696dafadae.png

因此ProtocolFilterWrapper对像的protocol属性是ProtocolListenerWrapper对象,ProtocolListenerWrapper对象的protocol属性是InterfaceCompatibleRegistryProtocol对象,而InterfaceCompatibleRegistryProtocol对象的protocol属性是Protocol$Adaptive,打断点验证如下:

121e88230cfb4063825c919d5c4bcb9f.png

71350bdbddc9469387ba6995ec29f6a7.png

        因此,最终调用的是ProtocolFilterWrapper#export()方法,代码如下:

098267ea7f914c58ac50cd7300d12db1.png

eb95db613dd0475380c7ab7a93de9fc6.png

        看看RegistryProtocol#doLocalExport()方法,代码如下:

        而这里传入的参数protocol属性在前面说过,实际上是Protocol$Adaptive对象,相当于调用的又是Protocol$Adaptive#export()方法,但是此时传入的url为 providerUrl,跟之前的不太一样,由上面打断点可知,providerUrl的protocol属性为"dubbo",因此最终得到的结果对象为:ProtocolFilterWrapper对像,而ProtocolFilterWrapper对像的protocol属性是ProtocolListenerWrapper对象,ProtocolListenerWrapper对象的protocol属性是DubboProtocol,打断点验证,结果如下:

        前面两个对象的export()方法就不看了,直接看DubboProtocol#export()方法,代码如下:

        可知最终创建服务器是通过Exchangers#bind()方法,传入url和requestHandler,而requestHandler则是DubboProtocol的内部类对象,截取部分代码如下:

        看看Exchangers#bind()方法,代码如下:

        这里又涉及到SPI,默认的实现类名为"header",这里就不在多过解释,实现类就是HeaderExchanger。最终,调用的是HeaderExchanger#bind()方法,代码如下:

        看看Transporters#bind()方法,代码如下:

        又涉及到SPI机制,看看Transporter接口,代码如下:

        因此,直接看NettyTransporter#bind()方法,代码如下:

        上图中传入的handler属性是DecodeHandler对象,DecodeHandler对象的handler属性为HeaderExchangeHandler对象,而HeaderExchangeHandler对象的handler属性为DubboProtocol#ExchangeHandlerAdapter对象,断点验证,结果如下:

        看看父类的有参构造方法,涉及到赋初始值或者默认值,代码如下:

        再看看AbstractServer#doOpen()方法,代码如下:

        重点看看NettyServer#doOpen()方法,代码如下:

        其中,bootstrap.bind()方法是绑定ip/port,channelFuture.syncUninterruptibly()方法一个同步方法,阻塞主线程,避免程序跑完直接就结束了。

        这块就是Netty Server启动的核心,也是固定写法,任何一个框架,使用Netty作为其通讯框架,大概都是这样写,没什么好讲的,如果对 Netty框架感兴趣,不妨看我写的这篇博客《Netty源码深度解析》,里面对Netty原理有详细的介绍。我假设你对Netty比较熟,看到上面这块代码,我相信你会毫不犹豫的看这个类:NettyServerHandler,它是处理客户端的连接、请求、响应、断开连接的核心类。因此直接看这个类即可。

        ① 如果此时有一个客户端发起了连接,连接成功,调用NettyServerHandler#channelActive()方法;

        ② 客户端发来请求,调用NettyServerHandler#channelRead()方法;

        ③ 客户端断开连接,调用NettyServerHandler#channelInactive()方法;

        ④ 向客户端响应,调用NettyServerHandler#write()方法;

        重点②④,先看看NettyServerHandler#channelRead()方法,代码如下:

        到这里,其实我也不太确定应该是走if逻辑,还是else逻辑,主要是 request.isTwoWay() 不太懂。其实客户端发过来的请求,不是先到NettyServerHandler,之前不是提到过编码、解码吗?如果是客户端发过来的请求,在客户端放已经对请求做过编码,因此服务端接收情请求,需要进行解码。而且客户端发过来的数据,肯定也是二进制的,因此需要处理,因为到NettyServerHandler这边,接收到的,就变成了 Object message,而不是byte[] message,更加可以确定解码类是将接收到的二进制数据转成了 Object message,并且可能还会涉及到粘包拆包等,OK,看看Dubbo是怎么做的,直接看InternalDecoder#decode()方法,代码如下:

        codec属性实际上是在这里赋值的,如下:

        可以知道,最终调用到DubboCodec#decode()方法,实际上DubboCodec没有decode()方法,而是在其父类中实现的,即调用ExchangeCodec#decode()方法,代码如下:

        这里可以打断点看看,结果如下:

        很明显,这里的Request的 twoWay属性是在客户端设置的,并放在了请求头中,如果远程调用需要服务端响应结果则为true;不需要响应则为 false。而Request的 mData属性为DecodeableRpcInvocation对象。再回到HeaderExchangeHandler#received()方法,代码如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397766.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CentOS7 配置 nginx 和 php 方案

配置方案 一、安装软件二、编写配置文件,连接PHP三、引用文件四、测试 鉴于网上教程错综复杂,写下一这篇文章 本教程只需要三步即可 一、安装软件 yum install -y nginx php php-fpm二、编写配置文件,连接PHP 一般情况下在安装完 nginx 后…

python-质因数分解(赛氪OJ)

[题目描述] 已知正整数 n 是两个不同的质数的乘积,试求出两者中较大的那个质数。输入格式: 输入一个正整数 n。输出格式: 输出一个正整数 p,即较大的那个质数。样例 #1样例输入 #1 21样例输出 #1 7提示: 1≤n≤2109 来…

无字母数字的绕过方法

php代码 <?phpif(isset($_GET[code])){$code $_GET[code];if(strlen($code)>35){die("Long.");}if(preg_match("/[A-Za-z0-9_$]/",$code)){die("NO.");}eval($code);}else{highlight_file(__FILE__);} 题目的限制&#xff1a; webshell…

书籍分享:【矩阵力量】豆瓣评分高达9.6,看完感叹《矩阵论》又白学了

书籍分享&#xff1a;【矩阵力量】豆瓣评分高达9.6&#xff0c;看完感叹《矩阵论》又白学了 《矩阵力量》简要介绍书籍下载链接 《矩阵力量》简要介绍 《矩阵力量》是姜伟生精心编写的线性代数的深度理解之作&#xff0c;作者将抽象的线性代数概念用通俗易懂的语言和大量生动形…

Windows下,C# 通过FastDDS高效通信

目录 1、安装FastDDS 库2、使用IDL定义自己的数据格式3、生成DLL3.1 托管 &#xff08;Managed&#xff09;模式3.2 非托管 &#xff08;Unmanaged&#xff09;模式 -- 可用于Unity 代码示例 eprosima Fast DDS is a C implementation of the DDS (Data Distribution Service) …

vscode用快捷键一键生成vue模板

项目中有些代码模块是固定的&#xff0c;如下面的代码所示&#xff0c;为了不重复写这些相同的代码&#xff0c;我们可以使用快键键一键生成模板。 流程&#xff1a; 中文&#xff1a;首选项-> 用户代码片段 -> 输入框中输入vue,找到vue.json文件&#xff08;没有vue.j…

Vue-07.生命周期

生命周期&#xff1a; 生命周期&#xff1a;指一个对象从创建到销毁的全过程 生命周期的八个阶段&#xff1a;每触发一个阶段&#xff0c;就会自动执行一个生命周期方法&#xff08;钩子方法&#xff09; 状态 阶段周期 beforeCreated 创…

内部排序(插入、交换、选择)

一、排序的部分基本概念 1. 算法的稳定性 若待排序表中有两个元素 Ri 和 Rj &#xff0c;其对应的关键字相同即 keyi keyj&#xff0c;且在排序前 Ri 在 Rj 的前面&#xff0c;若使用某一排序算法排序后&#xff0c;Ri 仍然在 Rj 的前面&#xff0c;则称这个排序算法是稳定的…

【MySQL】详解数据库约束、聚合查询和联合查询

数据库约束 约束类型 数据库的约束类型主要包括以下几种&#xff1a; 主键约束&#xff08;Primary Key Constraint&#xff09;&#xff1a;确保表中的每一行都有唯一的标识&#xff0c;且不能为NULL。 外键约束&#xff08;Foreign Key Constraint&#xff09;&#xff1a…

5.ADC(模拟信号转数字信号)

理论 3个ADC控制器 转换&#xff1a;单次转换模式、 连续转换模式 转换时间 采样时间 12.5周期 当ADCCLK(时钟) 14MHz&#xff0c;采样时间为1.5周期&#xff0c;TcoNv(转换时间) 1.5 12.5 14 周期 1us 采样精度&#xff1a;12位/16位(212 4096) 实际电压值 (通道采…

Java面试题--JVM大厂篇之破解 JVM 性能瓶颈:实战优化策略大全

目录 引言: 正文: 1. 常见的JVM性能问题 频繁的GC导致应用暂停 内存泄漏导致的内存不足 线程争用导致的CPU利用率过高 类加载问题导致的启动时间过长 2. 优化策略大全 2.1 代码层面的优化 2.1.1 避免不必要的对象创建 2.1.2 优化数据结构的选择 2.1.3 使用并发工具…

Python爬虫:下载4K壁纸

&#x1f381;&#x1f381;创作不易&#xff0c;关注作者不迷路&#x1f380;&#x1f380; 目录 &#x1f338;完整代码 &#x1f338;分析 &#x1f381;基本思路 &#x1f381;需要的库 &#x1f381;提取图片的链接和标题 &#x1f453;寻找Cookie和User-Agent &…

突破•指针六

听说这是目录哦 数组和指针笔试题解析&#x1fae7;一维数组1&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355;&#x1f355; 字符数组1&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;&#x1f354;2&#…

PCL 采样一致性模型介绍

采样一致性可以简单高效的检测出一些具有数学表达式的目标模型。PCL中的sample consensus模块中不仅包含各种的采样一致性估计方法,也包含一些已经编写好的数学模型,下面主要介绍一下PCL中的采样一致性模型。 1. 二维圆模型 pcl::SampleConsensusModelCircle2D< PointT …

AI学习记录 - 自注意力机制的计算流程图

画图不易&#xff0c;如果你从这个图当中得到灵感&#xff0c;大佬赏个赞 过段时间解释一下&#xff0c;为啥这样子计算&#xff0c;研究这个自注意力花了不少时间&#xff0c;网上很多讲概念&#xff0c;但是没有具体的流程图和计算方式总结…

Win11表情符号输入详细教程,一学就会!

在Win11电脑操作中&#xff0c;用户可以根据自己的需求&#xff0c;点击输入想要的表情符合。但许多新手用户不知道怎么操作才能输入&#xff1f;这时候用户按下快捷键&#xff0c;快速打开表情符号选择界面&#xff0c;然后选择需要的表情符号点击输入即可。以下系统之家小编给…

Can GPT-3 Perform Statutory Reasoning?

文章目录 题目摘要相关工作SARAGPT-3 对美国法典的了解GPT-3 在对合成法规进行简单推理时遇到困难结论 题目 GPT-3 可以进行法定推理吗&#xff1f; 论文地址&#xff1a;https://arxiv.org/abs/2302.06100 摘要 法定推理是用事实和法规进行推理的任务&#xff0c;法规是立法机…

Linux嵌入式学习——C++学习(2)

一、标识符的作用域和可见性 &#xff08;一&#xff09;作用域 1、全局作用域 在函数外部声明的变量和函数具有全局作用域。这些变量和函数在程序的任何地方都可以被访问。 2.局部作用域 在函数内部、循环体内部或条件语句内部声明的变量具有局部作用域。这些变量只能在其…

X射线物质质量衰减系数的查询计算方法

最近进行硕士毕业课题&#xff0c;需要各种各样物质的质量衰减系数&#xff08;线性衰减系数&#xff09;&#xff0c;包括高原子序数的金属物质还有一些复杂的化合物或者混合物&#xff0c;之前知道美国的XCOM &#xff1a;XCOM: Photon Cross Sections Database这个数据库可以…

仓颉语言运行时轻量化实践

杨勇勇 华为语言虚拟机实验室架构师&#xff0c;目前负责仓颉语言静态后端的开发工作 仓颉语言运行时轻量化实践 仓颉Native后端&#xff08;CJNative&#xff09;是仓颉语言的高性能、轻量化实现。这里的“轻量化”意指仓颉程序运行过程中占用系统资源&#xff08;内存、CPU等…