| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> Dubbo流程及源码分析(四) -> 正文阅读 |
|
[网络协议]Dubbo流程及源码分析(四) |
????????扑街前言:本篇是dubbo的最后一篇文章了,在此对之前dubbo相关的文章做一个总结,第一篇是dubbo的SPI和Java的SPI,第二篇是Spring 集成dubbo和provider 方的启动,接着第三篇就是consumer 方的启动,那么本篇要讲的就是服务调用的全部流程。 InvokerInvocationHandler起始????????在上篇文章中我们知道了,consumer 放的启动就是为配置的接口拉取注册中心中的服务信息并生成代理,那么最后包装的代理invoker 就是InvokerInvocationHandler,不管是jdk 还是javassist 去生成代理,最后封装的invoker 都是InvokerInvocationHandler,所以我们要看服务调用的流程话,一定是从这个InvokerInvocationHandler 开始。 ? ? ? ? 既然只InvokerInvocationHandler 类,那直接搜到具体得代码就行了,InvokerInvocationHandler 就是InvocationHandler 类的实现,那么就可以找到具体的invoke 方法,而这里面也没有什么内容,还是调用invoker 接口的invoke 方法。 ? ? ? ? ?还是结合之前的内容,InvokerInvocationHandler 的上一层invoker 封装是什么呢,我们可以找到MockClusterInvoker 的实现。 Mock是Cluster层的开始? ? ? ? 找到MockClusterInvoker 的具体代码,到了这一层我们就已经到了Cluster 路由层了,在代码中我们是可以找到dubbo 在这一层具体是做了些什么的。 ? ? ? ? 看代码,这里首先就是获取dubbo 的mock 的配置,然后是false 也就是没有配置的话,就是就是下一步;如果是force 的话,那么就是直接执行mock 的逻辑,不发起远程调用;如果都不是,还可以做到服务降级,也就是如果远程调用出错的话,可以做已定义的mock 逻辑。
? ? ? ? 继续还是invoker 的实现调用,而这里就是consumer 启动时封装的具体容错对象,没有配置的情况下就是failover,那么可以继续跟到failoverCluster,因为直接点到invoker 接口是没有找到这个实现类的,所以我们找到对应的父类即可,也就是AbstractClusterInvoker 类,下面看下具体代码。 ? ? ? ? 可以看到的是第一步就是路由,先获取到的invokers,然后在加载具体的负载均衡策略,将策略在传给集群容错的调用,这样下一步就可以直接定位到具体的容错对象,也就是failoverClusterInvoker 类,然后要找到具体的doInvoke 方法。
? ? ? ? failoverClusterInvoker 这里代码太多了,我就不一个一个展示了,首先我们要关注的是要通过负载均衡策略去从刚刚上面路由到的invokers 中获取到具体的invoker 对象,然后由具体的invoker.invoke 进行调用返回,这里的invoker 已经是架构图中的cluster 层的最后invoker 对象了,结合架构图这里的invoke 调用时不会直接到DubboInvoker,这里还需要经过一系列的filter 后才能转到DubboInvoker 对象,那么是怎么经过这些filter 的呢? ? ? ? ? 从架构图中我们可以知道filter 是在protocol 层,而protocol 层也是rpc层,所以我们可以找rpc层的dubbo项目,找到protocol的配置,而在api项目中我们也可以找到相关的配置,在这个配置中我们就可以看到相关的filter 配置了,其实可以看到filter 就是一个wrapper,而在这个ProtocolFilterWrapper 的buildInvokerChain方法中,也就是获取了一系列的filter 从而形成了一个过滤器链,用于包装invoker。 ? ? ? ? ? ? ?当走过filter 之后,我们就可以得到一个原始的invoker,然后我们就会走到一个叫做AsyncToSyncInvoker 异步转同步的类中,找到对应的invoke 方法,然后到DubboInvoker 的父类AbstractInvoker,然后就是老套路跟到DubboInvoker 的doInvoke 方法,在这里我们基本上就可以看到请求发送的地方了,currentClient 是从连接缓存中获取的,这里是可以做长连接使用的。 ? ? ? ? 这里其实注释也描述了,我们最后会在HeaderExchangeChannel 的request 方法中调用channel.send,这里也是最后走到了NettyChannel的channel.writeAndFlush调用,这个就是真正的netty 服务调用了。到这一步我们已经将exchange 信息交换层的内容走完了,后面就是transport 网络传输层和数据序列化层的内容了。 ? NettyClient正式开始远程调用? ? ? ? 既然我们之前对代理对象封装的就是netty 实现的远程调用,那么我们直接找到NettyClient 类就行,找到doOpen 方法,回想一下我们之前自己编写rpc 框架时,也是使用netty 首先要做的就是一二次编码和解码,然后就是对应的自定义handler,dubbo 也是一样的流程(目前基本上所有的rpc框架都是这个流程),因为我们是consumer 端的调用,那么首先要找的就是对应的编码器,找到NettyClient 中的这段代码,跟进编码器(注意编码器和解码器不要混淆)找到下面这段代码,看到MessageToByteEncoder 对象如果看过我netty 相关的文章这个应该是比较熟悉的,这个是netty 提供的编码器的一种。 ?
? ? ? ? 跟进codec.encode 方法,这里会有多个实现,我们要找的是DubboCountCodec,然后跟进是ExchangeCodec 找到对应的请求编码encodeRequest 方法。 ? ? ? ? 到这个地方我们就要讲一个dubbo 基础知识点了,先引入一个dubbo 官网的描述地址:实现细节 | Apache Dubbo,这里可以直观的看到dubbo 的协议头是16个字节,前4个字节中的0和1字节分别存入Magic High、Magic Low、第二号字节(也就是真正的第3个字节)第16个bit 位存是请求还是响应标识,第17个bit 位存入标识是:是否希望响应有返回值,第18个bit 位是标识是否是心态请求,第19到23个bit 位是表示具体的序列化方式,这里的序列化方式有6种,具体的表示可以看文档,最后第3号字节存的就是响应的状态,这个具体的还是看文档,提一个比如:20表示的就是OK;第4到第11号字节(总共就是8个字节)存入的就是请求的唯一键;第12到15号字节存入的就是消息体的长度,这个就是为了解决TCP协议的拆包和沾包问题。 ? ? ? ? 上述就是对于dubbo 定义协议头的方式,我们自己如果需要定义这种协议头的话,就可以参考这个,然后我们继续说代码,我把响应的代码全部粘到下面了,其实就可以看到首先就是封装前16个字节头,然后根据不同的情况继续封装消息体,然后就是封装成不同的对象,然后再次序列化(也就是二次编码),最后用ChannelBuffer 进行写出,这里的writerIndex 方法其实就是调用netty的buffer 对象进行写出的,一个意思。
provider方的接收NettyServer的开始?? ? ? ? ?既然已经将消息封装好并写到了channel 中,那么provider 端就是使用netty 来接受了,对了consumer 端还有一个NettyClientHandler 这个就是对于读写的一个封装。我们可以接着看provider 端的接收,直接找打NettyServer 对象的doOpen 方法,还是一样的要做业务处理先解码,我们简单的看下解码器,跟编码器的跟踪方法一直,可以找打ByteToMessageDecoder 的实现,然后可以跟进codec.decode,找到最后的exchangeCodec 实现类,找到对应的解码方式。 ? ? ? ? 具体的解码代码我放在下面了,跟编码的流程是一样的,首选解析前16个字节的消息头,然后就是根据消息头的信息解析对应的消息体,然后将消息体反序列化,然后组装成对应的请求对象。
? ? ? ? ?当解析完成之后,会用ByteToMessageDecoder 提供的out 集合写入到下一层,这里我们就要看对应的NettyServerHandler 了,找到相关的业务处理方法 channelRead方法,然后我们可以跟到HeaderExchangeHandler.received,在这里我们又能发现,当前线程任然是netty的IO线程,但是这里dubbo会将心跳消息进行组装,然后直接返回,至于其余的消息则是根据配置的线程,也就是provider 方的配置,来获取线程分发的方式。(下面图片取自于dubbo官方文档,但是有几点目前不适用于2.7.3的源码,比如心跳在IO线程中已经返回了) ? ? ? ? 我们之前配置的是message,所以我们要找的Handler 就是MessageOnlyChannelHandler.received,这里我们看它的executor.execute 方法,这里的入参是ChannelEventRunnable 线程,那么有到了老套路环境,是线程就找到它的run 方法,下面我们看下它的代码逻辑。 ? ? ? ? 我们这里看下上面所说线程对应的run 方法,其实它所做的也就是一些请求情况的判断,然后调用对应的处理,那么现在就可以走到handler.received 方法里面,这里跟到的是DecodeHanndler,这里就有疑问了,为什么之前已经解码了的,现在又要解码,其实之前的解码只是解码了一部分,请求对象中还有一部分消息没有解码,为的就是防止Netty 中的IO线程出现问题。说会正题,目前再下一步就是HeaderExchangeHandler.received,这里我们可以放心consumer 方和provider 方使用exchange 层传输的对象都是同一个。 ? ? ? ? 其实到这里,我们再结合架构图已经可以发现这里已经形成了一个完成的流程了,exchange 层走完之后,那么就是到了DubboProtocol,然后进过filter 解析为invoker,然后进行真正的远程调整,然后原路返回,这样dubbo 的服务调用过程就结束了。 ? ? ? ? ? 上述就是dubbo 的服务调过程源码解析,其实简单的总结一下就是,provider 方启动时,先启动netty 进行连接绑定,然后连接注册中间,将暴露接口进行服务注册,provider 方启动成功;consumer 方启动时,先将获取注册中心,可以将自己也注册到注册中心上,然后拉去所有暴露接口的注册中心中的服务信息,然后将所有的暴露接口进行封装,其中首先就是封装netty 连接,然后就是容错机制、负载均衡策略、路由策略等等,然后将为装好的invoker 生成代理,consumer 方启动成功;接着就是服务调用,consumer 方进行代理调用,就是一层层解析,然后netty 调用,provider 方监听到连接和读写事件,进行层层解析得到一个具体的invoker,然后就是对invoker 进行对应的业务操作,最后就是走之前启动封装的流程,再将返回对象进行封装,由netty 回写给consumer 方,然后consumer 方又是层层解析,然后给到对应的调用业务,dubbo就此结束。 ? ? ? ? 总结一下最近一段时间的文章,dubbo 算是我自己学习过程中总结的比较完成的系列文章,如果有需要可以跟着我的文章一步一步学习,源码已经放在我的资源里面,有疑问可以留言给我,我看到会尽量回答,愿你我成为技术学习道路上的同行者,当然有大佬看到不对的地方也可以私信我,我会及时改正。 |
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 5:32:10- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |