先说一个让很多人沉默的问题

你在简历上写过"熟悉 RPC 框架"吗?

如果面试官接着问:"RPC 调用的完整链路是什么?从客户端发起请求,到服务端返回响应,中间每一步发生了什么?"

能流畅回答的人,大概不到 10%。

能进一步回答"连接池为什么能把 P99 延迟从 10ms 压到 0.2ms"的人,更是凤毛麟角。

这不是他们不努力,而是他们用的学习路径从一开始就走偏了——调过 gRPC,改过 GitHub 上别人的 RPC demo,但从来没有亲手造过一个。

今天这篇文章,就是为想造一个的人写的。

为什么不直接学 gRPC / brpc / srpc?

这个问题我被问过很多次,答案很直接。

gRPC 的核心 C++ 实现超过 50 万行。brpc 也在 10 万行 以上。你打开任何一个文件,迎接你的是层层宏定义、平台兼容代码、协议扩展点……光理清模块依赖关系就要花一周。

更关键的是:这些项目展示的是一个经历了多年打磨的成品

你看不到 RpcChannel 的异步调用链是怎么一步步设计出来的,看不到 protobuf 反射 dispatch 是在哪个阶段接进去的,看不到连接池的 acquire/release 接口为什么要这样设计,更看不到 msgid 透传是怎么从协议层一路贯穿到链路追踪的。

这些"从0到1"的过程,是任何成品代码都给不了你的东西。

XRPC:4700 行,25 天,从空文件夹到完整 RPC 框架

这个项目叫 XRPC,基于我之前实现的 ReactorX 事件驱动引擎和 NetCore 网络库,向上构建完整的 RPC 框架层。

核心代码 4700+ 行,含测试与压测代码总计约 8600 行,25 天完整教学,每天增量实现,每天结束都能编译运行。

先看架构全貌

在这里插入图片描述

先看压测数据

同一台机器,连接池开启,1000 次顺序 RPC 调用:

P50 延迟:  0.061 ms
P95 延迟:  0.163 ms
P99 延迟:  0.226 ms   ← 不到 0.23ms
最大延迟:  0.357 ms
顺序 QPS:  12,821 次/秒

延迟分布:
  <0.1ms ████████████████████████████████████  821次  (82.1%)
  <0.2ms ███████                               162次  (16.2%)
  <0.3ms                                        13次  (1.3%)

扩展性曲线,连接池开启 vs 关闭,并发 32 workers:

并发数  │  无连接池 QPS   P99      │  有连接池 QPS   P99     加速比
────────┼────────────────────────┼───────────────────────────────
  1     │      2,564      1.02ms  │     12,500     0.20ms    4.9x
  4     │      7,692      8.62ms  │     50,000     0.20ms    6.5x
  16    │     12,698     10.18ms  │    123,077     0.21ms    9.7x
  32    │     17,778      7.28ms  │    133,333     0.46ms    7.5x

连接池开启,32 并发时 QPS 达到 13.3 万,P99 仍在 0.5ms 以内。

没有连接池的版本,P99 高达 7~10ms,这就是每次 RPC 新建 TCP 连接的代价——每次都要经历三次握手,在高并发下彻底成为瓶颈。这个对比,是 Day 12 压测专题的核心内容,数据是真实跑出来的,不是估算。

三行启动服务端,五行发起调用

接口设计参考 gRPC 风格,学完之后切换任何主流 RPC 框架几乎无缝衔接:

// ══ 第一步:实现业务方法 ══════════════════════════════
class OrderServiceImpl : public order::Order {
public:
    void makeOrder(google::protobuf::RpcController*,
                   const order::MakeOrderRequest*  req,
                   order::MakeOrderResponse*        rsp,
                   google::protobuf::Closure*       done) override {
        rsp->set_order_id("ORD-" + std::to_string(++seq_));
        rsp->set_total_amount(req->quantity() * 99.0);
        rsp->set_ret_code(0);
        done->Run();   // 框架在此编码响应并回包
    }
private:
    std::atomic<int> seq_{1000};
};

// ══ 第二步:3 行启动 RpcServer ═══════════════════════
EventLoop loop;
RpcServer server(&loop, InetAddress(9090), /*io_threads=*/4);
server.registerService(std::make_shared<OrderServiceImpl>());
server.start();
loop.loop();

// ══ 第三步:客户端异步调用 ══════════════════════════
auto ctrl = std::make_shared<RpcController>();
ctrl->SetTimeout(3000);   // per-call 超时 3s
auto closure = std::make_shared<RpcClosure>([&]() {
    printf("订单号: %s  金额: %.2f 元\n",
           rsp->order_id().c_str(), rsp->total_amount());
});
auto channel = std::make_shared<RpcChannel>(
    &client_loop, InetAddress("127.0.0.1", 9090));
channel->Init(ctrl, req, rsp, closure);
order::Order_Stub stub(channel.get());
stub.makeOrder(ctrl.get(), req.get(), rsp.get(), closure.get());

// ══ 进阶:连接池(零额外代码切换)══════════════════
ConnectionPool pool(InetAddress("127.0.0.1", 9090), /*pool_size=*/8);
pool.start();
auto channel = std::make_shared<RpcChannel>(&pool);  // 只改这一行
// 其余调用代码完全相同,框架自动取用空闲连接并归还

从无池切换到有池,只需改一行——RpcChannel 的构造参数从 EventLoop+地址 换成 ConnectionPool,其余调用代码一字不改。

25 天,你会亲手构建什么

整个教程分三个递进阶段,每一阶段结束都有完整可运行的交付物。

第一阶段:ReactorX 事件驱动引擎(Day 1–5)

一切性能的根基。

  • Day 1 实现 epoll 封装(Poller)和事件分发抽象(Channel),第一次运行时你会看到键盘输入被事件循环捕获——这是事件驱动从概念变成代码的一刻
  • Day 2 构建 EventLoop,实现 One Loop Per Thread 框架,到这天结束你已经能写一个真正跑起来的 echo server
  • Day 3 接入基于 timerfd 的定时器
  • Day 4 实现 eventfd 跨线程唤醒和线程池
  • Day 5 做压测,QPS 可达 50 万+

第二阶段:NetCore 高性能网络库(Day 6–10)

在事件驱动引擎之上,搭建完整的网络抽象层。

  • Day 6 封装 Socket RAII
  • Day 7 实现 Acceptor 连接接受器(含 idleFd 技巧防止 fd 耗尽)
  • Day 8 实现三区域自动扩容 Buffer(readv 系统调用一次读取所有数据)
  • Day 9 是整个网络库最难也最精华的一天——TcpConnection 的四状态机、shared_ptr + weak_ptr + tie 机制防止野指针和提前析构、高水位回调控制发送缓冲
  • Day 10 把所有组件粘合成 Multi-Reactor 架构的 TcpServer,主 Reactor 只接连接,Sub Reactor 池负责 IO,Round-Robin 负载均衡,三十行代码启动支持数万并发的多线程服务器

第三阶段:RocketRPC 框架层(Day 11–25)

更多推荐