构建高效RPC框架:私有协议设计及消息封装解码实践

发布于:2024-05-09 ⋅ 阅读:(32) ⋅ 点赞:(0)

在这里插入图片描述

🐼作者简介:一名大三在校生🎋
空有想法,没有实践,难成大事

专栏前言:探索RPC框架的奥秘

在这里插入图片描述

简介在现代软件开发中,随着微服务架构的普及,远程过程调用(RPC)框架成为了连接服务之间通信的桥梁。我有决定开发了一款高性能的RPC框架,它不仅实现了服务之间的高效调用,还集成了关键的服务治理功能,如负载均衡、熔断机制和限流策略,以确保系统的稳定性和可靠性。

核心技术本项目采用Netty作为其强大的底层通信组件,确保了网络通信的高效与稳定。同时,通过与ZooKeeper的结合,实现了服务的注册与发现,为服务治理提供了坚实的基础。

下面我将提供一个全面的视角,来理解RPC框架的内部工作原理及其在实际开发中的应用。欢迎大家持续关注订阅专栏!!!

四、封装报文

在设计一个 rpc(Remote Procedure Call)远程调用框架时,需要考虑如何对请求和响应数据进行封装、以及编码、解码,以及如何表示调用的方法和参数。此时,我们必须要设计一个私有且通用的私有协议,协议是一种公平对话的模式,有了标准协议调用方和服务提供方就可以互相按照标准进行协商。

1、设计私有协议

我们想发送数据的时候必须遵循一些规范,比如dubbo中就封装了dubbo协议。事实上任何基于tcp上的应用层的通信方式都是一种协议,如下图的http协议,是我们最熟悉不过的应用层协议了:

相对于 HTTP 的而言,rpc 更多的是负责应用间的通信,所以性能要求相对更高。但 HTTP 协议的数据包大小相对请求数据本身要大很多,又需要加入很多无用的内容,比如换行符号、回车符等;还有一个更重要的原因是,HTTP 协议属于无状态协议,客户端无法对请求和响应进行关联,每次请求都需要重新建立连接,响应完成后再关闭连接。因此,对于要求高性能的 rpc 来说,HTTP 协议基本很难满足需求,所以 rpc 会选择设计更紧凑的私有协议。

2、协议结构

我们的项目设计的协议分为 Header(头部)和 Body(主体)两部分。Header 包含协议的元数据,例如消息类型、序列化类型、请求ID 等。Body 包含实际的 yrpc 请求或响应数据。

+-----------------------------------------------+
|                    Header                     |
+-----------------------------------------------+
|                      Body                     |
+-----------------------------------------------+

Header 结构
Header 可以包含以下字段:

  • Magic Number(4 字节):魔数,用于识别该协议,例如:0xCAFEBABE。
  • Version(1 字节):协议版本号。
  • MessageType(1 字节):消息类型,例如:0x01 表示请求,0x02 表示响应。
  • Serialization Type(1 字节):序列化类型,例如:0x01 表示 JSON,0x02 表示 Protobuf 等。
  • Request ID(8 字节):请求ID,用于标识请求和响应的匹配。
  • Body Length(4 字节):Body 部分的长度。
  • head length(4 字节)

Body 结构
Body 的结构取决于具体的 yrpc 请求或响应数据。
对于 yrpc 请求,Body 可以包含以下字段:

  • Service Name:被调用的服务名称。
  • Method Name:被调用的方法名称。
  • Method Arguments:被调用方法的参数列表。
  • Method Argument Types:被调用方法参数的类型列表。

对于 yrpc 响应,Body 可以包含以下字段:

  • Status Code:响应状态码,例如:0x00 表示成功,0x01 表示失败。
  • Error Message:错误信息,当 Status Code 为失败时,包含具体的错误信息。
  • Return Value:方法返回值,当 Status Code 为成功时,包含方法调用的返回值。

大致如下:

代码案例如下:

@Test
public void testMessage() throws IOException {
    ByteBuf message = Unpooled.buffer();
    message.writeBytes("ydl".getBytes(StandardCharsets.UTF_8));
    message.writeByte(1);
    message.writeShort(125);
    message.writeInt(256);
    message.writeByte(1);
    message.writeByte(0);
    message.writeByte(2);
    message.writeLong(251455L);
    // 用对象流转化为字节数据
    AppClient appClient = new AppClient();

    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(outputStream);
    oos.writeObject(appClient);
    byte[] bytes = outputStream.toByteArray();
    message.writeBytes(bytes);

    printAsBinary(message);

}

public static void printAsBinary(ByteBuf byteBuf) {
    byte[] bytes = new byte[byteBuf.readableBytes()];
    byteBuf.getBytes(byteBuf.readerIndex(), bytes);

    String binaryString = ByteBufUtil.hexDump(bytes);
    StringBuilder formattedBinary = new StringBuilder();

    for (int i = 0; i < binaryString.length(); i += 2) {
        formattedBinary.append(binaryString.substring(i, i + 2)).append(" ");
    }

    System.out.println("Binary representation: " + formattedBinary.toString());
}

在这里插入图片描述

欢迎添加微信,加入我的核心小队,请备注来意

👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇


网站公告

今日签到

点亮在社区的每一天
去签到