gRPC 入门指南
什么是 gRPC
gRPC 是由 Google 开源的高性能 RPC 框架,基于 HTTP/2 和 Protocol Buffers。它支持多种编程语言,提供双向流、流控、认证等特性。
Protocol Buffers
定义服务
使用 .proto 文件定义服务和消息结构:
syntax = "proto3";
package user;
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
rpc UpdateUser (UpdateUserRequest) returns (User);
rpc WatchUser (WatchUserRequest) returns (stream UserEvent);
}
message GetUserRequest {
string user_id = 1;
}
message User {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
message ListUsersRequest {
int32 page = 1;
int32 page_size = 2;
}
message ListUsersResponse {
repeated User users = 1;
int32 total = 2;
}
数据类型
| .proto 类型 | Go 类型 | Java 类型 | 说明 |
|---|---|---|---|
| double | float64 | double | 64位浮点 |
| float | float32 | float | 32位浮点 |
| int32 | int32 | int | 变长编码 |
| int64 | int64 | long | 变长编码 |
| string | string | String | UTF-8 字符串 |
| bytes | []byte | ByteString | 字节数组 |
四种通信模式
一元 RPC(Unary)
请求-响应模式,类似传统 HTTP:
rpc GetUser(GetUserRequest) returns (User);
服务端流式
服务端持续发送数据流:
// 服务端
func (s *server) WatchUser(req *pb.WatchUserRequest, stream pb.UserService_WatchUserServer) error {
for {
event := getUserEvent(req.UserId)
if err := stream.Send(event); err != nil {
return err
}
time.Sleep(1 * time.Second)
}
}
客户端流式
客户端持续发送数据流:
rpc UploadLogs(stream LogEntry) returns (UploadResponse);
双向流式
双方可以独立发送数据:
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
func (s *server) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
// 处理消息并回复
stream.Send(&pb.ChatMessage{...})
}
}
gRPC vs REST
| 特性 | gRPC | REST |
|---|---|---|
| 协议 | HTTP/2 | HTTP 1.1/2 |
| 数据格式 | Protocol Buffers(二进制) | JSON/XML(文本) |
| 接口定义 | 强类型(.proto) | 无标准(常用 OpenAPI) |
| 流式通信 | 原生支持 | 需 WebSocket |
| 浏览器支持 | 需 gRPC-Web | 原生 |
| 调试工具 | 需特殊工具 | curl / Postman |
| 性能 | 高(二进制 + HTTP/2) | 中等 |
中间件(Interceptor)
// 服务端拦截器:日志记录
func loggingInterceptor(ctx context.Context, req interface{},
info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
log.Printf("gRPC call: %s", info.FullMethod)
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("gRPC call %s completed in %v", info.FullMethod, time.Since(start))
return resp, err
}
// 注册拦截器
server := grpc.NewServer(
grpc.UnaryInterceptor(loggingInterceptor),
)
最佳实践
- 使用
google/protobuf/wrappers.proto处理可选字段 - 合理设置消息大小限制(默认 4MB)
- 启用 gzip 压缩减少带宽
- 使用健康检查协议实现服务探活
- 谨慎设计 proto 文件,字段号一旦发布不宜修改