From 9a9e78bb5d6f5b6d545cd85a5b1891817c556eb8 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 16 Oct 2025 10:17:49 +0800 Subject: [PATCH] update readme --- .gitignore | 6 +- CMakeLists.txt | 5 +- README.md | 247 +++++++++++---------- doc/单机上rest_rpc和brpc性能测试.md | 330 ---------------------------- doc/单线程.png | Bin 15514 -> 0 bytes doc/多线程1k.png | Bin 14365 -> 0 bytes doc/多线程64k.png | Bin 14853 -> 0 bytes examples/CMakeLists.txt | 10 + examples/client.cpp | 59 +++++ examples/server.cpp | 97 ++++++++ include/rest_rpc.hpp | 4 +- include/rest_rpc/rpc_client.hpp | 15 +- include/rest_rpc/use_asio.hpp | 20 -- 13 files changed, 314 insertions(+), 479 deletions(-) delete mode 100644 doc/单机上rest_rpc和brpc性能测试.md delete mode 100644 doc/单线程.png delete mode 100644 doc/多线程1k.png delete mode 100644 doc/多线程64k.png create mode 100644 examples/CMakeLists.txt create mode 100644 examples/client.cpp create mode 100644 examples/server.cpp diff --git a/.gitignore b/.gitignore index 63b31b2..0823d05 100644 --- a/.gitignore +++ b/.gitignore @@ -52,4 +52,8 @@ build/ **.user x64 -.cache \ No newline at end of file +.cache +# for clion +cmake-build-debug +cmake-build-release +cmake-build-debug-coverage \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d3991d..4b3a43b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,4 +11,7 @@ include(cmake/develop.cmake) if (BUILD_UNIT_TESTS) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests) -endif () \ No newline at end of file +endif () +if (BUILD_EXAMPLES) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples) +endif () diff --git a/README.md b/README.md index 8c35c41..9929adc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ | 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++11, high performance, cross platform, easy to use rpc framework. +c++20, high performance, cross platform, easy to use rpc framework. It's so easy to love RPC. @@ -15,7 +15,7 @@ Modern C++开发的RPC库就是这么简单好用! # rest_rpc简介 -rest_rpc是一个高性能、易用、跨平台、header only的c++11 rpc库,它的目标是让tcp通信变得非常简单易用,即使不懂网络通信的人也可以直接使用它。它依赖header-only的standalone [asio](https://github.com/chriskohlhoff/asio)(tag:asio-1-36-0) +rest_rpc是一个高性能、易用、跨平台、header only的基于c++20 协程的rpc 库,它的目标是让tcp通信变得非常简单易用,即使不懂网络通信的人也可以直接使用它。它依赖header-only的standalone [asio](https://github.com/chriskohlhoff/asio)(tag:asio-1-36-0) 可以快速上手,使用者只需要关注自己的业务逻辑即可。 @@ -36,29 +36,36 @@ rest_rpc为用户提供了非常简单易用的接口,几行代码就可以实 ``` //服务端注册加法rpc服务 -struct dummy{ - int add(rpc_conn conn, int a, int b) { return a + b; } -}; +int add(rpc_conn conn, int a, int b) { return a + b; } int main(){ - rpc_server server(9000, std::thread::hardware_concurrency()); + rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency()); - dummy d; - server.register_handler("add", &dummy::add, &d); - - server.run(); + server.register_handler(); + + server.start(); } ``` ``` //客户端调用加法的rpc服务 int main(){ - rpc_client client("127.0.0.1", 9000); - client.connect(); - - int result = client.call("add", 1, 2); - - client.run(); + auto rpc_call = []() -> asio::awaitable { + rpc_client client; + auto ec = co_await client.connect("127.0.0.1:9004"); + if(ec) { + REST_LOG_ERROR << ec0.message(); + co_return; + } + + auto r = co_await client.call(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()); } ``` @@ -69,133 +76,134 @@ int main(){ //1.先定义person对象 struct person { - int id; - std::string name; - int age; - - MSGPACK_DEFINE(id, name, age); + int id; + std::string name; + int age; }; //2.提供并服务 -person get_person(rpc_conn conn) { - return { 1, "tom", 20 }; +person get_person(person p) { + p.name = "jack"; + return p; } int main(){ - //... - server.register_handler("get_person", get_person); + rpc_server server("127.0.0.1:9004", std::thread::hardware_concurrency()); + server.register_handler(); + server.start(); } ``` ``` //客户端调用获取person对象的rpc服务 int main(){ - rpc_client client("127.0.0.1", 9000); - client.connect(); - - person result = client.call("get_person"); - std::cout << result.name << std::endl; - - client.run(); -} -``` - -## 酷 - -异步? - -同步? - -future? - -callback? - -当初为了提供什么样的接口在社区群里还争论了一番,有人希望提供callback接口,有人希望提供future接口,最后我 -决定都提供,专治强迫症患者:) - -现在想要的这些接口都给你提供了,你想用什么类型的接口就用什么类型的接口,够酷吧,让我们来看看怎么用这些接口吧: - -``` -//服务端提供echo服务 -std::string echo(rpc_conn conn, const std::string& src) { - return src; -} - -server.register_handler("echo", echo); -``` - -客户端同步接口 - -``` -auto result = client.call("echo", "hello"); -``` - -客户端异步回调接口 - -``` -client.async_call("echo", [](asio::error_code ec, R data){ - std::cout << "echo " << data << '\n'; -}); -``` - -## async_call接口说明 -有两个重载的async_call接口,一个是返回future的接口,一个是带超时的异步接口。 - -返回future的async_call接口: -``` -std::future future = client.async_call("echo", "purecpp"); -``` - -带超时的异步回调接口: -``` -async_call("some_rpc_service_name", callback, service_args...); -``` - -如果不显式设置超时时间的话,则会用默认的5s超时. -``` -async_call("some_rpc_service_name", callback, args...); -``` - -``` -client.async_call("echo", [](asio::error_code ec, std::string result) { - if (ec) { - std::cout << ec.message() <<" "<< data << "\n"; - return; + auto rpc_call = []() -> asio::awaitable { + rpc_client client; + auto ec = co_await client.connect("127.0.0.1:9004"); + if(ec) { + REST_LOG_ERROR << ec0.message(); + co_return; } - - std::cout << result << " async\n"; -}, "purecpp"); -``` - -客户端异步future接口 - -``` -auto future = client->async_call("echo", "hello"); -auto status = future.wait_for(std::chrono::seconds(2)); -if (status == std::future_status::timeout) { - std::cout << "timeout\n"; -} -else if (status == std::future_status::ready) { - auto str = future.get().as(); - std::cout << "echo " << str << '\n'; + + person p{1, "tom", 20}; + auto r = co_await client.call(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还支持了订阅发布的功能,这是目前很多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 { + std::string str; + while (true) { + std::cin >> str; + if(str == "quit") { + break; + } + + co_await server.publish("topic1", str);// 向客户端发布一个string,你也可以发布一个对象,内部会自动序列化 + } + }; + + sync_wait(get_global_executor(), pub()); +} -https://github.com/qicosmos/rest_rpc/blob/master/examples/server/main.cpp#L121 -https://github.com/qicosmos/rest_rpc/blob/master/examples/client/main.cpp#L383 +client 端代码: +```cpp +void subscribe() { + REST_LOG_INFO << "will subscribe, waiting for publish"; + auto sub = [&]() -> asio::awaitable { + 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("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/blob/master/doc/%E5%8D%95%E6%9C%BA%E4%B8%8Arest_rpc%E5%92%8Cbrpc%E6%80%A7%E8%83%BD%E6%B5%8B%E8%AF%95.md +https://github.com/qicosmos/rest_rpc/tree/master/tests/bench.cpp +## 使用自己的序列化库 +rest_rpc 默认使用yalantinglibs的struct_pack 去做系列化/反序列化的,它的性能非常好。 + +rest_rpc 也支持用户使用自己的序列化库,只需要去实现一个序列化和一个反序列化函数。 +```cpp + namespace user_codec { + // adl lookup in user_codec namespace + template + 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)...)); + } else { + msgpack::pack(buffer, std::forward(args)...); + } + + return std::string(buffer.data(), buffer.size()); + } + + template 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(); + } catch (...) { + return T{}; + } + } + } // namespace user_codec +``` +实现这两个函数之后rest_rpc 将会使用自定义的序列化/反序列化函数了。 # rest_rpc的更多用法 @@ -203,9 +211,6 @@ https://github.com/qicosmos/rest_rpc/blob/master/doc/%E5%8D%95%E6%9C%BA%E4%B8%8A https://github.com/qicosmos/rest_rpc/tree/master/examples -# future - -make an IDL tool to genrate the client code. ## 社区和群 purecpp.cn diff --git a/doc/单机上rest_rpc和brpc性能测试.md b/doc/单机上rest_rpc和brpc性能测试.md deleted file mode 100644 index 590c2dc..0000000 --- a/doc/单机上rest_rpc和brpc性能测试.md +++ /dev/null @@ -1,330 +0,0 @@ -单机上[rest_rpc](https://github.com/qicosmos/rest_rpc "rest_rpc")和[brpc](https://github.com/apache/incubator-brpc "brpc")性能测试 - -# 测试环境 - - | 软硬件环境 | 参数 | - | -------- | :-----: | - | OS | 18.04.1-Ubuntu | - | CPU | 6Core,Intel(R) Core(TM) i5-9400F CPU @ 2.90GHz | - | 内存 | 16G | - | g++版本 | g++ (Ubuntu 8.2.0-1ubuntu2~18.04) 8.2.0 | - | 机器类别 | 实体机 | - -# 测试方法 - -1. server与client均为单进程并且部署在同一台实体机; -2. server开启多线程处理,线程数是CPU核数; -3. client是开启单线程与多线程分别测试性能; -4. client循环向server发送一定字节的字符串,比如由‘A’构成的1K字符串,server收到后不做业务处理,立即返回给client,client收到server返回的字符串也不做业务处理; -5. 当循环结束后,client统计延时; -6. server每隔1s会向标准输出设备打印出收到的请求数; -7. 采用rest_rpc与brpc默认的编译方式(cmake -DCMAKE_BUILD_TYPE=Release)。 -8. rest_rpc采用高效模式与brpc进行对比测试。 - -# 测试代码编写及分析 - -## rest_rpc测试代码 - -1. 下载源码:https://github.com/qicosmos/rest_rpc -2. 解压后进入examples/server目录,修改main.cpp,增加函数: -``` -std::string echo(connection* conn, const std::string& orignal) { - g_qps.increase(); - return orignal; -} -``` -3. echo函数将客户端从conn连接传过来的字符串original原封不动得返回,并调用increase函数进行请求计数。 -在main函数增加 - -``` -server.register_handler("echo", echo); -``` - -用于向server注册消息处理函数。 - -4. examples/client目录,修改main.cpp,client为单线程模式: - -在test_performance1()函数中进行请求发送: -``` -void test_performance1() { - rpc_client client("127.0.0.1", 9000); - bool r = client.connect(); - if (!r) { - std::cout << "connect timeout" << std::endl; - return; - } - string str(1024, 'A'); - auto begin = high_resolution_clock::now(); - for (size_t i = 0; i < LOOP; i++) { - auto future = client.async_call("echo", str); - auto status = future.wait_for(std::chrono::seconds(2)); - if (status == std::future_status::deferred) { - std::cout << "deferred\n"; - } - else if (status == std::future_status::timeout) { - std::cout << "timeout\n"; - } - else if (status == std::future_status::ready) { - } - } - auto end = high_resolution_clock::now(); - cout << "elapse time = " << duration_cast(end - begin).count() << "s" << endl; - std::cout << "finish\n"; -} -``` - -client为多线程模式: -``` -void test_performance1() { - vectorclients; - auto begin = high_resolution_clock::now(); - for (int i = 0;i < THREADNUM;i++) - { - clients.emplace_back(thread(client_thread)); - } - - for (auto &it : clients) - { - it.join(); - } - auto end = high_resolution_clock::now(); - cout << "elapse time = " << duration_cast(end - begin).count() << "s" << endl; - cout << "finish\n"; -} -``` - -定义循环次数为: static const int LOOP = 100000; -定义线程数:const static int THREADNUM = 6; -编写client_thread: - -## brpc测试代码 - -1. 下载源码:https://github.com/apache/incubator-brpc.git -2. 按官网编译指令下载相应包后进行编译 - -进入目录examples/echo_c++,修改server.cpp,在 -``` -virtual void Echo(google::protobuf::RpcController* cntl_base, - const EchoRequest* request, EchoResponse* response, google::protobuf::Closure* done) -注释相应LOG日志: - virtual void Echo(google::protobuf::RpcController* cntl_base, - const EchoRequest* request, - EchoResponse* response, - google::protobuf::Closure* done) { - // This object helps you to call done->Run() in RAII style. If you need - // to process the request asynchronously, pass done_guard.release(). - brpc::ClosureGuard done_guard(done); - - brpc::Controller* cntl = - static_cast(cntl_base); - - // The purpose of following logs is to help you to understand - // how clients interact with servers more intuitively. You should - // remove these logs in performance-sensitive servers. - /*LOG(INFO) << "Received request[log_id=" << cntl->log_id() - << "] from " << cntl->remote_side() - << " to " << cntl->local_side() - << ": " << request->message() - << " (attached=" << cntl->request_attachment() << ")";*/ - - // Fill response. - response->set_message(request->message()); - g_qps.increase(); - // You can compress the response by setting Controller, but be aware - // that compression may be costly, evaluate before turning on. - // cntl->set_response_compress_type(brpc::COMPRESS_TYPE_GZIP); - - if (FLAGS_echo_attachment) { - // Set attachment which is wired to network directly instead of - // being serialized into protobuf messages. - cntl->response_attachment().append(cntl->request_attachment()); - } -} -``` -代码行response->set_message(request->message());将将客户端的请求原封不动发回客户端。 - -3. 修改client.cpp,单线程模式: -``` -const static size_t BUF_SIZE = 1024; -const static int LOOP = 1000000; -char buf[BUF_SIZE] = "" -修改main.函数:: -int main(int argc, char* argv[]) { - memset(buf, 'A', BUF_SIZE); - // Parse gflags. We recommend you to use gflags as well. - GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true); - - // A Channel represents a communication line to a Server. Notice that - // Channel is thread-safe and can be shared by all threads in your program. - brpc::Channel channel; - - // Initialize the channel, NULL means using default options. - brpc::ChannelOptions options; - - options.protocol = FLAGS_protocol; - options.connection_type = FLAGS_connection_type; - options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/; - options.max_retry = FLAGS_max_retry; - if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) { - LOG(ERROR) << "Fail to initialize channel"; - return -1; - } - - // Normally, you should not call a Channel directly, but instead construct - // a stub Service wrapping it. stub can be shared by all threads as well. - example::EchoService_Stub stub(&channel); - - // Send a request and wait for the response every 1 second. - //int log_id = 0; - - example::EchoRequest request; - example::EchoResponse response; - request.set_message(buf); - auto begin = high_resolution_clock::now(); - for (int i = 0; i < LOOP; i++) - { - brpc::Controller cntl; - cntl.request_attachment().append(FLAGS_attachment); - stub.Echo(&cntl, &request, &response, NULL); - if (!cntl.Failed()) { - /*LOG(INFO) << "Received response from " << cntl.remote_side() - << " to " << cntl.local_side() - << ": " << response.message() << " (attached=" - << cntl.response_attachment() << ")" - << " latency=" << cntl.latency_us() << "us";*/ - } - else { - LOG(WARNING) << cntl.ErrorText(); - } - } - auto end = high_resolution_clock::now(); - cout << "elapse time = " << duration_cast(end - begin).count() << "s" << endl; - std::cout << "finish\n"; - //LOG(INFO) << "EchoClient is going to quit"; - return 0; -} -``` - -去除日志,并统计循环发送延迟,在循环体内只是向server发送报文,并未执行任何业务。 - -多线程模式 - -``` -#include -#include -#include -#include -#include -#include -#include -#include "echo.pb.h" -using namespace std; -using namespace chrono; -DEFINE_string(attachment, "", "Carry this along with requests"); -DEFINE_string(protocol, "baidu_std", "Protocol type. Defined in src/brpc/options.proto"); -DEFINE_string(connection_type, "", "Connection type. Available values: single, pooled, short"); -//DEFINE_string(server, "0.0.0.0:8000", "IP Address of server"); -DEFINE_string(server, "127.0.0.1:9000", "IP Address of server"); -DEFINE_string(load_balancer, "", "The algorithm for load balancing"); -DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds"); -DEFINE_int32(max_retry, 3, "Max retries(not including the first RPC)"); -DEFINE_int32(interval_ms, 1000, "Milliseconds between consecutive requests"); -//const static size_t BUF_SIZE = 1024 * 8 * 2 * 4; -const static size_t BUF_SIZE = 1024; -const static int LOOP = 100000; -const static int THREADNUM = 20; -char buf[BUF_SIZE] = ""; -void client_thread() -{ - brpc::Channel channel; - brpc::ChannelOptions options; - - options.protocol = FLAGS_protocol; - options.connection_type = FLAGS_connection_type; - options.timeout_ms = FLAGS_timeout_ms/*milliseconds*/; - options.max_retry = FLAGS_max_retry; - if (channel.Init(FLAGS_server.c_str(), FLAGS_load_balancer.c_str(), &options) != 0) { - LOG(ERROR) << "Fail to initialize channel"; - return; - } - example::EchoService_Stub stub(&channel); - example::EchoRequest request; - example::EchoResponse response; - request.set_message(buf); - for (int i = 0; i < LOOP; i++) - { - brpc::Controller cntl; - cntl.request_attachment().append(FLAGS_attachment); - stub.Echo(&cntl, &request, &response, NULL); - if (!cntl.Failed()) { - } - else { - LOG(WARNING) << cntl.ErrorText(); - } - } -} -int main(int argc, char* argv[]) { - memset(buf, 'A', BUF_SIZE); - GFLAGS_NS::ParseCommandLineFlags(&argc, &argv, true); - vectorclients; - auto begin = high_resolution_clock::now(); - for (int i = 0;i < THREADNUM;i++) - { - clients.emplace_back(thread(client_thread)); - } - for (auto &it : clients) - { - it.join(); - } - auto end = high_resolution_clock::now(); - cout << "elapse time = " << duration_cast(end - begin).count() << "s" << endl; - cout << "finish\n"; -} -``` - -# 测试结果 - -## brpc和grpc的[测试结果](https://github.com/apache/incubator-brpc/blob/master/docs/cn/benchmark.md "测试结果") - - ![alt](https://github.com/apache/incubator-brpc/blob/master/docs/images/qps_vs_threadnum.png "多线程") - -## rest_rpc和brpc的测试结果 - -本次测试使用相同环境与方法对rest_rpc与brpc进行性能测试,最终测试数据取多次测试数据的平均值,测试数据如下: - -**client使用单进程单线程测试模式** - - | 消息字节 | QPS(rest_rpc) | QPS(brpc) | - | -------- | :-----: | :-----: | - | 1K | 52631 | 41667 | - | 8K | 42863 | 34483 | - | 16K | 37037 | 29412 | - | 64K | 16949 | 8982 | - - ![alt](https://github.com/qicosmos/rest_rpc/blob/master/doc/%E5%8D%95%E7%BA%BF%E7%A8%8B.png "单线程") - -从相关数据可以看出,rest_rpc QPS要高于brpc,特别是当消息字节达到64K时,rest QPS几乎是brpc的2倍。 - -**client多线程测试1K消息(rest_rpc采用高效模式)** - - | 线程数 | QPS(rest_rpc) | QPS(brpc) | - | -------- | :-----: | :-----: | - | 6 | 200000 | 103448 | - | 10 | 250000 | 166667 | - | 20 | 285714 | 240963 | - - ![alt](https://github.com/qicosmos/rest_rpc/blob/master/doc/%E5%A4%9A%E7%BA%BF%E7%A8%8B1k.png "多线程1k") - -**client多线程测试64K消息(rest_rpc采用高效模式)** - - | 线程数 | QPS(rest_rpc) | QPS(brpc) | - | -------- | :-----: | :-----: | - | 6 | 60000 | 34883 | - | 10 | 76923 | 40000 | - | 20 | 59405 | 35242 | - - ![alt](https://github.com/qicosmos/rest_rpc/blob/master/doc/%E5%A4%9A%E7%BA%BF%E7%A8%8B64k.png "多线程64k") - - - - diff --git a/doc/单线程.png b/doc/单线程.png deleted file mode 100644 index 5233d7ea78f1632e6526fc1cb4c81e3a9c12e2b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15514 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 diff --git a/doc/多线程1k.png b/doc/多线程1k.png deleted file mode 100644 index b506331bf11dd6708dc09526dfdf7dc10ca5974c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14365 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 diff --git a/doc/多线程64k.png b/doc/多线程64k.png deleted file mode 100644 index c6eba97f1a47ae6ad12a5763ba926d85449fdb66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14853 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 +using namespace rest_rpc; + +// Only need a declaration. +std::string_view echo(std::string_view str); +int add(int a, int b); + +void basic_usage() { + auto rpc_call = []() -> asio::awaitable { + rpc_client client; + auto ec0 = co_await client.connect("127.0.0.1:9004"); + if(ec0) { + REST_LOG_ERROR << ec0.message(); + co_return; + } + + auto r = co_await client.call("test"); + if(r.ec==rpc_errc::ok) { + REST_LOG_INFO << "call result: " << r.value; + assert(r.value == "test"); + } + + auto r1 = co_await client.call(1, 2); + if(r1.ec==rpc_errc::ok) { + REST_LOG_INFO << "call result: " << r1.value; + assert(r1.value == 3); + } + }; + + sync_wait(get_global_executor(), rpc_call()); +} + +void subscribe() { + REST_LOG_INFO << "will subscribe, waiting for publish"; + auto sub = [&]() -> asio::awaitable { + rpc_client client; + co_await client.connect("127.0.0.1:9004"); + while (true) { + auto [ec, result] = co_await client.subscribe("topic1"); + if (ec != rpc_errc::ok) { + REST_LOG_ERROR << "subscribe failed: " << make_error_code(ec).message(); + break; + } + + if (result == "close") { + co_return; + } + + REST_LOG_INFO << result; + } + }; + + sync_wait(get_global_executor(), sub()); +} + +int main() { + basic_usage(); + subscribe(); +} \ No newline at end of file diff --git a/examples/server.cpp b/examples/server.cpp new file mode 100644 index 0000000..9f88639 --- /dev/null +++ b/examples/server.cpp @@ -0,0 +1,97 @@ +#include +using namespace rest_rpc; + +std::string_view echo(std::string_view str) { + return str; +} + +struct dummy{ + int add(int a, int b) { return a + b; } +}; +int add(int a, int b) { return a + b; } + +struct person { + int id; + std::string name; + int age; +}; + +person get_person(person p) { + p.name = "jack"; + return p; +} + +void basict_usage() { + rpc_server server("127.0.0.1:9004", 4); + server.register_handler(); + + dummy d{}; + server.register_handler<&dummy::add>(&d); + + server.register_handler(); + + auto ec = server.async_start(); + if(ec) { + REST_LOG_ERROR << ec.message(); + return; + } + + auto rpc_call = []() -> asio::awaitable { + rpc_client client; + auto ec0 = co_await client.connect("127.0.0.1:9004"); + if(ec0) { + REST_LOG_ERROR << ec0.message(); + co_return; + } + + auto r = co_await client.call("test"); + if(r.ec==rpc_errc::ok) { + REST_LOG_INFO << "call result: " << r.value; + assert(r.value == "test"); + } + + auto r1 = co_await client.call<&dummy::add>(1, 2); + if(r1.ec==rpc_errc::ok) { + REST_LOG_INFO << "call result: " << r1.value; + assert(r1.value == 3); + } + + person p{1, "tom", 20}; + auto r2 = co_await client.call(p); + if(r2.ec==rpc_errc::ok) { + REST_LOG_INFO << "call result: " << r2.value.name; + assert(r2.value.name == "jack"); + } + }; + + sync_wait(get_global_executor(), rpc_call()); +} + +void publish() { + rpc_server server("127.0.0.1:9004", 4); + server.register_handler(); + server.register_handler(); + server.async_start(); + + REST_LOG_INFO << "will pubish, waiting for input"; + auto pub = [&]() -> asio::awaitable { + std::string str; + while (true) { + std::cin >> str; + if(str == "quit") { + break; + } + + co_await server.publish("topic1", str); + } + }; + + sync_wait(get_global_executor(), pub()); + server.stop(); +} + +int main() { + basict_usage(); + + publish(); +} \ No newline at end of file diff --git a/include/rest_rpc.hpp b/include/rest_rpc.hpp index cd998b6..2392021 100644 --- a/include/rest_rpc.hpp +++ b/include/rest_rpc.hpp @@ -1,2 +1,2 @@ -#include "rest_rpc/rest_rpc_client.hpp" -#include "rest_rpc/rest_rpc_server.h" \ No newline at end of file +#include "rest_rpc/rpc_client.hpp" +#include "rest_rpc/rpc_server.hpp" \ No newline at end of file diff --git a/include/rest_rpc/rpc_client.hpp b/include/rest_rpc/rpc_client.hpp index 73c6d9d..e319d3d 100644 --- a/include/rest_rpc/rpc_client.hpp +++ b/include/rest_rpc/rpc_client.hpp @@ -20,9 +20,7 @@ template struct call_result { R value; }; -template <> struct call_result { - rpc_errc ec; -}; +template <> struct call_result { rpc_errc ec; }; class rpc_client { public: @@ -218,12 +216,14 @@ private: socket_->impl_, asio::buffer(&resp_header, sizeof(rest_rpc_header)), asio::as_tuple(asio::use_awaitable)); if (ec) { - result.ec = rpc_errc::write_error; + result.ec = rpc_errc::read_error; close_socket(*socket_); + comple_all(); co_return result; } if (resp_header.magic != 39) { result.ec = rpc_errc::protocol_error; + comple_all(); co_return result; } @@ -240,6 +240,7 @@ private: REST_LOG_WARNING << "read body error: " << ec.message(); result.ec = rpc_errc::read_error; close_socket(*socket_); + comple_all(); co_return result; } result.ec = (rpc_errc)socket_->body_[0]; @@ -257,6 +258,12 @@ private: co_return std::move(result); } + void comple_all() { + for (auto &pair : socket_->sub_ops_) { + pair.second.complete(false); + } + } + asio::awaitable watchdog(auto duration) { asio::steady_timer timer(socket_->get_executor()); timer.expires_after(duration); diff --git a/include/rest_rpc/use_asio.hpp b/include/rest_rpc/use_asio.hpp index 16ffd6b..6f03553 100644 --- a/include/rest_rpc/use_asio.hpp +++ b/include/rest_rpc/use_asio.hpp @@ -18,23 +18,3 @@ using ssl_socket = asio::ssl::stream; #endif #include - -#ifdef CINATRA_ENABLE_SSL -#if __cplusplus > 201402L -#if defined(__GNUC__) -#if __GNUC__ < 8 -#include -namespace rpcfs = std::experimental::filesystem; -#else -#include -namespace rpcfs = std::filesystem; -#endif -#else -#include -namespace rpcfs = boost::filesystem; -#endif -#else -#include -namespace rpcfs = boost::filesystem; -#endif -#endif