gRPC 速查表
本页面汇总了 gRPC 最常用的语法和命令,方便快速查阅。
Proto 语法
基本结构
syntax = "proto3";
package mypackage;
option go_package = "./mypackage";
option java_package = "com.example.mypackage";
message MessageName {
string field1 = 1;
int32 field2 = 2;
}
数据类型
| 类型 | 说明 |
|---|---|
double, float | 浮点数 |
int32, int64 | 整数(负数效率低) |
uint32, uint64 | 无符号整数 |
sint32, sint64 | 有符号整数(负数效率高) |
fixed32, fixed64 | 固定长度整数 |
bool | 布尔值 |
string | 字符串 |
bytes | 字节数组 |
字段规则
message Example {
string single = 1; // 单值(默认)
optional string opt = 2; // 可选
repeated string list = 3; // 数组
map<string, int32> map = 4; // Map
}
枚举
enum Status {
UNKNOWN = 0; // 第一个必须是0
ACTIVE = 1;
INACTIVE = 2;
}
服务定义
service MyService {
// 一元 RPC
rpc Unary (Request) returns (Response);
// 服务端流
rpc ServerStream (Request) returns (stream Response);
// 客户端流
rpc ClientStream (stream Request) returns (Response);
// 双向流
rpc Bidirectional (stream Request) returns (stream Response);
}
代码生成
Go
# 安装插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 生成代码
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
hello.proto
Python
# 安装工具
pip install grpcio grpcio-tools
# 生成代码
python -m grpc_tools.protoc -I. \
--python_out=. \
--grpc_python_out=. \
hello.proto
Java
<!-- Maven 配置 -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.25.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.59.0:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</plugin>
Node.js
# 动态加载(推荐)
npm install @grpc/grpc-js @grpc/proto-loader
# 静态生成
npm install -g grpc-tools
grpc_tools_node_protoc -I. \
--js_out=import_style=commonjs,binary:. \
--grpc_out=. \
--plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` \
hello.proto
Go 服务端
基本服务
// 服务实现
type server struct {
pb.UnimplementedGreeterServer
}
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}
// 启动服务
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
s.Serve(lis)
}
拦截器
func interceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
log.Printf("请求: %s", info.FullMethod)
return handler(ctx, req)
}
s := grpc.NewServer(grpc.UnaryInterceptor(interceptor))
流式服务
// 服务端流
func (s *server) ListItems(req *pb.Request, stream pb.Service_ListItemsServer) error {
for _, item := range items {
stream.Send(item)
}
return nil
}
// 客户端流
func (s *server) Upload(stream pb.Service_UploadServer) error {
for {
chunk, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.Result{Success: true})
}
// 处理 chunk
}
}
// 双向流
func (s *server) Chat(stream pb.Service_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
stream.Send(&pb.Message{Content: "回复"})
}
}
Go 客户端
基本调用
// 创建连接
conn, _ := grpc.Dial("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
defer conn.Close()
// 创建客户端
client := pb.NewGreeterClient(conn)
// 调用
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
resp, _ := client.SayHello(ctx, &pb.HelloRequest{Name: "World"})
流式调用
// 服务端流
stream, _ := client.ListItems(ctx, &pb.Request{})
for {
item, err := stream.Recv()
if err == io.EOF {
break
}
// 处理 item
}
// 客户端流
stream, _ := client.Upload(ctx)
for _, chunk := range chunks {
stream.Send(chunk)
}
result, _ := stream.CloseAndRecv()
// 双向流
stream, _ := client.Chat(ctx)
go func() {
for _, msg := range messages {
stream.Send(msg)
}
stream.CloseSend()
}()
for {
msg, err := stream.Recv()
if err == io.EOF {
break
}
// 处理 msg
}
错误处理
gRPC 错误码
| 错误码 | 值 | 说明 |
|---|---|---|
OK | 0 | 成功 |
Canceled | 1 | 操作被取消 |
Unknown | 2 | 未知错误 |
InvalidArgument | 3 | 无效参数 |
DeadlineExceeded | 4 | 超时 |
NotFound | 5 | 资源不存在 |
AlreadyExists | 6 | 资源已存在 |
PermissionDenied | 7 | 权限不足 |
ResourceExhausted | 8 | 资源耗尽 |
FailedPrecondition | 9 | 前置条件失败 |
Aborted | 10 | 操作中止 |
OutOfRange | 11 | 超出范围 |
Unimplemented | 12 | 未实现 |
Internal | 13 | 内部错误 |
Unavailable | 14 | 服务不可用 |
DataLoss | 15 | 数据丢失 |
Unauthenticated | 16 | 未认证 |
返回错误
import "google.golang.org/grpc/status"
import "google.golang.org/grpc/codes"
// 简单错误
return nil, status.Error(codes.NotFound, "用户不存在")
// 格式化错误
return nil, status.Errorf(codes.InvalidArgument, "参数错误: %s", field)
处理错误
import "google.golang.org/grpc/status"
resp, err := client.SayHello(ctx, req)
if err != nil {
st, ok := status.FromError(err)
if ok {
switch st.Code() {
case codes.NotFound:
// 处理不存在
case codes.DeadlineExceeded:
// 处理超时
}
}
}
元数据
发送元数据
md := metadata.New(map[string]string{
"authorization": "Bearer token",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)
接收元数据
md, ok := metadata.FromIncomingContext(ctx)
if ok {
tokens := md.Get("authorization")
}
TLS/安全
服务端 TLS
creds, _ := credentials.NewServerTLSFromFile("server.crt", "server.key")
s := grpc.NewServer(grpc.Creds(creds))
客户端 TLS
creds, _ := credentials.NewClientTLSFromFile("ca.crt", "example.com")
conn, _ := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
常用命令
# 查看服务定义
grpcurl -plaintext localhost:50051 describe
# 调用服务
grpcurl -plaintext -d '{"name": "World"}' localhost:50051 Greeter/SayHello
# 列出服务
grpcurl -plaintext localhost:50051 list