golang-grpc by bobmatnyc/claude-mpm-skills
npx skills add https://github.com/bobmatnyc/claude-mpm-skills --skill golang-grpcgRPC 提供基于 Protocol Buffers 的强类型 RPC API,具备一流的流式支持,并为服务间通信提供卓越性能。本技能专注于生产环境默认配置:版本化的 protobuf、截止时间、错误码、拦截器、健康检查、TLS 和可测试性。
✅ 正确做法:版本化包
// proto/users/v1/users.proto
syntax = "proto3";
package users.v1;
option go_package = "example.com/myapp/gen/users/v1;usersv1";
service UsersService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message GetUserRequest { string id = 1; }
message GetUserResponse { User user = 1; }
message ListUsersRequest { int32 page_size = 1; string page_token = 2; }
message User {
string id = 1;
string email = 2;
string display_name = 3;
}
❌ 错误做法:未版本化包(难以演进)
package users;
安装生成器:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
生成代码:
protoc -I proto \
--go_out=./gen --go_opt=paths=source_relative \
--go-grpc_out=./gen --go-grpc_opt=paths=source_relative \
proto/users/v1/users.proto
✅ 正确做法:验证 + 将错误映射到 gRPC 状态码
package usersvc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
usersv1 "example.com/myapp/gen/users/v1"
)
type Service struct {
usersv1.UnimplementedUsersServiceServer
Repo Repo
}
type Repo interface {
GetUser(ctx context.Context, id string) (User, error)
}
type User struct {
ID, Email, DisplayName string
}
func (s *Service) GetUser(ctx context.Context, req *usersv1.GetUserRequest) (*usersv1.GetUserResponse, error) {
if req.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "id is required")
}
u, err := s.Repo.GetUser(ctx, req.GetId())
if err != nil {
if err == ErrNotFound {
return nil, status.Error(codes.NotFound, "user not found")
}
return nil, status.Error(codes.Internal, "internal error")
}
return &usersv1.GetUserResponse{
User: &usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
},
}, nil
}
❌ 错误做法:返回原始错误(客户端失去状态码语义)
return nil, errors.New("user not found")
为每次调用设定时间限制;对耗时处理程序强制执行服务端超时。
✅ 正确做法:要求设置截止时间
if _, ok := ctx.Deadline(); !ok {
return nil, status.Error(codes.InvalidArgument, "deadline required")
}
使用元数据进行身份验证/会话关联,不要用于主要请求数据。
✅ 正确做法:从元数据中读取认证令牌
md, _ := metadata.FromIncomingContext(ctx)
auth := ""
if vals := md.Get("authorization"); len(vals) > 0 {
auth = vals[0]
}
使用拦截器处理横切关注点:身份验证、日志记录、指标、追踪、请求 ID。
✅ 正确做法:包含请求 ID 的一元拦截器
func unaryRequestID() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
id := uuid.NewString()
ctx = context.WithValue(ctx, requestIDKey{}, id)
resp, err := handler(ctx, req)
return resp, err
}
}
✅ 正确做法:在 ctx.Done() 时停止
func (s *Service) ListUsers(req *usersv1.ListUsersRequest, stream usersv1.UsersService_ListUsersServer) error {
users, err := s.Repo.ListUsers(stream.Context(), int(req.GetPageSize()))
if err != nil {
return status.Error(codes.Internal, "internal error")
}
for _, u := range users {
select {
case <-stream.Context().Done():
return stream.Context().Err()
default:
}
if err := stream.Send(&usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
}); err != nil {
return err
}
}
return nil
}
添加健康服务;仅在非生产环境中启用反射。
✅ 正确做法:健康检查 + 条件反射
hs := health.NewServer()
grpc_health_v1.RegisterHealthServer(s, hs)
if env != "production" {
reflection.Register(s)
}
优先使用带截止时间的 GracefulStop。
✅ 正确做法:优雅停止
stopped := make(chan struct{})
go func() {
grpcServer.GracefulStop()
close(stopped)
}()
select {
case <-stopped:
case <-time.After(10 * time.Second):
grpcServer.Stop()
}
在生产环境中使用 TLS(或 mTLS);本地开发环境外避免使用不安全凭证。
✅ 正确做法:服务端 TLS
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil { return err }
grpcServer := grpc.NewServer(grpc.Creds(creds))
使用 bufconn 测试 gRPC 处理程序,无需打开真实套接字。
✅ 正确做法:内存中的 gRPC 测试服务器
const bufSize = 1024 * 1024
lis := bufconn.Listen(bufSize)
srv := grpc.NewServer()
usersv1.RegisterUsersServiceServer(srv, &Service{Repo: repo})
go func() { _ = srv.Serve(lis) }()
ctx := context.Background()
conn, err := grpc.DialContext(
ctx,
"bufnet",
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return lis.Dial() }),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil { t.Fatal(err) }
defer conn.Close()
client := usersv1.NewUsersServiceClient(conn)
resp, err := client.GetUser(ctx, &usersv1.GetUserRequest{Id: "1"})
_ = resp
_ = err
忽略截止时间:无限制的处理程序会导致尾部延迟和资源耗尽。
返回字符串错误:使用 status.Error 或 status.Errorf 将领域错误映射到 codes.*。
无背压的流式传输:在 ctx.Done() 时停止并处理 Send 错误。
在生产环境中暴露反射:将反射视为发现接口。
UNKNOWN 错误应对措施:
status.Error(codes.X, "...") 代替原始错误。应对措施:
ctx 传播到下游调用。应对措施:
ctx.Done() 时停止流式传输,并处理 stream.Send 错误。每周安装量
165
代码仓库
GitHub 星标数
18
首次出现
2026年1月23日
安全审计
安装于
opencode137
gemini-cli135
codex133
github-copilot125
cursor118
claude-code107
gRPC provides strongly-typed RPC APIs backed by Protocol Buffers, with first-class streaming support and excellent performance for service-to-service communication. This skill focuses on production defaults: versioned protos, deadlines, error codes, interceptors, health checks, TLS, and testability.
✅ Correct: versioned package
// proto/users/v1/users.proto
syntax = "proto3";
package users.v1;
option go_package = "example.com/myapp/gen/users/v1;usersv1";
service UsersService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message GetUserRequest { string id = 1; }
message GetUserResponse { User user = 1; }
message ListUsersRequest { int32 page_size = 1; string page_token = 2; }
message User {
string id = 1;
string email = 2;
string display_name = 3;
}
❌ Wrong: unversioned package (hard to evolve)
package users;
Install generators:
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Generate:
protoc -I proto \
--go_out=./gen --go_opt=paths=source_relative \
--go-grpc_out=./gen --go-grpc_opt=paths=source_relative \
proto/users/v1/users.proto
✅ Correct: validate + map errors to gRPC codes
package usersvc
import (
"context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
usersv1 "example.com/myapp/gen/users/v1"
)
type Service struct {
usersv1.UnimplementedUsersServiceServer
Repo Repo
}
type Repo interface {
GetUser(ctx context.Context, id string) (User, error)
}
type User struct {
ID, Email, DisplayName string
}
func (s *Service) GetUser(ctx context.Context, req *usersv1.GetUserRequest) (*usersv1.GetUserResponse, error) {
if req.GetId() == "" {
return nil, status.Error(codes.InvalidArgument, "id is required")
}
u, err := s.Repo.GetUser(ctx, req.GetId())
if err != nil {
if err == ErrNotFound {
return nil, status.Error(codes.NotFound, "user not found")
}
return nil, status.Error(codes.Internal, "internal error")
}
return &usersv1.GetUserResponse{
User: &usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
},
}, nil
}
❌ Wrong: return raw errors (clients lose code semantics)
return nil, errors.New("user not found")
Make every call bounded; enforce server-side timeouts for expensive handlers.
✅ Correct: require deadline
if _, ok := ctx.Deadline(); !ok {
return nil, status.Error(codes.InvalidArgument, "deadline required")
}
Use metadata for auth/session correlation, not for primary request data.
✅ Correct: read auth token from metadata
md, _ := metadata.FromIncomingContext(ctx)
auth := ""
if vals := md.Get("authorization"); len(vals) > 0 {
auth = vals[0]
}
Use interceptors for cross-cutting concerns: auth, logging, metrics, tracing, request IDs.
✅ Correct: unary interceptor with request ID
func unaryRequestID() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
id := uuid.NewString()
ctx = context.WithValue(ctx, requestIDKey{}, id)
resp, err := handler(ctx, req)
return resp, err
}
}
✅ Correct: stop on ctx.Done()
func (s *Service) ListUsers(req *usersv1.ListUsersRequest, stream usersv1.UsersService_ListUsersServer) error {
users, err := s.Repo.ListUsers(stream.Context(), int(req.GetPageSize()))
if err != nil {
return status.Error(codes.Internal, "internal error")
}
for _, u := range users {
select {
case <-stream.Context().Done():
return stream.Context().Err()
default:
}
if err := stream.Send(&usersv1.User{
Id: u.ID,
Email: u.Email,
DisplayName: u.DisplayName,
}); err != nil {
return err
}
}
return nil
}
Add health service; enable reflection only in non-production environments.
✅ Correct: health + conditional reflection
hs := health.NewServer()
grpc_health_v1.RegisterHealthServer(s, hs)
if env != "production" {
reflection.Register(s)
}
Prefer GracefulStop with a deadline.
✅ Correct: graceful stop
stopped := make(chan struct{})
go func() {
grpcServer.GracefulStop()
close(stopped)
}()
select {
case <-stopped:
case <-time.After(10 * time.Second):
grpcServer.Stop()
}
Use TLS (or mTLS) in production; avoid insecure credentials outside local dev.
✅ Correct: server TLS
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil { return err }
grpcServer := grpc.NewServer(grpc.Creds(creds))
Test gRPC handlers without opening real sockets using bufconn.
✅ Correct: in-memory gRPC test server
const bufSize = 1024 * 1024
lis := bufconn.Listen(bufSize)
srv := grpc.NewServer()
usersv1.RegisterUsersServiceServer(srv, &Service{Repo: repo})
go func() { _ = srv.Serve(lis) }()
ctx := context.Background()
conn, err := grpc.DialContext(
ctx,
"bufnet",
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return lis.Dial() }),
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil { t.Fatal(err) }
defer conn.Close()
client := usersv1.NewUsersServiceClient(conn)
resp, err := client.GetUser(ctx, &usersv1.GetUserRequest{Id: "1"})
_ = resp
_ = err
Ignore deadlines : unbounded handlers cause tail latency and resource exhaustion.
Return string errors : map domain errors to codes.* with status.Error or status.Errorf.
Stream without backpressure : stop on ctx.Done() and handle Send errors.
Expose reflection in production : treat reflection as a discovery surface.
UNKNOWN errorsActions:
status.Error(codes.X, "...") instead of raw errors.Actions:
ctx to downstream calls.Actions:
ctx.Done() and handle stream.Send errors.Weekly Installs
165
Repository
GitHub Stars
18
First Seen
Jan 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode137
gemini-cli135
codex133
github-copilot125
cursor118
claude-code107
Lark Drive API 使用指南:飞书云文档、Wiki、表格 Token 处理与文件管理
23,400 周安装