Skip to content

gRPC

欢迎来到 gRPC 知识库!

⚡ gRPC 简介

gRPC 是 Google 开发的高性能、开源的 RPC (Remote Procedure Call) 框架。它使用 Protocol Buffers 作为接口定义语言(IDL)和底层消息交换格式,基于 HTTP/2 传输协议。

🎯 gRPC vs REST vs GraphQL

特性gRPCRESTGraphQL
协议HTTP/2HTTP/1.1HTTP/1.1
数据格式Protobuf (二进制)JSON (文本)JSON (文本)
性能⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
浏览器支持有限(需 gRPC-Web)
流式传输✅ 双向流部分(Subscription)
类型安全✅ 强类型
代码生成✅ 自动
学习曲线🔴 较陡🟢 平缓🟡 中等

✅ gRPC 优势

1. 高性能

  • HTTP/2 - 多路复用、头部压缩
  • Protobuf - 二进制序列化,体积小
  • 流式传输 - 服务端流、客户端流、双向流

2. 强类型

  • 使用 .proto 文件定义接口
  • 自动生成类型安全的客户端和服务端代码

3. 多语言支持

  • 官方支持 10+ 语言
  • 相同的 .proto 文件生成不同语言代码

4. 流式传输

  • 服务端流
  • 客户端流
  • 双向流

🎯 适用场景

✅ 特别适合

  • 微服务通信 - 服务间高性能调用
  • 实时通信 - 流式数据传输
  • 移动端 - 网络带宽有限
  • 多语言环境 - 跨语言 RPC
  • 内部 API - 不面向浏览器

⚠️ 不太适合

  • 浏览器直接调用(需要 gRPC-Web)
  • 需要人类可读的 API(JSON 更友好)
  • 简单的 CRUD 场景(REST 更简单)

📖 Protocol Buffers

.proto 文件定义

protobuf
syntax = "proto3";

package user;

// 消息定义
message User {
  int64 id = 1;
  string username = 2;
  string email = 3;
  int32 age = 4;
}

message GetUserRequest {
  int64 id = 1;
}

message ListUsersRequest {
  int32 page = 1;
  int32 page_size = 2;
}

message ListUsersResponse {
  repeated User users = 1;
  int32 total = 2;
}

// 服务定义
service UserService {
  // 一元 RPC
  rpc GetUser (GetUserRequest) returns (User);
  
  // 服务端流式 RPC
  rpc ListUsers (ListUsersRequest) returns (stream User);
  
  // 客户端流式 RPC
  rpc CreateUsers (stream User) returns (CreateUsersResponse);
  
  // 双向流式 RPC
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}

数据类型

Proto 类型GoJavaPythonNode
doublefloat64doublefloatnumber
floatfloat32floatfloatnumber
int32int32intintnumber
int64int64longintnumber/string
stringstringStringstrstring
boolboolbooleanboolboolean
bytes[]byteByteStringbytesBuffer

🚀 快速开始

1. 安装工具

bash
# 安装 protoc 编译器
# macOS
brew install protobuf

# 安装 Go 插件
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

2. 定义服务

protobuf
// user.proto
syntax = "proto3";

package user;

option go_package = "example.com/user";

message User {
  int64 id = 1;
  string username = 2;
  string email = 3;
}

message GetUserRequest {
  int64 id = 1;
}

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
}

3. 生成代码

bash
protoc --go_out=. --go-grpc_out=. user.proto

4. 实现服务端 (Go)

go
package main

import (
    "context"
    "log"
    "net"

    pb "example.com/user"
    "google.golang.org/grpc"
)

// 实现 UserService
type server struct {
    pb.UnimplementedUserServiceServer
}

func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    // 模拟数据库查询
    return &pb.User{
        Id:       req.Id,
        Username: "zhangsan",
        Email:    "zhang@example.com",
    }, nil
}

func main() {
    // 监听端口
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }

    // 创建 gRPC 服务器
    s := grpc.NewServer()
    
    // 注册服务
    pb.RegisterUserServiceServer(s, &server{})
    
    log.Println("gRPC server listening on :50051")
    
    // 启动服务
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

5. 实现客户端 (Go)

go
package main

import (
    "context"
    "log"
    "time"

    pb "example.com/user"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

func main() {
    // 连接服务器
    conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(insecure.NewCredentials()))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()

    // 创建客户端
    client := pb.NewUserServiceClient(conn)

    // 调用方法
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    user, err := client.GetUser(ctx, &pb.GetUserRequest{Id: 1})
    if err != nil {
        log.Fatalf("could not get user: %v", err)
    }

    log.Printf("User: %v", user)
}

🌊 流式 RPC

1. 服务端流式

