这次升级改动主要有以下几个方面:
1,去掉回调全部换成C++20``协程;
2,基于协程的订阅发布.
3,序化库换成性能更好的yalantinglibs的struct_pack
4,更安全的编译期检查.
5,提升性能
6,支持用户自定义序化
现在来看看最新的rest_rpc的基本用法:
基本用法
cpp
//定义rpc函数
int add(rpc_conn conn, int a, int b) { return a + b; }
std::string_view echo(std::string_view str) { return str; }
int main(){
rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency());
server.register_handler<add>();
server.register_handler<echo>();
server.start();
}
int main(){
auto rpc_call = []() -> asio::awaitable<void> {
rpc_client client;
co_await client.connect("127.0.0.1:9004");
auto r = co_await client.call<add>(1, 2);
assert(r.value == 3);
auto r1 = co_await client.call<echo>("test");
assert(r.value == "test");
};
sync_wait(rpc_call());
}
最简单的rpc服务几行代码就行了.rpc函数也可按协程函数定义.
cpp
asio::awaitable<std::string_view> echo_coro(std::string_view str) {
co_return str;
}
int main(){
rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency());
server.register_handler<echo_coro>();
server.async_start();
auto rpc_call = []() -> asio::awaitable<void> {
rpc_client client;
co_await client.connect("127.0.0.1:9004");
auto r1 = co_await client.call<echo_coro>("test");
assert(r.value == "test");
};
sync_wait(rpc_call());
}
这里使用的是asio协程,也是根据社区同学的建议使用asio协程,因为可很容易于和其它的基于asio协程的mysql,redis等库结合起来.
特色功能
rest_rpc的特色是支持订阅发布.
客户订阅某个主题,服务端向客户发布.
cpp
struct person {
int id;
std::string name;
int age;
};
int main() {
auto sub = [&]() -> asio::awaitable<void> {
rpc_client client;
co_await client.connect("127.0.0.1:9004");
while (true) {
auto [ec, result] = co_await client.subscribe<person>("topic1");
if (ec != rpc_errc::ok) {
REST_LOG_ERROR << "subscribe failed: " << make_error_code(ec).message();
break;
}
assert(result.name == "tom");
}
};
sync_wait(sub());
}
void publish() {
rpc_server server("127.0.0.1:9004", 4);
server.async_start();
auto pub = [&]() -> asio::awaitable<void> {
std::string str;
while (true) {
std::cin >> str;
if (str == "quit") {
break;
}
co_await server.publish("topic1", person{1, "tom", 20});
}
};
sync_wait(pub());
}
自定义序化
rest_rpc默认使用的是yalantinglibs.struct_pack,也可按自己的序化库替换它,替换也很简单,定义两个函数即可.比如想替换成msgpack去序化/反序化.
cpp
namespace user_codec {
template <typename... Args>
std::string serialize(rest_adl_tag, Args &&...args) {
in_user_pack = true;
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 {
in_user_unpack = true;
static msgpack::unpacked msg;
msgpack::unpack(msg, data.data(), data.size());
return msg.get().as<T>();
} catch (...) {
return T{};
}
}
}
//user_codec名间
在user_codec的名字空间下实现序化/反序化函数即可.
零拷贝传输
假如用户希望发送很大的一个数据,可能有数GB,如果按普通的做法,需要先序化,这样就有内存拷贝,rest_rpc针对该场景专门做了优化,
当客户调用rpc函数时传入的是std::string_view时,rest_rpc将不会拷贝传入的数据,也不会序化它,而是直接通过套接字发送到服务端.
rpc函数的返回类型为std::string_view时,客户不会反序化和内存拷贝收到的响应数据,而是直接返回的是收到的套接字数据.
这样就可实现rpc的零拷贝数据发送了,能取得最佳的性能.
性能
rest_rpc的性能比grpc和brpc更好,