当前位置: 首页 > news >正文

Dubbo3单端口多协议源码分析

前言

Dubbo3 发布了基于 HTTP1、HTTP2、HTTP3 的 triple 通信协议,相较于之前的 dubbo 私有化协议,triple 协议在跨语言、云原生、服务治理方面有显著优势。

为了方便用户平滑迁移到 triple 协议,Dubbo3 新增了一项能力,支持在同一个端口上同时发布 dubbo 协议和 triple 协议的服务,做到协议迁移几乎没有额外负担。

实战

同时发布 dubbo 和 triple 协议的服务,只需增加配置:

dubbo:protocol:name: dubboext-protocol: tri

或者设置 ProtocolConfig#extProtocol 属性

ProtocolConfig protocolConfig = new ProtocolConfig("dubbo", 50000);
// 额外发布triple协议的服务
protocolConfig.setExtProtocol("tri");

下面是一个示例,在 50000 端口发布 EchoService 服务,同时支持 dubbo 和 triple 通信协议。

public class Provider {public static void main(String[] args) {ServiceConfig<EchoService> serviceConfig = new ServiceConfig<>();serviceConfig.setInterface(EchoService.class);serviceConfig.setRef(new EchoServiceImpl());ApplicationConfig applicationConfig = new ApplicationConfig("app-provider");applicationConfig.setQosEnable(false);ProtocolConfig protocolConfig = new ProtocolConfig("dubbo", 50000);// 额外发布triple协议的服务protocolConfig.setExtProtocol("tri");DubboBootstrap.getInstance().application(applicationConfig).protocol(protocolConfig).service(serviceConfig).registry(new RegistryConfig("N/A")).start().await();}
}

EchoService 接口

@Mapping(path = "/")
public interface EchoService {@Mapping(path = "/echo", method = HttpMethods.GET)String echo(@Param("name") String name, @Param("age") int age);
}public class EchoServiceImpl implements EchoService {@Overridepublic String echo(String name, int age) {return "echo: {name: %s , age: %d}".formatted(name, age);}
}

启动消费者,直连dubbo://127.0.0.1:50000通过 dubbo 协议去调用服务是成功的。

同样,直接在浏览器访问http://127.0.0.1:50000/echo?name=lisa&age=18也是可以的。

因为基于 http 协议,所以 triple 协议的服务非常好调试,通用性也更好,现在你完全可以直接用 dubbo 来处理南北向流量,在这之前,你必须通过加入一个网关层,做 http 协议到 dubbo 协议的转换,非常麻烦。

源码分析

秉承着“知其然,知其所以然”的态度,通过源码了解 dubbo3 单端口多协议的实现原理。

按照以往的开发经验,一般都是一个端口只处理一种通信协议。以 nacos 为例,端口 8848 提供 http 协议的服务,端口 9848 提供 gRPC 协议的服务,客户端按需选择端口建立连接。单端口多协议的支持,确实比较少见。

熟悉 dubbo 的同学都知道,dubbo 会把服务封装成 ServiceConfig 对象,并通过其 export 方法导出。笔者顺着服务导出的链路,最终整理出以下流程图。

画板

triple 协议对应的协议实现类是 TripleProtocol,dubbo 默认使用 netty 来处理网络数据传输。既然是服务导出,那么必然会创建 Netty 的 ServerBootstrap,根据调用链查找,果然在导出的过程中会创建 NettyPortUnificationServer,在它的构造函数里会调用其 doOpen 方法,最终创建 ServerBootstrap 绑定本地端口监听 RPC 请求。

ServerBootstrap 的 ChannelPipeline 是数据处理的管道,dubbo 怎么配置它是关键。

通过源码可以发现,这个叫negotiation-protocol的 Handler 就是通信协议协商的处理器。

NettyPortUnificationServerHandler 实现了 ByteToMessageDecoder 接口,所以它是一个解码器,当有数据可读时,会触发其 decode 方法,最终触发 detectProtocol 方法进行协议检测。

detectProtocol 流程:

  1. 遍历支持检测的协议名称,挨个检测
  2. 为了和 Protocol 区分,dubbo 新增了 WireProtocol 接口,它支持协议检测和配置协议处理器
  3. 标记读指针
  4. 调用 ProtocolDetector 组件进行协议检测,返回检测结果
  5. 重置读指针,因为这里只是检测协议,不能触发实际的数据读取,否则后面的 Handler 读到的数据不完整
  6. 如果协议检测通过,那么就调用 configServerProtocolHandler 方法配置Handler,同时移除自己,因为自己只是负责协议协商,协议一旦确认,自己这个 Handler 就没用了

协议协商是一部分,WireProtocol#configServerProtocolHandler 同样很关键。协议一旦确认,那么双方就要基于这个协议去通信了,WireProtocol 会根据检测的结果,根据协议来配置协议处理器 Handler。

配置协议处理器,本质上是配置两个东西:

