tRPC:它是什么,以及为什么可以高性能
先给结论
tRPC 是 tRPC Group 开源的一套多语言、插件化 RPC 框架:用 Protocol Buffers 描述服务接口,生成客户端与服务端桩代码,让进程间调用在代码形态上接近本地方法调用;它同时覆盖调用、编解码与传输,并通过插件接入服务发现、负载均衡、监控、链路追踪、配置和流控等治理能力。trpc-go 是这套框架的 Go 实现。^[来源:tRPC 官网] ^[来源:tRPC 架构设计] ^[来源:trpc-go README]
“高性能”不是某一个开关带来的结果。在 tRPC-Go 中,它来自一条尽量短、二进制化、可替换的请求路径:protobuf/帧编码减少传输和解析负担,transport 可在 Unix 平台接入事件循环网络库 tnet,Writev 和零拷贝接口减少不必要的缓冲拼接与复制;服务治理又能用限流、过载保护避免系统在超载时整体失效。它们优化的是不同层面的成本,最终效果仍取决于消息大小、连接模型、CPU、业务代码和下游依赖。^[来源:tRPC 协议设计] ^[来源:tnet README]
本文基于 2026-07-27 查阅的 tRPC 官方文档、
trpc-group/trpc-go主分支和其默认 Unix transport 的依赖源码撰写。文中解释的是设计和实现路径,不声明 tRPC 在所有业务或所有协议下都比 gRPC、HTTP 或其他 Go RPC 框架更快;需要以同负载压测验证。
tRPC 解决的是什么问题
微服务中的一次调用不只是“发一个 HTTP 请求”。调用方需要知道服务和方法,双方要约定消息结构、序列化和错误语义,还要处理超时、重试、服务发现、负载均衡、鉴权、指标和追踪。若每个团队分别拼装这些组件,互通和治理会变得昂贵。
tRPC 将它拆为三个核心层:
业务调用 / 生成的 Client Proxy 与 Service Stub
│
调用层:同步、异步、单向、流式调用
│ Filter:鉴权、追踪、指标、限流等横切能力
服务治理层:服务发现、负载均衡、监控、配置等插件
│
通信层:codec(协议/序列化) + transport(TCP/UDP/网络 I/O)
│
网络与远端服务官方架构将调用层、服务治理层和通信层分开;核心只定义接口和装配关系,Codec、命名服务、指标、追踪等具体实现可以作为插件替换。Filter 则放在 RPC 调用路径的切点上,Go 实现用递归方式组织过滤链。这样的设计首先是为了兼容异构技术栈和服务治理,并非只为性能;但分层使高性能 transport 或 codec 可以替换进入,而不必改业务接口。^[来源:tRPC 架构设计]
它怎样变成一次 RPC 调用
典型流程如下:
- 在
.proto中定义service、rpc、请求和响应消息;tRPC 使用 protobuf 支持跨语言服务通信。 trpc命令生成 Go client proxy 与 server stub。业务代码实现 server 接口;client 像调普通 Go 方法一样调用 proxy。- client 依次执行 Filter、服务发现/负载均衡等治理逻辑,选定目标和协议。
- codec 将调用元数据和 protobuf 消息编码为帧,transport 将字节写到网络;服务端按相反方向解帧、执行 Filter,再分发给注册的服务方法。
- 响应沿反向路径返回。流式 RPC 则在同一套协议框架中用 stream frame 传输多段数据。
Go 基础教程展示了这个契约:生成的 client proxy 暴露 Hello(ctx, req, opts...),服务端注册实现 Hello(ctx, req) 的结构;服务地址、端口、协议等可以写入 trpc_go.yaml。这使服务定义与通信细节分离,也可以将同一服务配置为 tRPC 或 HTTP 等协议。^[来源:tRPC-Go 基础教程]
为什么这条路径可能更快
1. 二进制消息与紧凑、可定界的帧
tRPC 面向跨语言的默认服务定义基于 Protocol Buffers。相对于常见的文本 JSON,protobuf 通常能以更紧凑的二进制形式表示结构化消息,减少网络字节和文本解析工作;但“通常”不等于任何消息都更小——字段形态、压缩和实现会改变结果。官网也明确将多种序列化和压缩方式列为协议扩展能力。^[来源:tRPC 官网] ^[来源:tRPC 协议设计]
在 trpc-go/codec.go 中,默认 tRPC codec 注册了 server/client codec 和 frame builder;协议帧头固定为 16 字节,携带帧类型、总长度、protobuf header 长度、stream/request ID 与协议版本。接收端可先读取固定头部,获知一帧边界,再处理 header 与 body;这是一种适合 TCP 字节流的确定性解帧方式,并为 unary 与 streaming 共用传输通道。它减少的是协议解析的不确定性和额外包装,不代表业务序列化或网络 RTT 会消失。^[来源:trpc-go codec.go]
2. Unix 上默认接入 tnet 的事件循环传输
trpc-go 的 tnet_unix.go 对 Linux、FreeBSD、DragonFly BSD 与 macOS 使用空白导入注册 transport/tnet。因此这些平台上,框架能够使用 tnet 网络库 的 transport;其他平台或显式替换 transport 时,路径可能不同。^[来源:trpc-go tnet_unix.go]
tnet 提供事件循环和非阻塞模式,也支持 ReadV/WriteV 批量系统调用。其文档把 handler 是否在 poller goroutine 中运行、是否使用业务 goroutine 池分成不同模型:高连接但 I/O 等待为主的场景可避免让每个连接都长期占用一个等待 goroutine;CPU 密集业务则可把 I/O 与业务处理分开,以免 poller 被计算阻塞。选择错误模型会反过来伤害尾延迟,例如在 poller 中执行慢业务。^[来源:tnet README - models]
3. 少一次复制或拼接,也可能很重要
tnet 的 Peek、Next、Skip 和 Release 直接在底层 linked buffer 上操作;Writev 可以顺序发送多段 byte slice,例如帧头和消息体,而无需先把它们拼成一个大的临时切片。这些 API 为高 QPS、小消息或大并发连接减少分配和内存带宽提供了条件。代价是生命周期更严格:零拷贝返回的数据在后续读取或 Release 后不能继续使用;错误持有切片可能造成数据错误或内存滞留。^[来源:tnet README - zero-copy APIs]
4. 连接与 CPU 资源按负载调度,而非一味堆 goroutine
网络 I/O 的高并发能力还要与 Go runtime 协同。tnet 的 poller 和业务 goroutine 最终由 Go GMP 调度模型 调度:可运行 goroutine 过多、CPU 饱和、锁竞争或频繁分配都会拉高排队和 GC 延迟。故 tRPC 的网络层优化更可能在连接数大、I/O 等待多、协议/拷贝成本显著时体现;若瓶颈是数据库、远端 RPC 或慢业务逻辑,换 RPC 框架很难改善总延迟。
5. 过载保护维护的是可用吞吐
官网提供流控和过载保护插件,目标是抵抗突发流量导致的不可用。它不让单请求“跑得更快”,却能在资源不足时拒绝或限制一部分工作,阻止请求排队无限增长、超时扩散和级联故障。因此它优化的是系统在压力下仍能完成的有效请求数与尾延迟,而不是孤立 benchmark 的最大 QPS。^[来源:tRPC 官网]
高性能并不等于所有配置都快
| 条件 | tRPC 的优化是否容易体现 | 原因 |
|---|---|---|
| 小到中等 protobuf 消息、高连接并发、I/O 等待占比高 | 较容易 | 紧凑编码、事件循环、少拷贝和批量写的收益更直接 |
| CPU 密集的 handler | 不确定 | 需要正确分离 I/O 与业务,并受 CPU 与 Go 调度限制 |
| 下游数据库或第三方 API 很慢 | 通常不明显 | 端到端延迟由下游等待主导 |
| 重 Filter、复杂日志、全量 tracing | 可能变慢 | 插件提高可观测性和治理,却会增加路径工作量 |
| 非 Unix 平台或替换了 transport | 需单独验证 | 默认 tnet 注册的构建条件不再成立 |
性能评估至少应在同一机器、Go 版本、消息模型、并发数、连接复用和 TLS 配置下,对比吞吐、P50/P95/P99、CPU、分配率、GC 和错误/拒绝率;然后使用 CPU profile、allocation profile 与 execution trace 定位开销。尤其不要用“框架标称 QPS”推导真实业务容量。
与 gRPC、HTTP 的关系
tRPC 不要求只使用一种协议。官方说明其通信层可通过 Codec 插件接入其他协议;trpc-go 默认支持 tRPC 与 HTTP,并可通过实现 codec 接口支持第三方协议。官方基础教程还展示了将服务配置从 trpc 改为 http,用 HTTP 路径调用同一套服务定义的方式。因此,它更像是一个将多协议通信和治理能力统一起来的框架,而非“只能替代 HTTP”的单一 wire protocol。^[来源:trpc-go README] ^[来源:tRPC-Go 基础教程]
选择时应优先看互操作和运维边界:若现有生态、网关、可观测性和团队经验都围绕 gRPC 或 HTTP,采用相应协议插件/transport 的迁移成本可能比理论上更紧凑的帧格式重要;若内部服务已经使用 tRPC 协议并且连接规模、序列化或拷贝确实是瓶颈,tRPC-Go 的默认能力更值得压测验证。
实践检查清单
- 先画出端到端耗时:编解码、排队、网络、handler、数据库/下游分别多少。
- 明确生产平台与实际 transport;不要假定所有环境都会使用 tnet。
- 对比时固定 protobuf schema、压缩、TLS、连接池/连接复用和超时配置。
- 压测同时记录拒绝率、超时与 P99,而非只看 QPS。
- 发现 CPU 或 GC 上升时,结合 profile 与 Go GMP 调度模型 检查 goroutine 排队、分配和锁竞争。
- 把 Filter、日志、追踪、限流也放进压测,因为它们是生产请求路径的一部分。
还可继续研究的问题
transport/tnet的连接复用、写队列和 backpressure 在当前版本中如何实现?- tRPC、HTTP、gRPC 三种协议在同一业务 protobuf 与连接模型下的 CPU/分配/P99 对比是什么?
- Filter 链长度和 tracing 采样率分别会给吞吐和尾延迟带来多大成本?
- 在不同
GOMAXPROCS、容器 CPU 配额与 TLS 配置下,tnet 的事件循环模型如何变化?