Dubbo源码深度解析(四)

news2024/11/14 1:16:15

         接上篇博客《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对象,断点验证,结果如下:

        在有参构造中,又对handler做了包装,如下:

        这里,又涉及到了SPI,也就是找Dispatcher接口的实现类,看看这接口,代码如下:

        因此可以找到一个实现类,即AllDispatcher,因此,最终的handler为:MultiMessageHandler对象,MultiMessageHandler对象的andler属性为HeartbeatHandler对象,而HeartbeatHandler对象的handler属性为AllDispatcher对象,而AllDispatcher对象的handler属性为DecodeHandler对象,而DecodeHandler对象的handler属性为HeaderExchangeHandler对象,而HeaderExchangeHandler对象的handler为DubboProtocol.ExchangeHandlerAdapter对象(内部类)。断点验证一下,结果如下:

        (不得不吐槽一下,Dubbo层层包装、大量代理的使用、动态生成字节码、以及基于URL的驱动,各种改URL的属性,比如protocol属性(即协议)等,是真的复杂!这些细节,全网有几个人讲得比我清楚、仔细呢?我这么说,应该没人反对吧😊😊😊)

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

        再看看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()方法,代码如下:

        到这里,需要看看DecodeHandler#decode()方法,但是在将这个方法之前,需要先看看编解码。之前不是提到过编码、解码吗?如果是客户端发过来的请求,在客户端放已经对请求做过编码,因此服务端接收情请求,需要进行解码。而且客户端发过来的数据,肯定也是二进制的,因此需要处理,因为到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()方法,代码如下:

        到这里为止,Dubbo的解码就讲完了,再回到DecodeHandler#decode()方法,代码如下:

        再看看DecodeableRpcInvocation#decode()方法,代码如下:

        打断点看看,结果如下:

        再回到DecodeableRpcInvocation#received()方法,继续往下看,代码如下:

        重点看看DubboProtocol.ExchangeHandlerAdapter#reply()方法,代码如下:

        看看DubboProtocol#getInvoker()方法,是如何获取Invoker对象的,代码如下:

        那DubboExporter对象是什么放入DubboProtocol的exporterMap属性中的呢?实际上是在这里,代码如下:

        是不是讲到现在,很多断断续续的东西就慢慢接上了呢?这就是读源码的乐趣,希望我写的博客,能给各位看官带来一点点思考和收获😊。下一篇博客地址为:《Dubbo源码深度解析(五)​​​​​​​》

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LVS详细解析及其NAT模式与DR模式部署(理论+实验全方位指导)

目录 1. 集群 2. 分布式系统 3. 集群与分布式的比较 4.通俗的解释 集群 分布式系统 总结 LVS(Linux Virtual Server)简介 LVS专业术语 工作原理 LVS调度算法 静态调度算法 动态调度算法 ipvsadm脑图 NAT模式集群 LVS的配置 在LVS主机内打开…

数据结构之顺序表的实现

主要参考: 【王道考研】王道数据结构与算法详细笔记(全)_王道数据结构笔记-CSDN博客 顺序表的概念 顺序表:用顺序存储的方式实现线性表顺序存储。把逻辑上相邻的元素存储在物理位置上也相邻的存储单元中,元素之间的关系…

1Panel配置

1. 脚本安装 curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sh quick_start.sh2. 配置镜像加速 在容器-> 配置中设置,否则安装软件会失败。 https://docker.211678.top https://docker.1panel.live …

面对复杂订单统计,如何用这款工具化整为零?

最近制作报表时遇到了个问题,怎么把整合到一起的订单统计数据拆分成一个个单独的订单统计报表呢?经过一段时间的探索后,我找到了一款完全免费的报表软件,能够解决这个问题,这款软件叫作山海鲸可视化,我会在…

python 已知x+y=8 求x*y*(x-y)的最大值

先用导数求解 已知xy8 求xy(x-y)的最大值 令y8-x 则 f(x)x⋅(8−x)⋅(x−(8−x))x⋅(8−x)⋅(2x−8) 导数方程为 f(x)-3x^2 24x - 32 求方程 − 3 x 2 24 x − 32 0 -3x^2 24x - 32 0 −3x224x−320 的根。 首先,我们可以尝试对方程进行因式分解。观察…

Maven系列(一):Maven下载安装配置【Maven使Java构建事半功倍】

前言 ​ 作为Java开发工程师,那么Maven现已成为不可或缺的一部分,从最开始的依赖管理到编译运行及打包,可以说使伴随了Java项目的整个生命周期。那么这篇文章,将带你去认识Maven以及Maven的下载、安装、配置等等。 1. 什么是Mav…

RCE复现问题和研究

目录 先了解一些常见的知识点 PHP常见命令执行函数 call_user_func eval() call_user_func_array array_filter 实战演练(RCE)PHP Eval函数参数限制在16个字符的情况下 ,如何拿到Webshell? 1、长度…

【智能控制】第九,十章 一阶和二阶系统神经网络控制,输出受限系统和输入受限系统的神经网络控制(北京航天航空大学)

