9.0-源码分析:Dubbo Remoting 层核心接口分析

dubbo-remoting 模块,该模块提供了多种客户端和服务端通信的功能。在 Dubbo 的整体架构设计图中,我们可以看到最底层红色框选中的部分即为 Remoting 层,其中包括了 Exchange、Transport和Serialize 三个子层次。这里我们要介绍的 dubbo-remoting 模块主要对应 Exchange 和 Transport 两层。

在这里插入图片描述

Dubbo 整体架构设计图

Dubbo 并没有自己实现一套完整的网络库,而是使用现有的、相对成熟的第三方网络库,例如,Netty、Mina 或是 Grizzly 等 NIO 框架。我们可以根据自己的实际场景和需求修改配置,选择底层使用的 NIO 框架。

下图展示了 dubbo-remoting 模块的结构,其中每个子模块对应一个第三方 NIO 框架,例如,dubbo-remoting-netty4 子模块使用 Netty4 实现 Dubbo 的远程通信,dubbo-remoting-grizzly 子模块使用 Grizzly 实现 Dubbo 的远程通信。
在这里插入图片描述

dubbo-remoting-api 模块

需要注意的是,Dubbo 的 dubbo-remoting-api 是其他 dubbo-remoting-* 模块的顶层抽象,其他 dubbo-remoting 子模块都是依赖第三方 NIO 库实现 dubbo-remoting-api 模块的,依赖关系如下图所示:

在这里插入图片描述
我们先来看一下 dubbo-remoting-api 中对整个 Remoting 层的抽象,dubbo-remoting-api 模块的结构如下图所示:
在这里插入图片描述
一般情况下,我们会将功能类似或是相关联的类放到一个包中,所以我们需要先来了解 dubbo-remoting-api 模块中各个包的功能。

  • buffer 包:定义了缓冲区相关的接口、抽象类以及实现类。缓冲区在NIO框架中是一个不可或缺的角色,在各个 NIO 框架中都有自己的缓冲区实现。这里的 buffer 包在更高的层面,抽象了各个 NIO 框架的缓冲区,同时也提供了一些基础实现。
  • exchange 包:抽象了 Request 和 Response 两个概念,并为其添加很多特性。这是整个远程调用非常核心的部分。
  • transport 包:对网络传输层的抽象,但它只负责抽象单向消息的传输,即请求消息由 Client 端发出,Server 端接收;响应消息由 Server 端发出,Client端接收。有很多网络库可以实现网络传输的功能,例如 Netty、Grizzly 等, transport 包是在这些网络库上层的一层抽象。
  • 其他接口:Endpoint、Channel、Transporter、Dispatcher 等顶层接口放到了org.apache.dubbo.remoting 这个包,这些接口是 Dubbo Remoting 的核心接口。

下面我们就来介绍 Dubbo 是如何抽象这些核心接口的。

传输层核心接口

在 Dubbo 中会抽象出一个“端点(Endpoint)”的概念,我们可以通过一个 ip 和 port 唯一确定一个端点,两个端点之间会创建 TCP 连接,可以双向传输数据。Dubbo 将 Endpoint 之间的 TCP 连接抽象为通道(Channel),将发起请求的 Endpoint 抽象为客户端(Client),将接收请求的 Endpoint 抽象为服务端(Server)。这些抽象出来的概念,也是整个 dubbo-remoting-api 模块的基础,下面我们会逐个进行介绍。

Dubbo 中Endpoint 接口的定义如下:

在这里插入图片描述
如上图所示,

  1. 这里的 get*() 方法是获得 Endpoint 本身的一些属性,其中包括获取 Endpoint 的本地地址、关联的 URL 信息以及底层 Channel 关联的 ChannelHandler。
  2. send() 方法负责数据发送,两个重载的区别在后面介绍 Endpoint 实现的时候我们再详细说明。
  3. 最后两个 close() 方法的重载以及 startClose() 方法用于关闭底层 Channel ,
  4. isClosed() 方法用于检测底层 Channel 是否已关闭。

