{"id":20516381,"url":"https://github.com/chloro-pn/pnrpc","last_synced_at":"2026-06-05T17:31:36.178Z","repository":{"id":120001497,"uuid":"606350212","full_name":"chloro-pn/pnrpc","owner":"chloro-pn","description":"pnrpc is an RPC framework based on asio c++20 coroutines","archived":false,"fork":false,"pushed_at":"2023-04-01T04:52:16.000Z","size":700,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-16T10:05:52.033Z","etag":null,"topics":["asio","cplusplus-20","rpc-framework"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chloro-pn.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-02-25T08:06:56.000Z","updated_at":"2024-05-22T01:27:34.000Z","dependencies_parsed_at":"2023-06-14T16:15:26.582Z","dependency_job_id":null,"html_url":"https://github.com/chloro-pn/pnrpc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chloro-pn%2Fpnrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chloro-pn%2Fpnrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chloro-pn%2Fpnrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chloro-pn%2Fpnrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chloro-pn","download_url":"https://codeload.github.com/chloro-pn/pnrpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242119535,"owners_count":20074760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asio","cplusplus-20","rpc-framework"],"created_at":"2024-11-15T21:28:34.520Z","updated_at":"2025-03-05T23:24:28.405Z","avatar_url":"https://github.com/chloro-pn.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pnrpc\npnrpc is an RPC framework based on asio c++20 coroutines\n\n## features\n* 基于asio c++20 coroutines实现，因此可以无缝支持asio提供的协程基础设施（co_spawn、dispatch、Error Handling、Co-ordinating Parallel Coroutines等等）；\n* 可以将异步操作封装为awaiter并以同步的方式书写代码，例如example/async；\n* 提供了rpc client的协程实现，这意味着你可以在rpc实现函数（是一个coroutine）中发起对其他rpc的请求，而这些都是协程化非阻塞的，例如example/async；\n* 支持多种线程模型：\n    * **单线程模型**：这种模型下accept、每个socket的网络io以及rpc调用都在一个线程中进行；\n    * **accept线程+多个io线程模型**：这种模型下accept单独占一个线程，将接收到的socket分配给多个io线程，每个io线程负责多个socket的网络io以及rpc调用；\n    * **accpet线程+多个io线程+多个rpc调用线程模型**：这种模型和第二种模型的区别在于，io线程只负责socket的网络io，用户可以通过将rpc接口绑定到不同的自定义io_context，这些自定义io_context将负责rpc调用逻辑。（可以将多个rpc接口绑定到一个io_context上，也可以将一个rpc接口绑定到多个io_context上，这是十分自由的）\n* 支持为rpc接口绑定限流策略，例如在example/sum这个接口的实现中绑定了令牌桶的限流策略。\n\n\n## requirement\n* cpp compiler supporting c++20 (like g++-11)\n* bazel 6.0.0\n\n## doc\n\n##### rpc声明\n首先需要通过宏```RPC_DECLARE```来声明rpc接口，以echo为例：\n```c++\nRPC_DECLARE(Echo, std::string, std::string, 0x01, pnrpc::RpcType::Simple, OVERRIDE_PROCESS)\n```\n参数的含义依次为:\n* rpc接口名称（不可重复）\n* 请求参数类型 \n* 回复参数类型\n  请求参数和回复参数类型需要满足如下concept，即除了内置基本类型，用户需要为参数类型指定序列化和反序列化函数：\n  ```c++\n  template \u003ctypename T\u003e\n  concept RpcTypeConcept = requires (T t, const char* ptr, size_t len, std::string\u0026 appender) {\n    { T::create_from_raw_bytes(ptr, len) } -\u003e std::same_as\u003cT\u003e;\n    { T::to_raw_bytes(t, appender) } -\u003e std::same_as\u003cvoid\u003e;\n  } || RpcBasicType\u003cT\u003e;\n  ```\n* 编号（不可重复）\n* rpc类型（一应一答型、客户端流式、服务器流式、双向流式四种类型），如下：\n```c++\nenum class RpcType : uint8_t {\n  Simple,\n  ServerSideStream,\n  ClientSideStream,\n  BidirectStream,\n};\n```\n* 需要定制的功能，有如下选项可以指定（这些选项可以同时指定，使用空格分开即可）：\n  * OVERRIDE_PROCESS 重载rpc处理函数，一般情况这个是必须指定的\n  * OVERRIDE_BIND 重载网络绑定函数，用户可以通过重载这个函数将rpc接口的执行绑定到自定义的io_context上\n  * OVERRIDE_RESTRICTOR 重载限流器函数，用户可以通过重载这个函数为rpc接口绑定限流器\n  * OVERRIDE_REQUEST_LIMIT 设置客户端到服务器方向socket的限流策略，单位字节/秒\n  * OVERRIDE_RESPONSE_LIMIT 设置服务器到客户端方向socket的限流策略，单位字节/秒\n\n##### rpc定义\n声明之后，需要为rpc接口定制的功能提供定义，如果定制了OVERRIDE_PROCESS（参考example/echo的例子）：\n```c++\nasio::awaitable\u003cvoid\u003e RPCEcho::process() {\n  auto request = co_await get_request_arg();\n  co_await set_response_arg(request.value(), true);\n  co_return;\n}\n```\n\n如果定制了OVERRIDE_BIND（参考example/rpc_sleep的例子）：\n```c++\nstd::unique_ptr\u003casio::io_context\u003e global_default_ctx;\n\nasio::io_context* RPCSleep::bind_io_context() {\n  return global_default_ctx.get();\n}\n```\n\n如果定制了OVERRIDE_RESTRICTOR（参考example/sum的例子）：\n```c++\nbool RPCSum::restrictor() {\n  // 设置令牌桶的令牌入桶速率为100/s，容量为10000\n  static TokenBucket tb(100, 100000);\n  return tb.consume(1);\n}\n```\n\n##### server端\n在server端首先通过REGISTER_RPC宏注册rpc接口，然后定义NetServer类型变量并启动server，NetServer的构造函数参数分别是ip、port、io线程个数（如果不指定的话就是单线程模型，accept、每个socket的网络io以及rpc调用都在一个线程中进行）\n```c++\n  REGISTER_RPC(Sum)\n  REGISTER_RPC(Echo)\n  REGISTER_RPC(Sleep)\n  REGISTER_RPC(Async)\n  REGISTER_RPC(SumStream)\n  REGISTER_RPC(Download)\n\n  NetServer ns(\"127.0.0.1\", 44444, 4);\n  std::thread th([\u0026]() {\n    ns.run();\n  });\n```\n\n##### client端\n在声明rpc接口的时候，已经定义了客户端stub类，用户可以通过该类型的变量作为客户端访问对应的rpc，对于不同的rpc类型，客户端stub类提供了不同的接口，下面这个是ClientSideStream类型的例子，用户可以通过send_request函数发送流式数据（第二个参数为eof，设置为true时意味着流式数据传送完毕），然后通过recv_response函数接收回复信息。\n```c++\n  asio::io_context io;\n\n  asio::co_spawn(io, [\u0026io]() -\u003e asio::awaitable\u003cvoid\u003e {\n    RPCSumStreamSTUB sum_client(io, \"127.0.0.1\", 44444);\n    co_await sum_client.async_connect();\n    for(uint32_t x = 0; x \u003c 3; ++x) {\n      co_await sum_client.send_request(x);\n    }\n    co_await sum_client.send_request(3, true);\n    uint32_t resp = 0;\n    int ret_code = co_await sum_client.recv_response(resp);;\n    assert(resp == 0 + 1 + 2 + 3);\n    assert(ret_code == RPC_OK);\n  }, asio::detached);\n```\n除了协程类型的stub接口外，还提供了阻塞式接口，如下：\n```c++\n  RPCEchoSTUB echo_client(io, \"127.0.0.1\", 44444);\n  echo_client.connect();\n  std::string resp;\n  uint32_t ret_code = echo_client.rpc_call(\"helloworld\", resp);\n  assert(ret_code == RPC_OK);\n  assert(resp == \"helloworld\");\n```\n\n## todo\n* 提供多种客户端连接模型（短连接、连接池、socket复用）\n* 完善支持的内置类型\n* 补充单元测试和文档","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchloro-pn%2Fpnrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchloro-pn%2Fpnrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchloro-pn%2Fpnrpc/lists"}