Go中如何跨语言实现传输? - GRPC
gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 协议和 Protocol Buffers(Protobuf)序列化,支持多语言、流式传输和企业级特性。与 RESTful API 相比,它性能更高、接口定义更规范,特别适合微服务架构和跨语言服务调用。
| 概念 | 作用 | 说明 |
|---|---|---|
| Protocol Buffers(Protobuf) | 接口定义语言(IDL) | 用于定义服务接口和消息结构,跨语言、跨平台,序列化性能极高 |
| Service(服务) | 定义 RPC 方法 | 在 .proto 文件中定义,包含方法名、请求消息、响应消息 |
| Message(消息) | 定义数据结构 | 类似于 Go 的结构体,用于定义请求和响应的数据格式 |
| Unary RPC(一元 RPC) | 最简单的 RPC 模式 | 客户端发送一个请求,服务端返回一个响应,类似传统的 HTTP 请求 |
| Server Streaming RPC(服务端流式) | 服务端流式返回 | 客户端发送一个请求,服务端返回多个响应 |
| Client Streaming RPC(客户端流式) | 客户端流式发送 | 客户端发送多个请求,服务端返回一个响应 |
| Bidirectional Streaming RPC(双向流式) | 双向流式传输 | 客户端和服务端可以同时发送和接收多个消息 |
# 1. 安装 protoc 编译器
# Mac
brew install protobuf
# Linux
apt install -y protobuf-compiler
# 2. 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 3. 安装 gRPC 核心库
go get google.golang.org/grpc
Unary RPC (一元 RPC)
grpc-demo/
├── proto/
│ └── user.proto # Protobuf 定义文件
├── server/
│ └── main.go # gRPC 服务端
├── client/
│ └── main.go # gRPC 客户端
└── go.mod
编写 Protobuf 定义(proto/user.proto)
这是 gRPC 开发的核心,定义接口契约。
// 指定 Protobuf 版本
syntax = "proto3";
// 指定生成的 Go 代码的包路径
option go_package = "./proto";
// 定义包名
package user;
// 定义消息(数据结构)
message User {
uint32 id = 1; // 字段编号:1-15 占 1 字节,常用字段优先
string username = 2;
string email = 3;
}
message GetUserRequest {
uint32 user_id = 1;
}
message GetUserResponse {
User user = 1;
}
// 定义服务
service UserService {
// 一元 RPC 方法
rpc GetUser(GetUserRequest) returns (GetUserResponse);
}
-
syntax = "proto3";:指定使用 Protobuf v3 版本(推荐,比 v2 更简洁)。 -
option go_package = "./proto";:指定生成的 Go 代码的包路径。 -
消息字段编号:每个字段必须有唯一的编号,1-15 占 1 字节,16-2047 占 2 字节,常用字段建议用 1-15。
-
service Xxx { rpc Yyy(Req) returns (Resp) }:定义服务,包含多个 RPC 方法。 -
rpc 方法名(请求) returns (响应):定义 RPC 方法。 -
message Xxx { type field = tag; }:定义消息结构,tag 必须唯一
protoc --go_out=. --go-grpc_out=. proto/user.proto
执行成功后,会在 proto/ 目录下生成两个文件:
user.pb.go:消息结构的 Go 代码user_grpc.pb.go:服务接口的 Go 代码
服务端实现
import (pb "grpc-demo/proto")
// 定义服务结构体,必须嵌入 Unimplemented*Server
type UserServer struct {
pb.UnimplementedUserServiceServer // 必须嵌入,保证向前兼容
}
// 实现 Protobuf 中定义的 RPC 方法
func (s *UserServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
// 核心业务逻辑:查询数据库等
user := &pb.User{
Id: req.UserId,
Username: "zhangsan",
Email: "zhangsan@example.com",
}
return &pb.GetUserResponse{User: user}, nil
}
func main() {
// 监听端口
lis, _ := net.Listen("tcp", ":50051")
// 创建 gRPC 服务器
s := grpc.NewServer()
// 将服务实现注册到 gRPC 服务器
pb.RegisterUserServiceServer(s, &UserServer{})
// 启动服务,监听 TCP 连接
s.Serve(lis)
}
-
type XxxServer struct { pb.UnimplementedXxxServer }:定义服务结构体,必须嵌入 Unimplemented -
pb.UnimplementedUserServiceServer:必须嵌入这个结构体,确保向前兼容,即使后续服务添加了新方法,旧的服务端代码也不会报错。 -
实现服务接口:必须实现
.proto文件中定义的所有 RPC 方法。
客户端实现
import (pb "grpc-demo/proto")
func main() {
// 连接服务端
conn, _ := grpc.Dial(
"localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
defer conn.Close()
// 创建客户端
client := pb.NewUserServiceClient(conn)
// 调用 RPC 方法
ctx := context.Background() // 空根上下文
resp, _ := client.GetUser(ctx, &pb.GetUserRequest{UserId: 1})
fmt.Printf("获取用户:%+v\n", resp.User)
}
client.Yyy(ctx, &Req{}):调用 RPC 方法
拦截器
拦截器类似 Gin 中间件,用于处理通用逻辑(日志、认证、限流)。
// 日志拦截器
func LogInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
// 请求前逻辑
fmt.Printf("收到请求:%s\n", info.FullMethod)
// 调用实际 RPC 方法
resp, err := handler(ctx, req)
// 请求后逻辑
return resp, err
}
// 注册拦截器
func main() {
lis, _ := net.Listen("tcp", ":50051")
// 创建服务器时注册拦截器
s := grpc.NewServer(grpc.UnaryInterceptor(LogInterceptor))
pb.RegisterUserServiceServer(s, &UserServer{})
s.Serve(lis)
}
grpc.NewServer(grpc.UnaryInterceptor(...)):创建 gRPC 服务器并注册拦截器