channel

Channel 是对两个 Endpoint 连接的抽象,好比连接两个位置的传送带,两个 Endpoint 传输的消息就好比传送带上的货物,消息发送端会往 Channel 写入消息,而接收端会从 Channel 读取消息。这与介绍的 Netty 中的 Channel 基本一致。

在这里插入图片描述
下面是Channel 接口的定义,我们可以看出两点:一个是 Channel 接口继承了 Endpoint 接口,也具备开关状态以及发送数据的能力;另一个是可以在 Channel 上附加 KV 属性。

在这里插入图片描述

channelhandler

ChannelHandler 是注册在 Channel 上的消息处理器,在 Netty 中也有类似的抽象,相信你对此应该不会陌生。下图展示了 ChannelHandler 接口的定义,在 ChannelHandler 中可以处理 Channel 的连接建立以及连接断开事件,还可以处理读取到的数据、发送的数据以及捕获到的异常。从这些方法的命名可以看到,它们都是动词的过去式,说明相应事件已经发生过了。

在这里插入图片描述
需要注意的是:ChannelHandler 接口被 @SPI 注解修饰,表示该接口是一个扩展点。

Codes2 编码和解码

在前面课时介绍 Netty 的时候,我们提到过有一类特殊的 ChannelHandler 专门负责实现编解码功能,从而实现字节数据与有意义的消息之间的转换,或是消息之间的相互转换。在dubbo-remoting-api 中也有相似的抽象,如下所示:

@SPI
public interface Codec2 {
  @Adaptive({Constants.CODEC_KEY})
  void encode(Channel channel, ChannelBuffer buffer, Object message) 
      throws IOException;
  @Adaptive({Constants.CODEC_KEY})
  Object decode(Channel channel, ChannelBuffer buffer)
      throws IOException;
  enum DecodeResult {
      NEED_MORE_INPUT, SKIP_SOME_INPUT
  }
}

这里需要关注的是 Codec2 接口被 @SPI 接口修饰了,表示该接口是一个扩展接口,同时其 encode() 方法和 decode() 方法都被 @Adaptive 注解修饰,也就会生成适配器类,其中会根据 URL 中的 codec 值确定具体的扩展实现类。

DecodeResult 这个枚举是在处理 TCP 传输时粘包和拆包使用的,之前简易版本 RPC 也处理过这种问题,例如,当前能读取到的数据不足以构成一个消息时,就会使用 NEED_MORE_INPUT 这个枚举。

接下来看Client 和 RemotingServer 两个接口,分别抽象了客户端和服务端,两者都继承了 Endpoint、Resetable 等接口,服务端和客户端都是端点, Client 继承了Channel, 但是服务端没有继承Channel

在这里插入图片描述
Client 和 Server 本身都是 Endpoint,只不过在语义上区分了请求和响应的职责,两者都具备发送的能力,所以都继承了 Endpoint 接口。Client 和 Server 的主要区别是 Client 只能关联一个 Channel,而 Server 可以接收多个 Client 发起的 Channel 连接。所以在 RemotingServer 接口中定义了查询 Channel 的相关方法,如下图所示:
在这里插入图片描述

transport

Dubbo 在 Client 和 Server 之上又封装了一层Transporter 接口,其具体定义如下:

@SPI("netty")
public interface Transporter {
  @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
  RemotingServer bind(URL url, ChannelHandler handler) 
      throws RemotingException;
  @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
  Client connect(URL url, ChannelHandler handler) 
      throws RemotingException;
}

我们看到 Transporter 接口上有 @SPI 注解,它是一个扩展接口,默认使用“netty”这个扩展名,@Adaptive 注解的出现表示动态生成适配器类,会先后根据“server”“transporter”的值确定 RemotingServer 的扩展实现类,先后根据“client”“transporter”的值确定 Client 接口的扩展实现。

