gRPC是Google开源的高性能RPC框架,基于HTTP/2协议和Protocol Buffers序列化机制,在微服务架构中逐渐替代传统REST API。相比RESTful的JSON文本序列化,gRPC使用二进制Protobuf编码,数据体积更小、序列化速度更快。后端开发中,gRPC特别适合内部服务间高频通信场景,双向流式通信模型也为实时数据传输提供了高效方案。
gRPC与REST的协议层差异
gRPC与REST的核心差异在于传输协议和序列化格式。REST基于HTTP/1.1,使用JSON文本序列化,每次请求独立建立连接。gRPC基于HTTP/2,复用连接、支持多路复用和流式传输,使用Protobuf二进制序列化。
性能层面,Protobuf序列化体积通常为JSON的1/3到1/2,解析速度快5-10倍。HTTP/2的多路复用避免了HTTP/1.1的队头阻塞问题,单连接可并发处理数百个请求。对于微服务间每秒上万次调用的场景,这些差异累积成显著的性能优势。
gRPC定义四种服务方法模型:Unary RPC(一元调用,请求-响应模式)、Server Streaming RPC(服务端流,一个请求返回流式响应)、Client Streaming RPC(客户端流,流式请求一个响应)、Bidirectional Streaming RPC(双向流,双向流式通信)。
Protobuf接口定义与代码生成
gRPC使用.proto文件定义服务接口和消息格式。定义一个用户服务示例:
// user.proto
syntax = "proto3";
package user.v1;
option go_package = "yunthe.com/proto/user/v1;userv1";
// 用户服务定义
service UserService {
// Unary: 创建用户
rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
// Unary: 查询用户
rpc GetUser(GetUserRequest) returns (GetUserResponse);
// Server Streaming: 批量查询
rpc ListUsers(ListUsersRequest) returns (stream User);
// Bidirectional Streaming: 实时消息
rpc ChatStream(stream ChatMessage) returns (stream ChatMessage);
}
message CreateUserRequest {
string username = 1;
string email = 2;
int32 age = 3;
repeated string tags = 4;
}
message CreateUserResponse {
int64 user_id = 1;
string username = 2;
int64 created_at = 3;
}
message GetUserRequest {
int64 user_id = 1;
}
message GetUserResponse {
int64 user_id = 1;
string username = 2;
string email = 3;
int32 age = 4;
repeated string tags = 5;
int64 created_at = 6;
}
message ListUsersRequest {
int32 page = 1;
int32 page_size = 2;
}
message User {
int64 user_id = 1;
string username = 2;
string email = 3;
}
message ChatMessage {
int64 user_id = 1;
string content = 2;
int64 timestamp = 3;
}
使用protoc编译器生成Go代码:
# 安装protoc和插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 生成Go代码
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
user.proto
# 生成文件:
# user.pb.go - Protobuf消息序列化/反序列化代码
# user_grpc.pb.go - gRPC服务端和客户端代码
gRPC服务端实现与拦截器配置
生成代码后,实现服务端逻辑:
package main
import (
"context"
"log"
"net"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/grpc/credentials"
pb "yunthe.com/proto/user/v1"
)
type userServiceServer struct {
pb.UnimplementedUserServiceServer
users map[int64]*pb.User
nextID int64
}
// Unary方法实现
func (s *userServiceServer) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
s.nextID++
user := &pb.User{
UserId: s.nextID,
Username: req.GetUsername(),
Email: req.GetEmail(),
}
s.users[s.nextID] = user
return &pb.CreateUserResponse{
UserId: s.nextID,
Username: req.GetUsername(),
CreatedAt: time.Now().Unix(),
}, nil
}
func (s *userServiceServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
user, ok := s.users[req.GetUserId()]
if !ok {
return nil, status.Errorf(codes.NotFound, "user %d not found", req.GetUserId())
}
return &pb.GetUserResponse{
UserId: user.UserId,
Username: user.Username,
Email: user.Email,
}, nil
}
// Server Streaming方法实现
func (s *userServiceServer) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
for _, user := range s.users {
if err := stream.Send(user); err != nil {
return err
}
}
return nil
}
// 日志拦截器
func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInterceptor, handler grpc.UnaryHandler) (interface{}, error) {
start := time.Now()
resp, err := handler(ctx, req)
log.Printf("method=%s duration=%v err=%v", info.FullMethod, time.Since(start), err)
return resp, err
}
// Recovery拦截器(panic恢复)
func recoveryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInterceptor, handler grpc.UnaryHandler) (resp interface{}, err error) {
defer func() {
if r := recover(); r != nil {
log.Printf("panic recovered: %v", r)
err = status.Errorf(codes.Internal, "internal error")
}
}()
return handler(ctx, req)
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("listen failed: %v", err)
}
// TLS配置
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
if err != nil {
log.Fatalf("TLS config failed: %v", err)
}
s := grpc.NewServer(
grpc.Creds(creds),
grpc.UnaryInterceptor(grpc.ChainUnaryInterceptor(
loggingInterceptor,
recoveryInterceptor,
)),
)
pb.RegisterUserServiceServer(s, &userServiceServer{
users: make(map[int64]*pb.User),
nextID: 0,
})
log.Println("gRPC server listening on :50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("serve failed: %v", err)
}
}
拦截器链按顺序执行,loggingInterceptor记录请求耗时,recoveryInterceptor捕获panic防止服务崩溃。生产环境还需加入认证拦截器、限流拦截器等。
gRPC客户端调用与负载均衡
package main
import (
"context"
"io"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
pb "yunthe.com/proto/user/v1"
)
func main() {
// 客户端连接(开发环境使用insecure)
conn, err := grpc.Dial("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy":"round_robin"}`),
)
if err != nil {
log.Fatalf("dial failed: %v", err)
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Unary调用
createResp, err := client.CreateUser(ctx, &pb.CreateUserRequest{
Username: "test_user",
Email: "test@yunthe.com",
Age: 25,
Tags: []string{"admin", "active"},
})
if err != nil {
log.Fatalf("create user failed: %v", err)
}
log.Printf("created user ID: %d", createResp.GetUserId())
// Server Streaming调用
stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{Page: 1, PageSize: 10})
if err != nil {
log.Fatalf("list users failed: %v", err)
}
for {
user, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("recv failed: %v", err)
}
log.Printf("user: %d %s %s", user.GetUserId(), user.GetUsername(), user.GetEmail())
}
}
生产环境负载均衡使用DNS解析多个后端地址,配合round_robin策略在客户端做请求分发。gRPC的客户端负载均衡避免了服务端负载均衡器的单点问题。也可以通过xDS协议接入Istio等服务网格,实现服务端代理模式的负载均衡。
gRPC在微服务通信中的优势集中在序列化效率和连接复用上。Protobuf严格类型约束减少了接口对接成本,HTTP/2流式通信对实时性要求高的场景提供了比WebSocket更规范的方案。在服务治理层面,gRPC生态的拦截器机制使认证、限流、链路追踪可以统一实现。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/grpc-wei-fu-wu-tong-xin-xie-yi-yu-protobuf-xu-lie-hua-shi/