不能直接用 std::thread + std::queue 做 RPC 调度,因为线程数爆炸、上下文切换开销大,且无法统一管理超时/重试/取消;需事件驱动非阻塞调度,如 libuv 或 boost.asio。

为什么不能直接用 std::thread + std::queue 做 RPC 调度
因为 JSON 序列化/反序列化本身不耗时,但网络 I/O 和回调执行容易阻塞;若每个请求都起一个 std::thread,连接多时线程数爆炸,上下文切换开销大,且无法统一管理超时、重试、取消。真正需要的是事件驱动的非阻塞调度,不是“多线程模拟异步”。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
libuv或boost.asio做底层 I/O 多路复用,避免自己轮询或阻塞recv - RPC 请求 ID 必须由客户端生成并透传(服务端不能重写),否则并发调用时无法匹配响应
- JSON 中的
"id"字段必须是整数或字符串,不能是对象或数组——很多 JSON 库(如nlohmann::json)对非标类型反序列化会静默失败或抛异常
nlohmann::json 如何安全地解析 RPC 请求体
常见错误是直接 json::parse(buf) 后就访问 ["method"] 或 ["params"],结果遇到 malformed JSON 或字段缺失时程序崩溃。必须做两级校验:语法正确性 + 结构完整性。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 用
try { j = json::parse(buf); } catch (const json::parse_error&) { return error("invalid json"); } - 检查必需字段:
if (!j.is_object() || !j.contains("jsonrpc") || j["jsonrpc"] != "2.0" || !j.contains("method")) -
"params"可为null、对象或数组,需用j.count("params")判断是否存在,再用j["params"].is_array()或.is_object()分支处理
如何让客户端 call() 看起来同步,实际走异步 I/O
用户希望写 auto result = client.call("add", {1, 2});,但底层不能阻塞等待网络响应。核心是把同步语义包装在异步原语之上——用 std::promise + std::future 桥接。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每次
call()创建一个std::promise<json></json>,存入 map:pending_calls_[id] = std::move(promise); - 发送请求后立即返回
promise.get_future(),调用方可.wait()或.get() - 收到响应时,根据响应中的
"id"查 map,调用promise.set_value(json_result);查不到说明超时或已取消,丢弃该响应 - 务必在 promise 设置前加锁(或用
std::atomic标记状态),防止响应和超时清理竞争
服务端如何注册函数并自动绑定参数
手动写 if-else 匹配 method 名再 cast params 很脆弱。可用模板 + 可变参数推导,把 std::function<json></json> 注册进去,运行时完成 JSON → C++ 类型转换。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 定义注册接口:
template<typename func> void register_method(std::string name, Func&& f);</typename> - 内部用
std::map<:string std::function json>> handlers_;</:string> - 参数转换逻辑收在一个私有函数里,例如
json_to_args<args...>(j["params"])</args...>,对数组按序取值,对对象按 key 取值,类型不匹配时返回 error json - 不要尝试支持任意嵌套结构体自动映射——JSON-RPC 2.0 规范只要求
params是数组或对象,过度泛化会让错误信息难以调试
最易被忽略的是超时清理:pending map 不清理会导致内存泄漏,但过早清理又可能丢响应。推荐用 libuv 的 uv_timer_t 或 boost::asio::steady_timer 为每个 pending request 单独设超时,触发时从 map 中 erase 并 set_exception。别依赖全局定时器轮询——精度差,且高并发下遍历 map 成瓶颈。


















