跳到主要内容

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 错误码

错误码说明
OK0成功
Canceled1操作被取消
Unknown2未知错误
InvalidArgument3无效参数
DeadlineExceeded4超时
NotFound5资源不存在
AlreadyExists6资源已存在
PermissionDenied7权限不足
ResourceExhausted8资源耗尽
FailedPrecondition9前置条件失败
Aborted10操作中止
OutOfRange11超出范围
Unimplemented12未实现
Internal13内部错误
Unavailable14服务不可用
DataLoss15数据丢失
Unauthenticated16未认证

返回错误

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