  • 消息编解码器:字节流和请求响应对象的转换,比如把请求字节流转换成 HttpRequest
  • RPC 请求处理器:根据请求对象去查找服务 Invoker,调用服务并返回结果

以 TripleHttp2Protocol 为例,根据 Http 版本的不同,会分别针对 http1 和 http2 版本进行配置。又以 http1 为例,dubbo 会配置服务端编解码器 HttpServerCodec,和RPC请求处理器 NettyHttp1ConnectionHandler。

至此,我们知道。dubbo 服务导出会创建 ServerBootstrap 监听本地端口来处理请求,然后配置一个 NettyPortUnificationServerHandler 的解码器来进行协议协商,协议一旦确定,dubbo 就会针对这个连接针对性地配置协议的编解码器和RPC请求处理器,以此来实现单端口多协议。

但是,现在还有最后一个问题,协议是怎么检测的?

答案是:通过魔数(Magic Number)

在计算机领域,魔数是一个有特殊含义或用途的固定值。

比如通过魔数来区分文件格式,魔数通常位于文件的头几个字节:

  • PNG 文件:前8字节为 \x89PNG\r\n\x1a\n(十六进制)
  • JPEG 文件:前2字节为 0xFFD8
  • ELF 可执行文件:前4字节为 0x7F454C46(ASCII 对应 \x7FELF

同理,通信协议一般也有魔数。幸运的是,dubbo 协议和 triple 协议都有魔数,自然也就能直观地识别协议了。

dubbo 私有化协议,每个请求报文的前2个字节就是魔数,值是十六进制的0xdabb

所以,DubboDetector 识别 dubbo 协议很简单,就是比较前2个字节

triple 是基于 http 的,所以 triple 协议的识别,本质是 http 协议的识别。

http1 没有明确的魔数,但是有规律。请求报文是以 HttpMethod 开头的,而 HttpMethod 只有八个,最长的是OPTIONS7个字节。所以,http1 协议检测也很简单,读取前7个字节,判断是否属于 HttpMethod 中的任意一个即可。

http2 就更简单了,http 协议规定,为了避免对端不支持 http2,所以客户端在使用 http2 前,必须先发一个 **连接前言(Connection Preface),**如果对端支持该协议,客户端就继续用 http2 通信,否则回退到 http1。这个连接前言就相当于是 http2 底层的一个握手数据,也可以把它看作魔数。

连接前言是一个固定值,如下:

PRI * HTTP/2.0
[空行]
SM

所以,http2 的协议检测,就是判断报文是否以连接前言开头

尾巴

dubbo3 为了方便用户平滑升级到 triple 协议,支持在一个端口上同时发布 dubbo 协议和 triple 协议的服务。实现原理是通过配置一个特殊的解码器来进行协议协商,通过识别请求报文的魔数来检测协议,协议一旦确认,再针对性地配置协议处理器。协议处理器,主要包含 消息编解码器和RPC请求处理器两部分。

http://www.dtcms.com/a/365819.html

相关文章:

  • 员工拍照泄密?U盘偷拷资料?终端数据安全如何守护?
  • G1垃圾收集器
  • 【高级】系统架构师 | 信息系统战略规划、EAI 与新技术
  • 攻防世界secret-galaxy-300
  • 深度学习----卷积神经网络的数据增强
  • 如何给JavaScript语句添加注释?
  • 19.JS
  • Jmeter怎么实现接口关联?
  • 算法题(198):数字三角形
  • 使用 Terraform、AWS 和 Python 构建无服务器实时数据管道
  • 学习React-9-useSyncExternalStore
  • Ubuntu下把 SD 卡格式化为 FAT32
  • 【工具变量】“国家级大数据综合试验区”试点城市DID(2000-2024年)
  • ArkTS状态管理V1
  • Llama v3 中的低秩自适应 (LoRA)
  • 头歌实训作业答案C++ 01
  • Proteus8 + STM32CubeMX 实现 STM32F103R6 串口通信教程
  • JMeter下载安装及使用入门
  • 常用符号 Emoji 对照表——Unicode UTF-8
  • SQLSERVER临时表
  • 关于专业化与多元化该怎么选?
  • 解决MQ访问不了或者登录不成功问题
  • 卷积神经网络CNN-part2-简单的CNN
  • TypeScript与JavaScript:从动态少年到稳重青年的成长之路
  • RabbitMQ相关知识
  • HTML第七课:发展史
  • Unity:XML笔记(二)——Xml序列化、反序列化、IXmlSerializable接口
  • 裸机程序(1)
  • 【ARM嵌入式汇编基础】-数据处理指令(三)
  • 低成本低功耗认证芯片推荐——LCS4110R