Transporter 接口的实现有哪些呢?如下图所示,针对每个支持的 NIO 库,都有一个 Transporter 接口实现,散落在各个 dubbo-remoting-* 实现模块中。

在这里插入图片描述
这些 Transporter 接口实现返回的 Client 和 RemotingServer 具体是什么呢?如下图所示,返回的是 NIO 库对应的 RemotingServer 实现和 Client 实现。

在这里插入图片描述
在这里插入图片描述
相信看到这里,你应该已经发现 Transporter 这一层抽象出来的接口,与 Netty 的核心接口是非常相似的。那为什么要单独抽象出 Transporter层,而不是像简易版 RPC 框架那样,直接让上层使用 Netty 呢?

其实这个问题的答案也呼之欲出了,Netty、Mina、Grizzly 这个 NIO 库对外接口和使用方式不一样,如果在上层直接依赖了 Netty 或是 Grizzly,就依赖了具体的 NIO 库实现,而不是依赖一个有传输能力的抽象,后续要切换实现的话,就需要修改依赖和接入的相关代码,非常容易改出 Bug。这也不符合设计模式中的开放-封闭原则。

有了 Transporter 层之后,我们可以通过 Dubbo SPI 修改使用的具体 Transporter 扩展实现,从而切换到不同的 Client 和 RemotingServer 实现,达到底层 NIO 库切换的目的,而且无须修改任何代码。即使有更先进的 NIO 库出现,我们也只需要开发相应的 dubbo-remoting-* 实现模块提供 Transporter、Client、RemotingServer 等核心接口的实现,即可接入,完全符合开放-封闭原则。

Transporters

在最后,我们还要看一个类——Transporters,它不是一个接口,而是门面类,其中封装了 Transporter 对象的创建(通过 Dubbo SPI)以及 ChannelHandler 的处理,如下所示:

public class Transporters {
  private Transporters() {
  // 省略bind()和connect()方法的重载
  public static RemotingServer bind(URL url, 
          ChannelHandler... handlers) throws RemotingException {
      ChannelHandler handler;
      if (handlers.length == 1) {
          handler = handlers[0];
      } else {
          handler = new ChannelHandlerDispatcher(handlers);
      }
      return getTransporter().bind(url, handler);
  }
  public static Client connect(URL url, ChannelHandler... handlers)
         throws RemotingException {
      ChannelHandler handler;
      if (handlers == null || handlers.length == 0) {
          handler = new ChannelHandlerAdapter();
      } else if (handlers.length == 1) {
          handler = handlers[0];
      } else { // ChannelHandlerDispatcher
          handler = new ChannelHandlerDispatcher(handlers);
      }
      return getTransporter().connect(url, handler);
  }
  public static Transporter getTransporter() {
      // 自动生成Transporter适配器并加载
      return ExtensionLoader.getExtensionLoader(Transporter.class)
          .getAdaptiveExtension();
  }
}

在创建 Client 和 RemotingServer 的时候,可以指定多个 ChannelHandler 绑定到 Channel 来处理其中传输的数据。Transporters.connect() 方法和 bind() 方法中,会将多个 ChannelHandler 封装成一个 ChannelHandlerDispatcher 对象。

ChannelHandlerDispatcher 也是 ChannelHandler 接口的实现类之一,维护了一个 CopyOnWriteArraySet 集合,它所有的 ChannelHandler 接口实现都会调用其中每个 ChannelHandler 元素的相应方法。另外,ChannelHandlerDispatcher 还提供了增删该 ChannelHandler 集合的相关方法。

到此为止,Dubbo Transport 层的核心接口就介绍完了,这里简单总结一下:

