golang-grpc
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePersona: You are a Go distributed systems engineer. You design gRPC services for correctness and operability — proper status codes, deadlines, interceptors, and graceful shutdown matter as much as the happy path.
Modes:
- Build mode — implementing a new gRPC server or client from scratch.
- Review mode — auditing existing gRPC code for correctness, security, and operability issues.
角色定位: 你是一名Go分布式系统工程师。你设计的gRPC服务兼顾正确性与可操作性——恰当的状态码、超时时间、拦截器以及优雅关闭,与正常流程同样重要。
模式:
- 构建模式——从零开始实现新的gRPC服务端或客户端。
- 审查模式——审核现有gRPC代码的正确性、安全性和可操作性问题。
Go gRPC Best Practices
Go gRPC最佳实践
Treat gRPC as a pure transport layer — keep it separate from business logic. The official Go implementation is .
google.golang.org/grpcThis skill is not exhaustive. Please refer to library documentation and code examples for more informations. Context7 can help as a discoverability platform.
将gRPC视为纯传输层——与业务逻辑分离。官方Go实现为。
google.golang.org/grpc本指南并非详尽无遗。如需更多信息,请参考库文档和代码示例。Context7可作为发现平台提供帮助。
Quick Reference
快速参考
| Concern | Package / Tool |
|---|---|
| Service definition | |
| Code generation | |
| Error handling | |
| Rich error details | |
| Interceptors | |
| Middleware ecosystem | |
| Testing | |
| TLS / mTLS | |
| Health checks | |
| 关注点 | 包/工具 |
|---|---|
| 服务定义 | |
| 代码生成 | |
| 错误处理 | |
| 丰富错误详情 | |
| 拦截器 | |
| 中间件生态 | |
| 测试 | |
| TLS / mTLS | |
| 健康检查 | |
Proto File Organization
Proto文件组织
Organize by domain with versioned directories (). Always use / wrapper messages — bare types like cannot have fields added later. Generate with or .
proto/user/v1/RequestResponsestringbuf generateprotocProto & code generation reference
按领域组织并使用版本化目录()。务必使用/包装消息——像这样的裸类型后续无法添加字段。使用或生成代码。
proto/user/v1/RequestResponsestringbuf generateprotocProto与代码生成参考
Server Implementation
服务端实现
- Implement health check service () — Kubernetes probes need it to determine readiness
grpc_health_v1 - Use interceptors for cross-cutting concerns (logging, auth, recovery) — keeps business logic clean
- Use with a timeout fallback to
GracefulStop()— drains in-flight RPCs while preventing hangsStop() - Disable reflection in production — it exposes your full API surface
go
srv := grpc.NewServer(
grpc.ChainUnaryInterceptor(loggingInterceptor, recoveryInterceptor),
)
pb.RegisterUserServiceServer(srv, svc)
healthpb.RegisterHealthServer(srv, health.NewServer())
go srv.Serve(lis)
// On shutdown signal:
stopped := make(chan struct{})
go func() { srv.GracefulStop(); close(stopped) }()
select {
case <-stopped:
case <-time.After(15 * time.Second):
srv.Stop()
}- 实现健康检查服务()——Kubernetes探针需要它来判断服务就绪状态
grpc_health_v1 - 使用拦截器处理横切关注点(日志、认证、恢复)——保持业务逻辑简洁
- 使用并搭配超时回退到
GracefulStop()——在防止挂起的同时处理正在进行的RPC请求Stop() - 生产环境中禁用反射——它会暴露你的完整API接口
go
srv := grpc.NewServer(
grpc.ChainUnaryInterceptor(loggingInterceptor, recoveryInterceptor),
)
pb.RegisterUserServiceServer(srv, svc)
healthpb.RegisterHealthServer(srv, health.NewServer())
go srv.Serve(lis)
// 收到关闭信号时:
stopped := make(chan struct{})
go func() { srv.GracefulStop(); close(stopped) }()
select {
case <-stopped:
case <-time.After(15 * time.Second):
srv.Stop()
}Interceptor Pattern
拦截器模式
go
func loggingInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("method=%s duration=%s code=%s", info.FullMethod, time.Since(start), status.Code(err))
return resp, err
}go
func loggingInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("method=%s duration=%s code=%s", info.FullMethod, time.Since(start), status.Code(err))
return resp, err
}Client Implementation
客户端实现
- Reuse connections — gRPC multiplexes RPCs on a single HTTP/2 connection; one-per-request wastes TCP/TLS handshakes
- Set deadlines on every call () — without one, a slow upstream hangs goroutines indefinitely
context.WithTimeout - Use with headless Kubernetes services via
round_robinschemedns:/// - Pass metadata (auth tokens, trace IDs) via
metadata.NewOutgoingContext
go
conn, err := grpc.NewClient("dns:///user-service:50051",
grpc.WithTransportCredentials(creds),
grpc.WithDefaultServiceConfig(`{
"loadBalancingPolicy": "round_robin",
"methodConfig": [{
"name": [{"service": ""}],
"timeout": "5s",
"retryPolicy": {
"maxAttempts": 3,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"]
}
}]
}`),
)
client := pb.NewUserServiceClient(conn)- 复用连接——gRPC在单个HTTP/2连接上多路复用RPC请求;每次请求新建连接会浪费TCP/TLS握手资源
- 为每个调用设置超时时间()——如果不设置,上游服务响应缓慢会导致goroutine无限挂起
context.WithTimeout - 通过scheme搭配无头Kubernetes服务使用
dns:///负载均衡round_robin - 通过传递元数据(认证令牌、追踪ID)
metadata.NewOutgoingContext
go
conn, err := grpc.NewClient("dns:///user-service:50051",
grpc.WithTransportCredentials(creds),
grpc.WithDefaultServiceConfig(`{
"loadBalancingPolicy": "round_robin",
"methodConfig": [{
"name": [{"service": ""}],
"timeout": "5s",
"retryPolicy": {
"maxAttempts": 3,
"initialBackoff": "0.1s",
"maxBackoff": "1s",
"backoffMultiplier": 2,
"retryableStatusCodes": ["UNAVAILABLE"]
}
}]
}`),
)
client := pb.NewUserServiceClient(conn)Error Handling
错误处理
Always return gRPC errors using with a specific code — a raw becomes , telling the client nothing actionable. Clients use codes to decide retry vs fail-fast vs degrade.
status.Errorerrorcodes.Unknown| Code | When to Use |
|---|---|
| Malformed input (missing field, bad format) |
| Entity does not exist |
| Create failed, entity exists |
| Caller lacks permission |
| Missing or invalid token |
| System not in required state |
| Rate limit or quota exceeded |
| Transient issue, safe to retry |
| Unexpected bug |
| Timeout |
go
// ✗ Bad — caller gets codes.Unknown, can't decide whether to retry
return nil, fmt.Errorf("user not found")
// ✓ Good — specific code lets clients act appropriately
if errors.Is(err, ErrNotFound) {
return nil, status.Errorf(codes.NotFound, "user %q not found", req.UserId)
}
return nil, status.Errorf(codes.Internal, "lookup failed: %v", err)For field-level validation errors, attach via .
errdetails.BadRequeststatus.WithDetails始终使用返回带有特定状态码的gRPC错误——原生会被转为,无法为客户端提供可操作的信息。客户端会根据状态码决定是重试、快速失败还是降级处理。
status.Errorerrorcodes.Unknown| 状态码 | 使用场景 |
|---|---|
| 输入格式错误(缺少字段、格式不正确) |
| 实体不存在 |
| 创建失败,实体已存在 |
| 调用者缺少权限 |
| 缺少或无效的令牌 |
| 系统未处于所需状态 |
| 超出速率限制或配额 |
| 临时问题,可安全重试 |
| 意外bug |
| 超时 |
go
// ✗ 错误示例——调用者得到codes.Unknown,无法决定是否重试
return nil, fmt.Errorf("user not found")
// ✓ 正确示例——特定状态码让客户端可以采取恰当的行动
if errors.Is(err, ErrNotFound) {
return nil, status.Errorf(codes.NotFound, "user %q not found", req.UserId)
}
return nil, status.Errorf(codes.Internal, "lookup failed: %v", err)对于字段级验证错误,通过附加。
status.WithDetailserrdetails.BadRequestStreaming
流式处理
| Pattern | Use Case |
|---|---|
| Server streaming | Server sends a sequence (log tailing, result sets) |
| Client streaming | Client sends a sequence, server responds once (file upload, batch) |
| Bidirectional | Both send independently (chat, real-time sync) |
Prefer streaming over large single messages — avoids per-message size limits and lowers memory pressure.
go
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, u := range users {
if err := stream.Send(u); err != nil {
return err
}
}
return nil
}| 模式 | 使用场景 |
|---|---|
| 服务端流式 | 服务端发送序列数据(日志追踪、结果集) |
| 客户端流式 | 客户端发送序列数据,服务端一次性响应(文件上传、批量处理) |
| 双向流式 | 双方独立发送数据(聊天、实时同步) |
优先使用流式处理而非大尺寸单条消息——避免单消息大小限制并降低内存压力。
go
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, u := range users {
if err := stream.Send(u); err != nil {
return err
}
}
return nil
}Testing
测试
Use for in-memory connections that exercise the full gRPC stack (serialization, interceptors, metadata) without network overhead. Always test that error scenarios return the expected gRPC status codes.
bufconnTesting patterns and examples
使用创建内存连接,可在不产生网络开销的情况下测试完整的gRPC栈(序列化、拦截器、元数据)。务必测试错误场景是否返回预期的gRPC状态码。
bufconn测试模式与示例
Security
安全
- TLS MUST be enabled in production — credentials travel in metadata
- For service-to-service auth, use mTLS or delegate to a service mesh (Istio, Linkerd)
- For user auth, implement and validate tokens in an auth interceptor
credentials.PerRPCCredentials - Reflection SHOULD be disabled in production to prevent API discovery
- 生产环境中必须启用TLS——凭证通过元数据传输
- 服务间认证使用mTLS或委托给服务网格(Istio、Linkerd)
- 用户认证实现并在认证拦截器中验证令牌
credentials.PerRPCCredentials - 生产环境中应禁用反射以防止API被发现
Performance
性能
| Setting | Purpose | Typical Value |
|---|---|---|
| Ping interval for idle connections | 30s |
| Ping ack timeout | 10s |
| Override 4 MB default for large payloads | 16 MB |
| Connection pooling | Multiple conns for high-load streaming | 4 connections |
Most services do not need connection pooling — profile before adding complexity.
| 设置项 | 用途 | 典型值 |
|---|---|---|
| 空闲连接的Ping间隔 | 30s |
| Ping响应超时 | 10s |
| 覆盖默认4MB限制以支持大负载 | 16 MB |
| 连接池 | 高负载流式处理的多连接 | 4个连接 |
大多数服务不需要连接池——在增加复杂度前先做性能分析。
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
Returning raw | Becomes |
| No deadline on client calls | Slow upstream hangs indefinitely. Always |
| New connection per request | Wastes TCP/TLS handshakes. Create once, reuse — HTTP/2 multiplexes RPCs |
| Reflection enabled in production | Lets attackers enumerate every method. Enable only in dev/staging |
| Wrong codes break client retry logic. |
| Bare types as RPC arguments | Can't add fields to |
| Missing health check service | Kubernetes can't determine readiness, kills pods during deployments |
| Ignoring context cancellation | Long operations continue after caller gave up. Check |
| 错误 | 修复方案 |
|---|---|
返回原生 | 会被转为 |
| 客户端调用未设置超时 | 上游服务响应缓慢会导致无限挂起。务必使用 |
| 每次请求新建连接 | 浪费TCP/TLS握手资源。创建一次连接并复用——HTTP/2支持多路复用RPC请求 |
| 生产环境启用反射 | 允许攻击者枚举所有方法。仅在开发/预发布环境启用 |
所有错误都返回 | 错误的状态码会破坏客户端重试逻辑。 |
| 使用裸类型作为RPC参数 | |
| 缺少健康检查服务 | Kubernetes无法判断服务就绪状态,部署期间会杀死Pod |
| 忽略上下文取消 | 调用者放弃后长时间操作仍在继续。检查 |
Cross-References
交叉引用
- → See skill for deadline and cancellation patterns
samber/cc-skills-golang@golang-context - → See skill for gRPC error to Go error mapping
samber/cc-skills-golang@golang-error-handling - → See skill for gRPC interceptors (logging, tracing, metrics)
samber/cc-skills-golang@golang-observability - → See skill for gRPC testing with bufconn
samber/cc-skills-golang@golang-testing
- → 参考技能了解超时和取消模式
samber/cc-skills-golang@golang-context - → 参考技能了解gRPC错误与Go错误的映射
samber/cc-skills-golang@golang-error-handling - → 参考技能了解gRPC拦截器(日志、追踪、指标)
samber/cc-skills-golang@golang-observability - → 参考技能了解使用bufconn进行gRPC测试
samber/cc-skills-golang@golang-testing