Files
qicosmos eebf050c2f update
2025-10-16 11:41:21 +08:00

241 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# rest_rpc
| OS (Compiler Version) | Status |
|------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| Ubuntu 22.04 (clang 14.0.0) | ![win](https://github.com/qicosmos/rest_rpc/actions/workflows/linux_clang.yml/badge.svg?branch=master) |
| Ubuntu 22.04 (gcc 11.2.0) | ![win](https://github.com/qicosmos/rest_rpc/actions/workflows/linux_gcc.yml/badge.svg?branch=master) |
| macOS Monterey 12 (AppleClang 14.0.0.14000029) | ![win](https://github.com/qicosmos/rest_rpc/actions/workflows/mac.yml/badge.svg?branch=master) |
| Windows Server 2022 (MSVC 19.33.31630.0) | ![win](https://github.com/qicosmos/rest_rpc/actions/workflows/windows.yml/badge.svg?branch=master) |
c++20, high performance, cross platform, easy to use rpc framework.
It's so easy to love RPC.
Modern C++开发的RPC库就是这么简单好用!
# rest_rpc简介
rest_rpc是一个高性能、易用、跨平台、header only的基于c++20 协程的rpc 库,它的目标是让tcp通信变得非常简单易用,即使不懂网络通信的人也可以直接使用它。它依赖header-only的standalone [asio](https://github.com/chriskohlhoff/asio)(tag:asio-1-36-0)
可以快速上手,使用者只需要关注自己的业务逻辑即可。
# 编译器版本
需要完全支持c++20 的编译器:
gcc12+, clang15+, msvc2022
# 谁在用rest_rpc
1. 博世汽车
[在这里增加用户](https://github.com/qicosmos/rest_rpc/wiki/%E4%BD%BF%E7%94%A8rest_rpc%E7%9A%84%E7%94%A8%E6%88%B7%E5%88%97%E8%A1%A8)
# rest_rpc的特点
## 易
rest_rpc为用户提供了非常简单易用的接口,几行代码就可以实现rpc通信了,来看第一个例子
### 一个加法的rpc服务
```
//服务端注册加法rpc服务
int add(rpc_conn conn, int a, int b) { return a + b; }
int main(){
rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency());
server.register_handler<add>();
server.start();
}
```
```
//客户端调用加法的rpc服务
int main(){
auto rpc_call = []() -> asio::awaitable<void> {
rpc_client client;
auto ec = co_await client.connect("127.0.0.1:9004");
if(ec) {
REST_LOG_ERROR << ec.message();
co_return;
}
auto r = co_await client.call<add>(1, 2);
if(r.ec==rpc_errc::ok) {
REST_LOG_INFO << "call result: " << r.value;
assert(r.value == 3);
}
};
sync_wait(get_global_executor(), rpc_call());
}
```
### 获取一个对象的rpc服务
```
//服务端注册获取person的rpc服务
//1.先定义person对象
struct person {
int id;
std::string name;
int age;
};
//2.提供rpc服务
person get_person(person p) {
p.name = "jack";
return p;
}
int main(){
rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency());
server.register_handler<get_person>();
server.start();
}
```
```
//客户端调用获取person对象的rpc服务
int main(){
auto rpc_call = []() -> asio::awaitable<void> {
rpc_client client;
auto ec = co_await client.connect("127.0.0.1:9004");
if(ec) {
REST_LOG_ERROR << ec.message();
co_return;
}
person p{1, "tom", 20};
auto r = co_await client.call<get_person>(p);
if(r.ec==rpc_errc::ok) {
REST_LOG_INFO << "call result: " << r.value.name;
assert(r.value.name == "jack");
}
};
sync_wait(get_global_executor(), rpc_call());
}
```
## 酷
rest_rpc 比较有特色的一个功能是支持订阅发布。
以订阅某个topic为例:
server 端代码:
```cpp
void publish() {
rpc_server server("127.0.0.1:9004", 4);
server.async_start();
REST_LOG_INFO << "will pubish, waiting for input";
auto pub = [&]() -> asio::awaitable<void> {
std::string str;
while (true) {
std::cin >> str;
if(str == "quit") {
break;
}
// 向客户端发布一个string,你也可以发布一个对象,内部会自动序列化
co_await server.publish("topic1", str);
}
};
sync_wait(get_global_executor(), pub());
}
client
```cpp
void subscribe() {
REST_LOG_INFO << "will subscribe, waiting for publish";
auto sub = [&]() -> asio::awaitable<void> {
rpc_client client;
co_await client.connect("127.0.0.1:9004");
while (true) {
// 订阅topic1,库会自动将结果反序列化为std::string, 如果publish是一个person对象,则subscribe参数填person,内部会自动反序列化
auto [ec, result] = co_await client.subscribe<std::string>("topic1");
if (ec != rpc_errc::ok) {
REST_LOG_ERROR << "subscribe failed: " << make_error_code(ec).message();
break;
}
REST_LOG_INFO << result;
}
};
sync_wait(get_global_executor(), sub());
}
```
## 快
rest_rpc是目前最快的rpc库,具体和grpc和brpc做了性能对比测试,rest_rpc性能是最高的,远超grpc。
性能测试代码在这里:
https://github.com/qicosmos/rest_rpc/tree/master/tests/bench.cpp
## 活
rest_rpc 也是比较灵活的,允许用户替换默认的序列化库。
rest_rpc 默认使用yalantinglibs的struct_pack 去做系列化/反序列化的,它的性能非常好。
rest_rpc 也支持用户使用自己的序列化库,只需要去实现一个序列化和一个反序列化函数。
```cpp
namespace user_codec {
// adl lookup in user_codec namespace
template <typename... Args>
std::string serialize(rest_adl_tag, Args &&...args) {
msgpack::sbuffer buffer(2 * 1024);
if constexpr (sizeof...(Args) > 1) {
msgpack::pack(buffer, std::forward_as_tuple(std::forward<Args>(args)...));
} else {
msgpack::pack(buffer, std::forward<Args>(args)...);
}
return std::string(buffer.data(), buffer.size());
}
template <typename T> T deserialize(rest_adl_tag, std::string_view data) {
try {
static msgpack::unpacked msg;
msgpack::unpack(msg, data.data(), data.size());
return msg.get().as<T>();
} catch (...) {
return T{};
}
}
} // namespace user_codec
```
实现这两个函数之后rest_rpc 将会使用自定义的序列化/反序列化函数了。
# rest_rpc的更多用法
rest_rpc 支持零拷贝发数据,以echo函数为例:
```cpp
std::string_view echo(std::string_view str) {
return str;
}
```
假如用户希望发送很大的一个数据,可能有数GB,如果按照常规的做法,需要先序列化,这样就存在内存拷贝,rest_rpc 针对这种场景专门做了优化,当client调用rpc函数时传入的是std::string_view 时,
rest_rpc 将不会对传入的数据做拷贝,也不会去做序列化,直接通过socket发送到服务端。
rpc函数的返回类型为std::string_view 时,client收到的响应数据也不会做反序列化和内存拷贝,直接返回的是收到的socket 数据。
这样就可以实现rpc的零拷贝数据发送了,能获得最佳的性能。
可以参考rest_rpc的example:
https://github.com/qicosmos/rest_rpc/tree/master/examples
## 社区和群
http://purecpp.cn
qq群:546487929