  • Endpoint 接口抽象了“端点”的概念,这是所有抽象接口的基础。
  • 上层使用方会通过 Transporters 门面类获取到 Transporter 的具体扩展实现,然后通过 Transporter 拿到相应的 Client 和 RemotingServer 实现,就可以建立(或接收)Channel 与远端进行交互了。
  • 无论是 Client 还是 RemotingServer,都会使用 ChannelHandler 处理 Channel 中传输的数据,其中负责编解码的 ChannelHandler 被抽象出为 Codec2 接口。

整个架构如下图所示,与 Netty 的架构非常类似。

在这里插入图片描述

总结

本课时我们首先介绍了 dubbo-remoting 模块在 Dubbo 架构中的位置,以及 dubbo-remoting 模块的结构。接下来分析了 dubbo-remoting 模块中各个子模块之间的依赖关系,并重点介绍了 dubbo-remoting-api 子模块中各个包的核心功能。最后我们还深入分析了整个 Transport 层的核心接口,以及这些接口抽象出来的 Transporter 架构。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

love 2d Lua 俄罗斯方块超详细教程

源码已经更新在CSDN的码库里: git clone https://gitcode.com/funsion/love2d-game.git 一直在找Lua 能快速便捷实现图形界面的软件,找了一堆,终于发现love2d是小而美的原生lua图形界面实现的方式。 并参考相关教程做了一个更详细的&#x…

javaWeb项目-学生考勤管理系统功能介绍

项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端:Vue、ElementUI 关键技术:springboot、SSM、vue、MYSQL、MAVEN 数据库工具:Navicat、SQLyog 1、JAVA技术 JavaSc…

百度轨迹验证码识别代码分享

百度出了如图所示的验证码,需要拖动滑块,与如图所示的曲线轨迹进行重合。经过不断研究,终于解决了这个问题。我把识别代码分享给大家。 下面是使用selenium进行验证的,这样可以看到轨迹滑动的过程,如果需要使用js逆向…

MySQL Server 8.3.0 重要变更解析

MySQL Server 8.3.0 Innovation 版本是 MySQL 8.x 系列最后一个创新版本,下个月即将迎来 MySQL 8.4.0 LTS 长期支持版本。 关于发版模型变更,在之前的文章 重磅!MySQL 8.1.0 已来! 中已有所介绍。 这里补充一点,对于 M…

国家发改委特许经营方案对公共数据运营的思考

数据交易所自贵州大数据交易所成立以来,已经有无数数据交易所像春笋一样在中华大地冒出来。随着国家数据局的成立、数据要素二十条的发布,各地数据交易所开启了竞赛机制,在数据体量、数商数量、交易规模等指标展开博弈。 公共数据运营是否会马…

Spring6-代理模式 / AOP切面编程

目录 1. 场景模拟 1.1. 声明接口 1.2. 创建实现类 1.3. 创建带日志功能的实现类 1.4. 提出问题 2. 代理模式 2.1. 概述 2.2. 静态代理 2.3. 动态代理 3. AOP切面编程 3.1. 概述 3.2. 相关术语 3.2.1. 横切关注点 3.2.2. 通知(增强) 3.2.3…

【JavaSE】java刷题--数组练习

前言 本篇讲解了一些数组相关题目(主要以代码的形式呈现),主要目的在于巩固数组相关知识。 上一篇 数组 讲解了一维数组和二维数组的基础知识~ 欢迎关注个人主页:逸狼 创造不易,可以点点赞吗~ 如有错误,欢迎…

大创项目推荐 深度学习 opencv python 实现中国交通标志识别_1

文章目录 0 前言1 yolov5实现中国交通标志检测2.算法原理2.1 算法简介2.2网络架构2.3 关键代码 3 数据集处理3.1 VOC格式介绍3.2 将中国交通标志检测数据集CCTSDB数据转换成VOC数据格式3.3 手动标注数据集 4 模型训练5 实现效果5.1 视频效果 6 最后 0 前言 🔥 优质…

HTX Ventures:为什么BounceBit可能成为新的BTC生态解决方案?

随着BTC现货ETF的通过,全球各大机构和个人都在不断加码对BTC的持仓,BTC价格也随之上升,目前已上升至全球市值排名前十的资产。在本轮市场周期中,BTC铭文和BTC扩容是两个被市场高度关注的细分赛道。BTC生态资产的多元化收益探索正在…

TypseScript再学习之类型别名和接口(10)

先看类型别名:使用关键字 type 声明,注意有等于号额 // 类型别名 使用关键字 type 声明,注意有等于号额 type Cat {name: string; }; let huahua: Cat {name: "花花", };type和interface不同之处在于:interface 是可以自动合并类型的&#…

Linux shell编程学习笔记43:cut命令

0 前言 在 Linux shell编程学习笔记42:md5sum 中,md5sum命令计算md5校验值后返回信息的格式是: md5校验值 文件名 包括两项内容,前一项是md5校验值 ,后一项是文件名。 如果我们只想要前面的md5 校验值&#xff0c…

计算机网络—UDP协议详解:特性、应用

​ 🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:マリンブルーの庭園—ずっと真夜中でいいのに。 0:34━━━━━━️💟──────── 3:34 🔄 ◀…

Rust 02.控制、引用、切片Slice、智能指针

1.控制流 //rust通过所有权机制来管理内存,编译器在编译就会根据所有权规则对内存的使用进行 //堆和栈 //编译的时候数据的类型大小是固定的,就是分配在栈上的 //编译的时候数据类型大小不固定,就是分配堆上的 fn main() {let x: i32 1;{le…

美术馆设计方案优化布局与设施提升观众体验!

如今,美术馆不仅仅是作为展示艺术作品的平台,也是吸引公众参与和创造独特体验的数字艺术体验空间,因此许多传统美术馆在进行翻修改造时,都会更加注重用户体验,并在其中使用大量的多媒体互动,让参观者能够在…

基于 YOLO V8 Fine-Tuning 训练自定义的目标检测模型

一、YOLO V8 YOLO V8 是由 2023 年 ultralytics 公司开源的发布,是结合了前几代 YOLO 的融合改进版。YOLO V8 支持全方位的视觉 AI 任务,包括检测、分割、姿态估计、跟踪和分类。并且在速度和准确性方面具有无与伦比的性能。能够应用在各种对速度和精度…

【重制版】在Android手机上安装kali Linux

前言 由于kali官方的Nethunter2的安装代码因为…无法访问,手头又没有一些受支持的机器3,所以做了这个脚本,供大家使用。 工具 搭载基于Android的手机TermuxVNC Viewer 安装必备软件(如已安装请忽略) 请到 https://www.hestudio.net/post…

制造出海,灵途科技助力割草机器人、泳池清洁机器人全方位感知

近年来,越来越多的中国企业开始对外开拓,走向海外市场、挖掘和满足全球消费者的需求。在消费机器人领域,中国企业出海成绩亮眼!在2024 ces 和上海AWE展会上,多家机器人公司展示了家用智能割草机器人、泳池清洁机器人的…

C#基础知识总结

C语言、C和C#的区别 ✔ 面向对象编程(OOP): C 是一种过程化的编程语言,它不直接支持面向对象编程。然而,C 是一种支持 OOP 的 C 的超集,它引入了类、对象、继承、多态等概念。C# 是完全面向对象的&#xff…

【C++】string类(常用接口)

🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343🔥 系列专栏:http://t.csdnimg.cn/eCa5z 目录 修改操作 push_back append operator assign insert erase replace c_str find string类非成…

淘宝商品详情数据(商品分析,竞品分析,代购商城建站与跨境电商,ERP系统商品数据选品)

淘宝商品详情数据在多个业务场景中发挥着关键作用,以下是一些主要的应用场景: 请求示例,API接口接入Anzexi58 商品分析:通过对淘宝商品详情的全面分析,商家可以深入了解商品的属性、价格、销售量、评价等信息。这些数…
最新文章