「zrpc 是 go-zero 内置的吗?怎么用?」—— 这篇把 zrpc 的身世(基于 gRPC)、它自带的服务治理、以及一条龙的使用流程(proto → goctl → server → client)讲清楚。
zrpc = 「zero rpc」,是 go-zero 自带的 RPC 通信框架。它让你用 protobuf 定义接口,在服务之间高效地「远程调用方法」。
一句话:zrpc = go-zero 对 gRPC 的「开箱即用封装」。它把 gRPC 的传输能力,加上服务发现、负载均衡、超时、重试、熔断、链路追踪这些「微服务刚需」,用配置的方式一键启用——你写业务,治理交给框架。
是。zrpc 是 go-zero 框架的一部分,包路径就是 github.com/zeromicro/go-zero/zrpc,不需要你单独引一个第三方 RPC 库。
import "github.com/zeromicro/go-zero/zrpc" 即可,无需再引 grpc 包手动搭。// 只需要 go-zero,就能用 zrpc: import ( "github.com/zeromicro/go-zero/zrpc" // 内置 RPC "google.golang.org/grpc" // zrpc 依赖的 gRPC(自动带) )
裸 gRPC 只管「序列化 + 传输」。微服务要的服务发现、治理,得自己拼。zrpc 把这些做成配置项。
| 能力 | 裸 gRPC 自己搞 | zrpc(内置) |
|---|---|---|
| 接口定义 | 写 .proto + protoc 生成 | 同样写 .proto,但 goctl rpc protoc 一条命令顺手把 zrpc 代码也生成了 |
| 服务发现 | 自己接 etcd/resolver | 配置 Etcd: {Hosts, Key} 自动注册 + 发现 |
| 负载均衡 | 自己选 picker | P2C(基于负载的均衡)内置 |
| 超时 / 重试 | 手动 context + 重试逻辑 | Timeout / Retries 配置即用 |
| 熔断 | 自己接 breaker | 客户端调用自动带熔断器 |
| 链路追踪 / 监控 | 自行埋点 | otel 追踪、prometheus 指标内置,配置开启 |
结论:zrpc = 裸 gRPC + 一套现成的微服务治理。如果你只是「两个服务传个数据」,裸 gRPC 够用;但一旦上微服务(多个实例、要发现、要防雪崩),zrpc 的价值就出来了。
从定义到调用,四步:写 proto → goctl 生成 → 起 server → client 调用。下面逐步给代码。
syntax = "proto3"; package user; option go_package = "./user"; service User { rpc GetUser(GetUserReq) returns (GetUserResp); } message GetUserReq { int64 id = 1; } message GetUserResp { int64 id = 1; string name = 2; }
goctl rpc protoc user.proto \# 生成 pb + 注册代码 + zrpc server/client
--go_out=./types --go-grpc_out=./types \
--zrpc_out=.
// etc/user.yaml —— 服务端配置 Name: user.rpc ListenOn: 0.0.0.0:8080 Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc # 注册到 etcd 的服务名 // internal/server/userserver.go —— goctl 生成,你只填逻辑 func (s *UserServer) GetUser(ctx context.Context, req *user.GetUserReq) (*user.GetUserResp, error) { u, err := s.svcCtx.DB.FindOne(ctx, req.Id) // 业务在这里 return &user.GetUserResp{Id: u.Id, Name: u.Name}, err } // main.go func main() { var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c) s := zrpc.MustNewServer(c.RpcServerConf, func(g *grpc.Server) { user.RegisterUserServer(g, server.NewUserServer(ctx)) // 注册实现 }) defer s.Stop() s.Start() // 启动后自动把 user.rpc 写进 etcd }
Etcd,启动时 zrpc 自动把「服务名 → 自己的地址」注册进 etcd;客户端靠同一 Key 去 etcd 找地址。你不用手写注册/发现代码。客户端同样零发现代码:配置 etcd 的 Key,goctl 生成的 user.NewUser(client) 直接拿到可调用客户端,注入 svc 即可。
// 调用方(如 user-api) 的 config:指向同一个 etcd Key UserRpc: Etcd: Hosts: - 127.0.0.1:2379 Key: user.rpc # 和 server 端一致 // svc/servicecontext.go —— 生成客户端并注入 type ServiceContext struct { Config config.Config UserRpc user.User # goctl 生成的客户端接口 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)), # ★ 创建客户端 } } // logic:像调本地方法一样调远程 func (l *XxxLogic) Do() { resp, err := l.svcCtx.UserRpc.GetUser(l.ctx, &user.GetUserReq{Id: 1}) # 背后:etcd 找地址 → P2C 选节点 → gRPC 调用 → 超时/重试/熔断 }
客户端三行关键:zrpc.MustNewClient(conf) 建客户端 → user.NewUser(client) 生成强类型 stub → 存进 svc。之后业务里 svcCtx.UserRpc.Xxx(ctx, req) 即可,发现、均衡、超时、重试、熔断全部透明。
zrpc 的杀手锏是「治理靠配置」。下面是最常见的几个开关(写在 yaml 里)。
Timeout: 3000(毫秒)。每次调用超过就中断,避免被下游拖死。Retries: 2。失败自动重试(注意下游要幂等)。Telemetry 接 OpenTelemetry,Prometheus 指标自动暴露,定位跨服务问题。# 调用方 user-api.yaml 里的 UserRpc 客户端配置示例 UserRpc: Etcd: { Hosts: [127.0.0.1:2379], Key: user.rpc } Timeout: 3000 # 超时 3s Retries: 1 # 失败重试 1 次
| 疑问 | 答案 |
|---|---|
| zrpc 是什么? | go-zero 自带的 RPC 框架,基于 gRPC + protobuf,专用于服务间高效远程调用方法。 |
| 是 go-zero 内置的吗? | 是。包路径 github.com/zeromicro/go-zero/zrpc,装 go-zero 即有,无需另引 RPC 库;底层封装了 google.golang.org/grpc。 |
| zrpc 和裸 gRPC 差啥? | zrpc = 裸 gRPC + 现成治理(etcd 发现、P2C 均衡、超时、重试、熔断、追踪)。裸 gRPC 只管传输。 |
| 怎么用? | 四步:① 写 .proto 定义 service;② goctl rpc protoc 生成 stub+骨架;③ server 端填逻辑 + 配置 etcd 启动(自动注册);④ client 端配置同一个 etcd Key,user.NewUser(zrpc.MustNewClient(conf)) 注入 svc 后像本地方法一样调。 |
| 治理怎么开? | 靠配置:Timeout、Retries、内置熔断、Telemetry 追踪;重试前请确认接口幂等。 |
终极一句话:zrpc 就是 go-zero 内置的「gRPC + 微服务治理全家桶」。你用 protobuf 定义接口、用 goctl 生成代码,发现/均衡/超时/重试/熔断全靠配置打开——把「写 RPC」变成「填配置 + 写业务逻辑」。