Full Duplex C++ RPC Library,Use Protobuf, Support HTTP API .
跨平台全双工双向(异步)RPC系统,也即通信两端都可以同时作为RPC服务方和客户端.
目前的RPC系统大多用于互联网行业后端系统,但他们之间更像一个单向图(不存在两个服务彼此依赖/互相调用),但游戏等行业中则两节点之间可能相互调用。 因此我们需要一个全双工RPC,在一个"链接"的两端均可开启服务和客户端,当然这里的"链接"是一个虚拟概念,它不一定基于TCP,也即"链接"的两端可以只存在逻辑链接而并没有网络直连。
gayrpc依赖以下库:
注意,请务必根据你的构建系统中的protoc版本对gayrpc_meta.proto和gayrpc_option.proto预先生成代码,这需要你在 src目录里执行:
protoc --cpp_out=. ./gayrpc/core/gayrpc_meta.proto ./gayrpc/core/gayrpc_option.proto
Windows下可以安装vcpkg,然后直接使用Visual Studio 2019/2022打开本代码仓库目录,并在VS 的CMake设置里勾选“从不使用CMake预设”(位置在"选项"-"CMake"-"CMake配置文件"),即不使用仓库目录中的CMakePresets文件!然后再进行生成CMake工程。
Linux下则请按照vcpkg文档进行构建。
gayrpc使用protobuf定义服务,并需要使用代码生成工具根据protobuf服务文件生成对应的代码。生成工具的地址是:https://github.com/IronsDu/protoc-gen-gayrpc
,它由liuhan编写完成。
首先将插件程序放到系统 PATH路径下(比如Linux下的/usr/bin),然后执行代码生成,比如(在具体的服务目录里,比如gayrpc/examples/echo/pb
):
protoc -I. -I../../../src --cpp_out=. echo_service.proto
protoc -I. -I../../../src --gayrpc_out=. echo_service.proto
https://github.com/IronsDu/gayrpc/tree/master/examples
Latency(single threaded):
connection num:1000
cost 16491 ms for 3000000 requests
throughput(TPS):187500
mean:5 ms, 5274983 ns
median:5 ms, 5132642 ns
max:57 ms, 57525470 ns
min:0 ms, 17231 ns
p99:19 ms, 19198630 ns
Throughput(two threaded):
Ubuntu 18.04 (i5 CPU)下,echo 300k QPS,当并发echo时 1000K QPS.
目前实现的RPC通信协议底层采用两层协议.(注意!RPC核心库并不依赖具体通信协议!) 第一层采用二进制协议,且字节序统一为大端. 通信格式如下:
[data_len | op | data]
字段解释:
data_len : uint64_t;
op : uint32_t;
data : char[data_len];
当op
值为1时表示RPC消息,此为第二层协议!这时第一层协议中的data的内存布局则为:
[meta_size | data_size | meta | data]
字段解释:
meta_size : uint32_t;
data_size : uint64_t;
meta : char[meta_size];
data : char[data_size];
其中meta
为 RpcMata
的binary.data
为某业务上的Protobuf Request或Response类型对象的binary或JSON.
RpcMata
的proto定义如下:
syntax = "proto3";
package gayrpc.core;
message RpcMeta {
enum Type {
REQUEST = 0;
RESPONSE = 1;
};
enum DataEncodingType {
BINARY = 0;
JSON = 1;
};
message Request {
// 请求的服务函数
uint64 method = 1;
// 请求方是否期待服务方返回response
bool expect_response = 2;
// 请求方的序号ID
uint64 sequence_id = 3;
};
message Response {
// 请求方的序号ID
uint64 sequence_id = 1;
// 执行是否成功
bool failed = 2;
// (当failed为true)错误码
int32 error_code = 3;
// (当failed为true)错误原因
string reason = 4;
};
// Rpc类型(请求、回应)
Type type = 1;
// RpcData的编码方式
DataEncodingType encoding = 2;
// 请求元信息
Request request_info = 3;
// 回应元信息
Response response_info = 4;
}
以下面的服务定义为例:
syntax = "proto3";
package dodo.test;
message EchoRequest {
string message = 1;
}
message EchoResponse {
string message = 1;
}
service EchoServer {
rpc Echo(EchoRequest) returns(EchoResponse){
option (gayrpc.core.message_id)= 1 ;//设定消息ID,也就是rpc协议中request_info的method
};
}
data
作为第二层协议数据,反序列化其中的meta
作为RpcMeta
对象RpcMata
中的type
REQUEST
则根据request_info
中的元信息调用method
所对应的服务函数.
此时第二层协议中的data
则为服务函数的请求请求对象(比如EchoRequest
).RESPONSE
则根据response_info
中的元信息调用sequence_id
对应的回调函数.
此时第二层协议中的data
则为服务方返回的Response(比如EchoResponse
)以client->echo(echoRequest, responseCallback)
为例
参考代码:GayRpcClient.h
data
meta
以replyObj->reply(echoResponse)
为例
参考代码:GayRpcReply.h
data
meta
https://github.com/IronsDu/gayrpc/tree/master/src/gayrpc/protocol