公司动态
2025年C++通信库性能横评:NNG、brpc、tRPC-Cpp与gRPC实战对比
1. 项目概述一次硬核的通信库性能摸底最近在做一个高并发的分布式数据采集项目底层通信框架的选型让我纠结了很久。市面上C的通信库选择不少从老牌的ZeroMQ、Boost.Asio到近几年势头很猛的国产新秀像NNGNanomsg Next Gen、brpc、tRPC-Cpp等每个都说自己性能强悍、易于集成。但官方Benchmark往往是在理想环境下跑出来的跟真实业务场景下的表现可能相差甚远。为了给项目选一个“靠谱”的基石我决定自己动手做一次贴近实战的、多维度的性能对比测试。这次测试的目标很明确找出在2025年典型的中后台服务开发场景下综合表现最优的C通信库。我们不光要看极限吞吐和延迟更要关注内存占用、CPU使用效率、连接管理的稳定性以及在复杂网络条件下的表现。测试会模拟微服务间RPC、实时数据流推送、以及高并发短连接这三种核心场景。参与评测的选手我选取了几个有代表性的NNG作为现代轻量级消息库的代表、brpc百度开源的工业级RPC框架、tRPC-Cpp腾讯开源的RPC框架以及作为基准对照的gRPC虽然非国产但生态和性能是重要参考。整个测试从环境搭建、用例设计、到数据采集和分析都是在一套标准的Linux服务器上完成的所有代码和配置都会开源力求过程可复现结论有依据。2. 测试环境与核心指标设计性能测试最忌讳的就是环境不一致和指标片面。为了确保公平我搭建了一个尽可能纯净且标准的测试环境。2.1 硬件与软件环境配置测试使用了两台同构的物理服务器通过万兆光纤网卡直连以排除交换机带来的不确定性和瓶颈。服务器配置为Intel Xeon Gold 6330 CPU (28核56线程)内存256GB DDR4。操作系统统一为Ubuntu 22.04 LTS内核版本5.15。所有评测库均采用其发布的最新稳定版本截至测试时NNG v1.5.2 brpc v1.6.0 tRPC-Cpp v1.0.3 gRPC v1.59.0。编译器为GCC 11.4开启-O2优化等级。这里有个关键细节所有库都采用静态链接方式编译到测试程序中。这样做有两个好处一是避免动态库加载和版本差异带来的细微影响二是生成的单个可执行文件便于部署和压测。编译命令类似这样# 以brpc为例 g -stdc17 -O2 -pthread -I/path/to/brpc/include -I/path/to/protobuf/include \ -L/path/to/brpc/lib -L/path/to/protobuf/lib \ client.cpp -o client -lbrpc -lprotobuf -lssl -lcrypto -lgflags -lleveldb -lz -static注意静态链接时需要确保所有依赖库如openssl, protobuf, gflags都正确链接否则运行时可能会因缺少符号而崩溃。我在这里踩过坑建议先用ldd检查动态依赖再逐步替换为静态库。2.2 三大核心测试场景与指标定义性能指标不能只看QPS每秒查询数必须多维度综合评估。我设计了三个场景对应不同的业务需求场景一微服务RPC请求-响应模式模拟业务类似用户登录、订单查询等需要同步等待结果的调用。测试方法客户端并发多个线程持续向服务端发送一个包含简单结构体如用户ID、时间戳的请求服务端执行一个轻量级计算如字符串反转后返回。核心指标平均延迟P50, P99, P999衡量响应速度特别是尾部延迟对用户体验的影响。QPS在可接受的延迟范围内如P99 10ms系统能达到的最大吞吐。CPU使用率高QPS下的CPU效率反映库本身的开销。场景二实时数据流推送发布-订阅模式模拟业务实时监控数据广播、股票行情推送、游戏状态同步。测试方法一个发布者多个订阅者。发布者以固定频率如每秒1万条发送数据包测量从发布到所有订阅者接收到的端到端延迟以及不同订阅者数量下的吞吐衰减情况。核心指标端到端延迟分布。吞吐量随订阅者数量变化曲线。内存增长长连接下的内存是否平稳有无泄漏迹象。场景三高并发短连接连接-发送-断开模拟业务HTTP API网关、短时数据上报。测试方法客户端模拟大量短生命周期的连接每个连接只发送一次请求后立即断开。测试每秒能成功完成多少次完整的“建连-通信-断连”生命周期。核心指标连接建立速率。在高压下的连接错误率如连接超时、拒绝。服务端资源回收效率如文件描述符、内存。3. 各通信库核心实现与调优要点在跑分之前必须理解每个库的“脾气秉性”并对其进行合理的配置才能发挥其最大性能。用默认配置跑出来的结果往往没有参考价值。3.1 NNG轻量级与灵活性的权衡NNG是Nanomsg的下一代理念是提供一套简单的Socket-like API来构建多种通信模式。它的核心优势是轻量和零依赖C库部署极其方便。核心实现模式在RPC场景我使用req/rep套接字模式。它的API非常直观// 服务端 nng_listener listener; nng_rep0_open(sock); nng_listener_create(listener, sock, tcp://0.0.0.0:8888); nng_listener_start(listener, 0); // 客户端 nng_req0_open(sock); nng_dial(sock, tcp://server_ip:8888, NULL, 0);关键调优参数NNG_OPT_RECVBUF和NNG_OPT_SENDBUF调整套接字缓冲区大小在网络抖动时能有效平滑吞吐。我设置为1MB。NNG_OPT_RECVMAXSZ设置最大接收消息大小防止恶意超大包。多线程处理NNG套接字本身非线程安全但一个套接字可以从多个线程发送或接收。我采用一个I/O线程池使用nng_aio处理网络事件另一个工作线程池处理业务逻辑通过无锁队列衔接这是发挥其性能的关键。实操心得NNG的文档相对简洁有些高级特性如自定义传输协议需要仔细阅读头文件。它的错误码有时比较晦涩建议封装一个工具函数来将nng_errno转换为可读的字符串便于调试。3.2 brpc工业级功能与性能的集大成者brpc是一个功能丰富的RPC框架内置了服务发现、负载均衡、熔断、追踪等微服务治理能力。它的性能基石是bthread一个M:N的协程库可以在阻塞操作时高效切换用同步的编程模型获得异步的性能。核心实现模式使用Protobuf定义服务接口这是最标准的方式。brpc的Server和Channel封装了几乎所有细节。// 定义proto和服务 service EchoService { rpc Echo(EchoRequest) returns (EchoResponse); } // 实现服务桩 class EchoServiceImpl : public EchoService { public: void Echo(google::protobuf::RpcController* cntl_base, const EchoRequest* request, EchoResponse* response, google::protobuf::Closure* done) { // ... 业务处理 response-set_message(request-message()); done-Run(); // 必须调用以通知框架完成 } };关键调优参数bthread并发度通过-bthread_concurrency启动参数设置通常设置为CPU核数。Channel参数connection_type设置为”single”短连接或”pooled”连接池。对于高并发RPCpooled是必须的并需要合理设置max_pool_size。协议选择默认使用baidu_std协议性能最好。与gRPC服务互通时可选用h2。超时与重试timeout_ms和max_retry需要根据业务敏感度设置。切忌无限制重试可能引发雪崩。踩坑记录brpc的日志输出默认非常详细在生产环境压测前务必调整glog级别-minloglevel2或关闭否则日志I/O会成为巨大的性能瓶颈。另外brpc内置的监控页面通过-bthread_concurrency非常有用可以实时查看QPS、延迟和错误率。3.3 tRPC-Cpp云原生时代的框架新选择tRPC-Cpp是腾讯为云原生环境设计的RPC框架强调与Kubernetes、服务网格的集成能力。它的架构清晰插件化程度高。核心实现模式同样基于Protobuf。它的一个特点是区分了融合模式和分离模式。融合模式下IO线程也处理业务延迟更低分离模式下IO线程和业务线程分离吞吐量更有保障。我根据场景进行了测试。// 服务端启动关键代码 trpc::ServicePtr service std::make_sharedEchoServiceImpl(); trpc::Server server; server.RegisterService(service); server.Start();关键调优参数线程模型选择在配置文件中设置threadmodel。对于计算密集型的RPC使用separate分离模式并配置io_thread_num和handle_thread_num。传输配置trans_info中可以设置max_conn_num最大连接数、recv_buffer_size等。协议配置支持trpc、http等多种协议。trpc协议头部更小性能更优。过滤器链tRPC的过滤器Filter类似于中间件可以方便地添加监控、鉴权等功能。但压测时需注意不必要的过滤器会增加开销。注意事项tRPC-Cpp的配置文件通常是trpc_cpp.yaml是其运行的核心任何线程、协议、服务的配置都在其中。修改配置后需要重启服务。它的文档和社区目前相比brpc稍显年轻遇到复杂问题时可能需要深入源码或社区提问。3.4 gRPC生态标准的性能基线gRPC作为CNCF毕业项目是跨语言RPC的事实标准。我们用它作为性能基线看看国产库在核心性能上是否实现了超越。核心实现模式基于HTTP/2和Protobuf使用Completion Queue进行异步操作是发挥其高性能的标准做法但代码稍显复杂。// 异步服务端示例片段 ServerBuilder builder; builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(service); std::unique_ptrServerCompletionQueue cq builder.AddCompletionQueue(); std::unique_ptrServer server(builder.BuildAndStart()); // 需要手动维护多个CallData对象来处理并发请求关键调优参数Channel参数grpc::ChannelArguments中可以设置max_send_message_length,max_receive_message_length以及GRPC_ARG_HTTP2_WRITE_BUFFER_SIZE等HTTP/2特定参数。Completion Queue数量通常与CPU核心数匹配。线程池gRPC C默认使用一个全局线程池可以通过SetDefaultThreadPool进行调整。性能提示gRPC的同步接口grpc::BlockingUnaryCall非常简单但性能远不及异步接口。在生产级性能测试中务必使用异步API。此外gRPC的连接复用是自动的但初始连接建立开销相对较大在短连接场景下是劣势。4. 性能测试数据与深度分析经过多轮测试采集了海量数据。下面我挑重点用图表和文字结合的方式呈现最核心的发现。4.1 微服务RPC场景延迟与吞吐的博弈在这个场景下我们固定并发客户端线程数为100请求包大小为100字节响应包大小为200字节测试持续时间为5分钟。通信库平均延迟 (P50)尾部延迟 (P99)最大QPS (P9910ms时)CPU使用率 (服务端最大QPS)NNG0.21 ms2.1 ms285,00078%brpc0.25 ms1.8 ms312,00082%tRPC-Cpp0.28 ms2.5 ms298,00085%gRPC0.35 ms4.3 ms265,00090%深度分析NNG表现出色其极简的设计带来了最低的平均延迟和可控的尾部延迟。在QPS达到28万时CPU使用率仍相对较低说明其内核开销很小。这验证了“简单即高效”的设计哲学非常适合对延迟极其敏感、业务逻辑相对简单的内部服务调用。brpc综合最优在保证P99延迟最低1.8ms的前提下达到了最高的QPS31.2万。这得益于其高效的bthread协程调度在大量并发连接时上下文切换开销远小于操作系统线程。更高的CPU使用率意味着它更充分地利用了硬件资源。tRPC-Cpp紧追不舍性能与brpc在伯仲之间略逊一筹但差距很小。其分离线程模型在测试中表现稳定说明框架成熟度已经很高。gRPC成为基线其平均和尾部延迟都是最高的QPS也最低。这并非gRPC不优秀而是其HTTP/2协议栈、复杂的流处理等特性带来了额外的开销。它的核心价值在于跨语言生态和丰富的功能而非极限性能。实操心得测试中发现当请求包体增大到10KB以上时各库的QPS差距会缩小而延迟差距会扩大。此时序列化Protobuf的开销开始占据主导。因此如果你的业务涉及大包传输序列化库的选择和调优可能比通信库本身更重要。4.2 实时数据流推送多路分发的稳定性在这个场景1个发布者向逐渐增加的订阅者广播1KB大小的数据包发送速率固定为每秒10万条。订阅者数量NNG 吞吐衰减brpc 吞吐衰减tRPC-Cpp 吞吐衰减备注10% (基准)0% (基准)0% (基准)单路推送均能跑满带宽10-5%-8%-12%brpc和tRPC内部有多路复用优化50-15%-10%-18%NNG的纯广播模式开始有压力100-28%-15%-25%brpc的流控和拥塞避免机制生效深度分析brpc在广播场景优势明显其内置的流控和高效的内部多路复用使得在订阅者数量增加时吞吐衰减最慢。这对于需要向海量客户端推送相同数据的场景如直播弹幕、行情广播非常有价值。NNG表现中规中矩NNG的Pub/Sub模式非常纯粹但当订阅者非常多时发布者需要为每个订阅者执行一次发送系统调用在内核态产生开销。可以通过绑定到多个传输端点或使用inproc传输进行优化但这增加了架构复杂度。内存占用观察在此长连接测试中brpc和tRPC-Cpp的内存增长曲线最为平稳24小时运行后增量不到5%。NNG在极端连接数下10k内存管理会出现一些碎片需要关注。4.3 高并发短连接场景连接管理的压力测试这个场景模拟API网关的压力客户端每秒尝试建立尽可能多的新连接发送一个请求后立即关闭。通信库最大连接建立速率连接错误率 (峰值)服务端FD回收延迟NNG12,000 conn/s0.5%较低依赖操作系统brpc9,500 conn/s0.05%极低连接池高效管理tRPC-Cpp8,800 conn/s0.1%低gRPC6,500 conn/s1.2%较高HTTP/2连接建立复杂深度分析NNG再次胜出由于其极其轻量的连接建立过程在纯短连接建立速率上夺冠。但错误率相对较高说明在极限压力下其简单的资源管理策略可能不够健壮。brpc的稳定性惊人虽然绝对速率不是最高但错误率低了一个数量级。这得益于其成熟的连接池和资源管理机制能够在高压下优雅降级而非崩溃。对于线上服务稳定性往往比极限性能更重要。gRPC不适合短连接HTTP/2的握手和多路复用特性使得其连接建立成本最高。它更适合长连接、多路复用的场景。5. 总结与选型建议经过这一轮全方位的“体检”每个库的画像已经非常清晰。没有绝对的“最优”只有最适合你当前场景的“最优解”。NNG像一把锋利的瑞士军刀。它最适合需要极致轻量、超低延迟、且通信模式匹配其原生套接字风格的场景。例如物联网设备通信、游戏服务器内部节点间通信、或作为更大系统中一个高性能的底层消息传递组件。它的劣势在于你需要自己实现服务发现、负载均衡等微服务基础设施生态相对单一。brpc像一个功能齐全的现代化工具箱。它最适合构建复杂的企业级微服务特别是当你的服务需要面对高并发、需要熔断限流、服务监控、多种负载均衡策略时。其文档丰富、社区活跃坑相对较少。如果你的团队主要使用C且追求性能与功能的平衡brpc是目前最稳妥、综合实力最强的选择。tRPC-Cpp像一个为云原生环境量身定制的精密仪器。它最适合未来明确会部署在Kubernetes上、且可能需要与Service Mesh如Istio集成的项目。它的设计理念更现代插件化架构有利于长期维护和扩展。如果你在腾讯云生态内或者非常看重云原生特性tRPC-Cpp值得重点考虑。目前其绝对性能略逊于brpc但差距很小且后续优化空间大。gRPC像一座连接不同语言岛屿的坚固桥梁。当你的技术栈是多语言混合如C后端、Go中间件、Java/Python前端并且需要严格的接口契约Protobuf和丰富的流式通信模式如双向流时gRPC仍然是不可替代的标准。为了性能你需要在C侧付出更多的编码努力使用异步接口。我个人的最终选择对于我手头的分布式数据采集项目其特点是内部服务均为C编写通信模式以高并发RPC和少量数据广播为主且团队对运维监控有较高要求。因此我最终选择了brpc。它在核心的RPC性能上做到了顶尖同时提供了开箱即用的运维能力社区的支持也能让我们在遇到问题时快速找到解决方案。这次测试也让我意识到性能数据只是选型的一部分团队的熟悉程度、社区的活跃度、与现有基础设施的整合成本这些“软实力”往往在长期项目中起着决定性作用。在敲定之前不妨用你的真实业务逻辑搭建一个原型系统跑一跑感受一下这才是最可靠的选型方法。