protobuf
service UserService {
  rpc ListUsers (ListUsersRequest) returns (stream User);
}
go
// 服务端
func (s *server) ListUsers(req *pb.ListUsersRequest, stream pb.UserService_ListUsersServer) error {
    users := []pb.User{
        {Id: 1, Username: "user1"},
        {Id: 2, Username: "user2"},
        {Id: 3, Username: "user3"},
    }
    
    for _, user := range users {
        if err := stream.Send(&user); err != nil {
            return err
        }
    }
    
    return nil
}

// 客户端
stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{})
for {
    user, err := stream.Recv()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("User: %v", user)
}

2. 客户端流式

protobuf
service UserService {
  rpc CreateUsers (stream User) returns (CreateUsersResponse);
}
go
// 客户端
stream, err := client.CreateUsers(ctx)
users := []pb.User{
    {Username: "user1"},
    {Username: "user2"},
}

for _, user := range users {
    if err := stream.Send(&user); err != nil {
        log.Fatal(err)
    }
}

reply, err := stream.CloseAndRecv()

3. 双向流式

protobuf
service ChatService {
  rpc Chat (stream ChatMessage) returns (stream ChatMessage);
}
go
// 客户端
stream, err := client.Chat(ctx)

// 发送
go func() {
    for {
        if err := stream.Send(&pb.ChatMessage{...}); err != nil {
            log.Fatal(err)
        }
        time.Sleep(time.Second)
    }
}()

// 接收
for {
    msg, err := stream.Recv()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("Received: %v", msg)
}

🔒 拦截器(中间件)

一元拦截器

go
// 服务端拦截器
func loggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
    start := time.Now()
    
    log.Printf("Method: %s", info.FullMethod)
    
    resp, err := handler(ctx, req)
    
    log.Printf("Duration: %v", time.Since(start))
    
    return resp, err
}

// 使用拦截器
s := grpc.NewServer(
    grpc.UnaryInterceptor(loggingInterceptor),
)

流式拦截器

go
func streamLoggingInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    log.Printf("Stream Method: %s", info.FullMethod)
    return handler(srv, ss)
}

s := grpc.NewServer(
    grpc.StreamInterceptor(streamLoggingInterceptor),
)

🔐 认证

1. 使用 Metadata

go
// 客户端
md := metadata.New(map[string]string{
    "authorization": "Bearer token",
})
ctx := metadata.NewOutgoingContext(context.Background(), md)

// 服务端
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return nil, status.Error(codes.Unauthenticated, "missing metadata")
    }
    
    token := md.Get("authorization")
    // 验证 token
    
    return &pb.User{...}, nil
}

2. 使用 TLS

go
// 服务端
creds, err := credentials.NewServerTLSFromFile("server.crt", "server.key")
s := grpc.NewServer(grpc.Creds(creds))

// 客户端
creds, err := credentials.NewClientTLSFromFile("server.crt", "")
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))

🔍 错误处理

go
import "google.golang.org/grpc/status"
import "google.golang.org/grpc/codes"

// 服务端
func (s *server) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    if req.Id <= 0 {
        return nil, status.Error(codes.InvalidArgument, "invalid user id")
    }
    
    user := findUser(req.Id)
    if user == nil {
        return nil, status.Error(codes.NotFound, "user not found")
    }
    
    return user, nil
}

// 客户端
user, err := client.GetUser(ctx, &pb.GetUserRequest{Id: 1})
if err != nil {
    st, ok := status.FromError(err)
    if ok {
        log.Printf("Code: %v, Message: %s", st.Code(), st.Message())
    }
}

常用状态码:

  • codes.OK - 成功
  • codes.InvalidArgument - 参数错误
  • codes.NotFound - 未找到
  • codes.Unauthenticated - 未认证
  • codes.PermissionDenied - 无权限
  • codes.Internal - 服务器内部错误

💡 最佳实践

  1. 合理设计 API

    • 遵循命名规范
    • 使用有意义的消息名称
    • 合理使用流式 RPC
  2. 错误处理

    • 使用标准错误码
    • 提供有意义的错误信息
  3. 超时设置

    • 客户端设置合理的超时时间
    • 使用 context 控制生命周期
  4. 连接复用

    • 复用 gRPC 连接
    • 避免频繁创建连接
  5. 负载均衡

    • 使用 gRPC 负载均衡
    • 结合服务发现
  6. 监控

    • 使用拦截器记录日志
    • 集成 Prometheus 监控

📖 学习资源

官方资源

推荐教程

工具推荐

  • grpcurl - gRPC 命令行工具
  • BloomRPC - gRPC GUI 客户端
  • grpcui - gRPC Web UI

准备好了吗?开始你的 gRPC 学习之旅!