目录 一阶系统神经网络控制 1. 系统描述 2. 滑模控制器设计(f(x)已知) 3. 自适应神经网络控制(f(x)未知) 4. 仿真结果 二阶系统神经网络控制 1. 系统描述 2. 神经网络控制器​编辑​编辑​编辑​编辑​编辑 3. 仿真结果 输出受限系统的神经网络控制 1. 问题描述 2.…

认识一下测试策略与测试方案

目录 测试方案 测试策略 测试策略的内容主要包括 测试技术和工具 测试启动、停止和完成标准 风险分析和应对方案 测试范围 测试角色和职责 测试方法和类型 测试工具 测试层级 测试指标 测试可交付成果 测试方案的内容包括 测试目标 测试范围 测试环境 测试策略…

Linux云计算 |【第二阶段】OPERATION-DAY2

主要内容: 部署LNMP架构(linux、Nginx、MariaDB、PHP)、NginxFastCGI动静分离、Nginx地址重写 环境准备:还原配置文件 [rootproxy ~]# cd /usr/local/nginx/ [rootproxy nginx]# sbin/nginx [rootproxy nginx]# cp conf/nginx.c…

服务器CPU天梯图2024年8月,含EYPC/至强及E3/E5

原文地址&#xff08;高清无水印原图/持续更新/含榜单出处链接&#xff09;&#xff1a; >>>服务器CPU天梯图<<< 本文提供的服务器CPU天梯图数据均采集自各大专业网站&#xff0c;榜单图片末尾会标准其来源&#xff08;挂太多链接有概率会被ban&#xff0c;…

2024杭电多校3——1007单峰数列

补题链接 一道数据结构体&#xff0c;差分线段树&#xff0c;我从没有看见过的全新版本&#xff0c;不过据说挺常见的。线段树维护题目里询问的东西&#xff0c;是否一样&#xff0c;单调还有单峰&#xff0c;小细节挺多的。建线段树开始是从2开始的&#xff0c;因为差分的第一…

Java:实现杨辉三角

目录 思路 完整代码 思路 可以把杨辉三角看作是一个直角三角形&#xff0c;这样可以方便进行输出 以直角三角形中 i行j列 的3 为例&#xff0c;我们可以推出: [ i ] [ j ] [ i -1] [ j ] [ i - 1] [ j -1] 。 同时&#xff0c;杨辉三角也可以看作是一个二维数组&#x…

Tomcat服务器和Web项目的部署

目录 一、概述和作用 二、安装 1.进入官网 2.Download下面选择想要下载的版本 3.点击Which version查看版本所需要的JRE版本 4.返回上一页下载和电脑和操作系统匹配的Tomcat 5. 安装完成后&#xff0c;点击bin目录下的startup.bat&#xff08;linux系统下就运行startup.sh&…

Midjourney技巧-生成拟人化动物(做你的品牌形象代言人)

​ 前言/introduction 如何用Midjourney生成这种时尚潮流的动物拟人图片&#xff1f; 用来做你的品牌形象代言人也是不赖&#xff01; ​ 关键词/personify 生成这种动物拟人化的图片其实主要在关键词-拟人化personify 比如&#xff0c;一只拟人化的老鼠/An anthropomorphic…

惊!北京三害、上海四毒、广东五虎,谁才是互联网界的“拼命三郎”?

在互联网江湖中&#xff0c;流传着这样一份神秘的“工作强度排名”&#xff0c;它们以地域为划分&#xff0c;将那些被公认为加班狂魔的互联网大厂归为了“北京三害”、“上海四毒”和“广东五虎”。 这份名单一出&#xff0c;让人不禁哑然失笑&#xff0c;同时也让人深思&…

【系统架构设计】计算机网络

【系统架构设计】计算机网络 网络架构与协议网络互联模型OSI/RM 结构模型TCP/IP结构模型IPv6 局域网与广域网局域网特点 网络互连与常用设备网络互联设备交换技术路由技术 网络存储技术 网络架构与协议 网络互联模型 OSI/RM 结构模型 1977年&#xff0c;国际标准化组织为适应…

framebuffer屏幕显示接口(linux)

framebuffer &#xff1a; 帧缓存、帧缓冲技术 Linux内核为应用提供的一套显示相关接口 流程&#xff1a; 1. 打开显示设备&#xff08;/dev/fb0&#xff09; 2. 获取显示设备相关参数 3. 建立内存映射 4. 写入RGB颜色值 5. 解除映射 6. 关闭显示…

【深度学习】什么是自回归模型?

文章目录 自回归模型的数学表达自回归模型的特点示例自回归模型在机器学习中的应用总结扩散模型与自回归模型的区别扩散模型在TTS(文本转语音)中的应用总结自回归模型(Autoregressive Model, AR Model)是一种用于时间序列分析和生成的统计模型。它的核心思想是预测变量(通…

Java:成员内部类

文章目录 匿名内部类代码讲解new 类new接口 特点&#xff1a;作用&#xff1a;在开发中的应用场景&#xff1a; 成员内部类&#xff08;了解&#xff09;静态内部类&#xff08;了解&#xff09;局部内部类&#xff08;了解&#xff09; 匿名内部类 一种特殊的局部内部类&…