IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: 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网络通讯协议

1. 前言

前面已经分析了Dubbo是如何暴露以及引用远程服务的,接下来自然是服务调用的实现细节了,但是在那之前,有必要先了解Dubbo的网络通信协议。
?

在单机系统中,调用本地方法获取结果很简单,因为同一个进程里内存是共享的。但是在分布式系统中,远程方法调用就显得较为复杂,必须通过网络将内存中的数据传输给对方,数据以何种格式传输呢?这就需要双方约定一个协议。

2. 通讯协议细节

image.png
这是Dubbo官方文档给的图,可以看出协议主要分为两块:16字节定长的Header、变长的Body。

2.1 协议头和协议体

Dubbo通讯协议头Header由16字节组成,Header设计的非常紧凑,能用bit表示就绝不用byte,内容如下:

Bit字段说明
0~15Magic Number魔数,固定0xdabb
16Req/Res0=Response,1=Request
172Way仅在Request在有用。是否期望服务器返回数据
18Event是否事件信息,如心跳
19~23Serialization ID序列化类型ID
24~31Status响应状态
32~95Request ID请求唯一标识
96~127Data LengthBody的长度
变长部分Body对象序列化的byte[]

Body部分是变长的,长度DataLength已经写在Header里了。Dubbo可以根据DataLength解决TCP粘包/拆包的问题,如果读取的字节数不足16,说明连一个完整的Header都没有接收到,此时会等待对端发送更多的数据。读取到一个完整的Header,会解析出DataLength,然后判断Body是否完整,不完整同样会等待对端传输更多的数据,完整则解析Body进行后续的请求处理。

2.2 编解码和序列化

Dubbo对于请求用Request类描述,对于响应用Response描述。
Dubbo针对协议头Header部分,使用Codec编解码,针对Body部分,使用序列化的方式。
?

当Consumer向Provider发起RPC调用时,会先创建RpcInvocation对象,再创建Request对象。然后利用NettyClient将数据发送到对端。我们知道,网络传输的总是字节序列,所以Request对象会经过编码再发送,对应的方法是ExchangeCodec#encodeRequest(),Dubbo会根据协议的格式向Channel先写入Header,再写入Body。

protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
    // 序列化策略 默认hessian2
    Serialization serialization = getSerialization(channel);
    // 协议头Header 16字节
    byte[] header = new byte[HEADER_LENGTH];
    // 2字节 魔数
    Bytes.short2bytes(MAGIC, header);
    header[2] = (byte) (FLAG_REQUEST | serialization.getContentTypeId());
    if (req.isTwoWay()) {
        header[2] |= FLAG_TWOWAY;
    }
    if (req.isEvent()) {
        header[2] |= FLAG_EVENT;
    }
    // RequestId 全局自增
    Bytes.long2bytes(req.getId(), header, 4);
    int savedWriteIndex = buffer.writerIndex();
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH);
    // 序列化Data
    ChannelBufferOutputStream bos = new ChannelBufferOutputStream(buffer);
    ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
    if (req.isEvent()) {
        encodeEventData(channel, out, req.getData());
    } else {
        encodeRequestData(channel, out, req.getData(), req.getVersion());
    }
    out.flushBuffer();
    if (out instanceof Cleanable) {
        ((Cleanable) out).cleanup();
    }
    bos.flush();
    bos.close();
    int len = bos.writtenBytes();
    // 校验负载,Body是否太大
    checkPayload(channel, len);
    Bytes.int2bytes(len, header, 12);

    buffer.writerIndex(savedWriteIndex);
    buffer.writeBytes(header); // write header.
    buffer.writerIndex(savedWriteIndex + HEADER_LENGTH + len);
}

Provider响应的Response,也是需要编码再发送的,处理流程和Request差不多,这里就不贴代码了。
?

对于Body部分,RpcInvocation该如何编码呢?我们以DubboCodec为例,事实上Dubbo并不会将整个RpcInvocation对象序列化输出,而是按照固定的格式,将重要信息写入到ObjectOutput。
格式如下:

  1. 协议Version
  2. ServiceName
  3. ServiceVersion
  4. MethodName
  5. ParameterTypesDesc
  6. Arguments序列化
  7. Attachments序列化

Provider按照约定的格式,依次将这些数据再读取出来即可。

protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
    RpcInvocation inv = (RpcInvocation) data;
    out.writeUTF(version);
    String serviceName = inv.getAttachment(INTERFACE_KEY);
    if (serviceName == null) {
        serviceName = inv.getAttachment(PATH_KEY);
    }
    out.writeUTF(serviceName);
    out.writeUTF(inv.getAttachment(VERSION_KEY));
    out.writeUTF(inv.getMethodName());
    out.writeUTF(inv.getParameterTypesDesc());
    Object[] args = inv.getArguments();
    if (args != null) {
        for (int i = 0; i < args.length; i++) {
            out.writeObject(encodeInvocationArgument(channel, inv, i));
        }
    }
    out.writeAttachments(inv.getObjectAttachments());
}

3. 总结

Dubbo通讯协议分为Header和Body两部分,Header固定由16个字节组成,设计的非常紧凑,尽可能的用bit表示,包含:魔数、请求响应标识、2Way标识、Event标识、序列化ID、Status、RequestID、DataLength。Body则是变长的,长度记录在Header最后4字节里,Dubbo可以基于此来解决TCP的粘包/拆包问题。
Header部分采用Codec编解码,Body部分使用序列化,具体取决于使用的的序列化实现方案,默认是Hessian2。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-13 13:13:40  更:2021-12-13 13:15:15 
 
开发: 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 10:16:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码