🐼作者简介:一名大三在校生🎋
空有想法,没有实践,难成大事
专栏前言:探索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());
}
欢迎添加微信,加入我的核心小队,请备注来意
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