学习碎笔-关于gRPC
一:gRPC 概述与核心特性
gRPC 是 Google 开发的高性能、跨语言的远程过程调用(RPC)框架,基于 HTTP/2 和 Protocol Buffers(Protobuf)实现。其核心特性包括:
- 多语言支持:服务端与客户端可使用不同语言(如 Go、Java、Python)实现,通过 Protobuf 定义统一接口。
- 高效传输:HTTP/2 提供多路复用、头部压缩等优化,Protobuf 二进制编码减少数据体积。
- 流式通信:支持单向/双向流(Streaming),适用于实时数据传输场景。
- 强类型约束:接口通过 Protobuf 预先定义,避免传统 REST API 的松散类型问题。
Go 案例准备工作
在 Go 中使用 gRPC 需以下工具:
protoc
:Protobuf 编译器(生成代码模板)
下载地址:点这里,下载好解压后添加到环境变量里面。
grpc-go
库:提供 gRPC 核心实现
安装命令:- 安装gRPC的核心库
go install google.golang.org/grpc@latest
- 安装代码生成工具
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Go项目中关于grcpde一般目录结构
G:.
├───api
├───grpc # 新建目录
│ ├───pb # 存放 .proto 文件和生成的代码
│ ├───server # gRPC server 实现
│ └───client # gRPC client 代码(如果需要)
├───config
├───db
└───internal
- gRPC 相关代码最好独立管理,方便维护和扩展
- pb 目录存放 protocol buffers 定义和生成的代码
- server 目录包含具体的服务实现
- client 用来存放调用其他grpc服务端的代码
因为一个项目中一般不止会被调用,也可能调用其他的grpc服务端,所以server
和client
一般都会存在
二:定义服务接口与代码生成
要构建 gRPC 服务,首先需用 Protobuf 定义服务接口。以下是一个简单的 .proto
文件示例:
// service.proto
syntax = "proto3";
package example;
service CalculatorService {
rpc Add (AddRequest) returns (AddResponse);
}
message AddRequest {
int32 a = 1;
int32 b = 2;
}
message AddResponse {
int32 result = 1;
}
关键元素解析:
- service:声明服务名称与方法(
Add
为 RPC 方法名) - message:定义请求/响应数据结构(字段编号影响二进制序列化顺序)
生成 Go 代码
使用 protoc
编译器生成对应 Go 代码:
protoc --proto_path=. --go_out=./gen service.proto
生成的 service.pb.go
文件包含:
AddRequest
与AddResponse
结构体CalculatorServiceClient
与CalculatorServiceServer
接口
服务端实现(Go 代码)
创建服务端需实现 Protobuf 生成的接口:
package main
import (
"gen/example"
"grpc"
"net"
)
type CalculatorServer struct{}
func (s *CalculatorServer) Add(ctx context.Context, req *example.AddRequest) (*example.AddResponse, error) {
result := req.A + req.B
return &example.AddResponse{Result: result}, nil
}
func main() {
lis, _ := net.Listen("tcp", ":5000")
server := grpc.NewServer()
example.RegisterCalculatorServiceServer(server, &CalculatorServer{})
server.Serve(lis)
}
要点说明:
- 服务端结构体需实现所有 RPC 方法(此处仅
Add
) RegisterCalculatorServiceServer
将实现绑定到 gRPC 服务器
三:客户端实现与流式通信案例
客户端调用示例(Go 代码)
客户端通过生成的 CalculatorServiceClient
调用远程方法:
package main
import (
"context"
"gen/example"
"grpc"
"log"
)
func main() {
conn, err := grpc.Dial("localhost:5000", grpc.WithInsecure())
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := example.NewCalculatorServiceClient(conn)
req := &example.AddRequest{A: 10, B: 20}
res, err := client.Add(context.Background(), req)
if err != nil {
log.Fatal(err)
}
log.Printf("Result: %d", res.Result) // 输出 30
}
关键步骤:
grpc.Dial
建立连接(生产环境应启用 TLS 替代WithInsecure
)- 客户端调用方法需传递
context
对象(用于超时控制等)
流式通信实战:斐波那契数列生成
gRPC 流式通信适合实时数据场景。以下为双向流示例:
- Proto 定义(追加流方法)
service CalculatorService {
// ...原有 Add 方法
rpc GenerateFibonacci (FibonacciRequest) returns (stream FibonacciResponse);
}
message FibonacciRequest {
int32 max_count = 1;
}
message FibonacciResponse {
int32 value = 1;
}
stream
关键字标记响应为流式(服务端持续发送)
- 服务端流实现(Go 代码)
func (s *CalculatorServer) GenerateFibonacci(req *example.FibonacciRequest, stream example.CalculatorService_GenerateFibonacciServer) error {
a, b := 0, 1
for count := 0; count < req.MaxCount; count++ {
if err := stream.Send(&example.FibonacciResponse{Value: a}); err != nil {
return err
}
a, b = b, a+b
}
return nil
}
- 通过
stream.Send
循环发送数列值
- 客户端流接收(Go 代码)
stream, err := client.GenerateFibonacci(context.Background(), &example.FibonacciRequest{MaxCount: 5})
if err != nil {
log.Fatal(err)
}
for {
res, err := stream.Recv()
if err == io.EOF {
break
}
log.Printf("Fibonacci: %d", res.Value) // 依次接收 0,1,1,2,3
}
Recv()
循环读取直到流结束(io.EOF
信号)
流式应用场景
- 实时日志推送
- 大规模数据集分块传输
- 双向交互(如聊天协议)
四:实现 gRPC TLS 加密传输与 Token 认证
1. TLS 加密传输实现(传输层安全)
步骤 1: 生成 TLS 证书(示例使用自签名证书)
# 生成 CA 证书
openssl req -x509 -newkey rsa:2048 -days 365 -out ca.crt -keyout ca.key -subj "/CN=MyCA"
# 生成服务端证书
openssl req -x509 -newkey rsa:2048 -days 365 -out server.crt -keyout server.key -subj "/CN=localhost" -CA ca.crt -CAkey ca.key
步骤 2: 服务端 TLS 配置(Go 代码)
import (
"grpc"
"grpc/credentials"
)
func main() {
// 加载服务端证书
cert, err := credentials.NewServerCertFromFile("server.crt", "server.key")
if err != nil {
log.Fatal(err)
}
// 创建 gRPC 服务器并启用 TLS
server := grpc.NewServer(
grpc.WithTransportCredentials(cert),
)
// 注册服务逻辑...
server.Start(":5000")
}
步骤 3: 客户端 TLS 连接(Go 代码)
import (
"grpc"
"grpc/credentials"
)
func main() {
// 加载 CA 证书(验证服务端身份)
creds, err := credentials.NewClientCertFromCAFile("ca.crt")
if err != nil {
log.Fatal(err)
}
// 建立 TLS 加密连接
conn, err := grpc.Dial("localhost:5000",
grpc.WithTransportCredentials(creds),
)
defer conn.Close()
// 调用服务方法...
}
关键点
- 生产环境应使用正式 CA 颁发的证书(如 Let’s Encrypt)
- 客户端需信任服务端证书的 CA(此处通过
ca.crt
验证)
2. Token 认证实现(应用层安全)
步骤 1: 客户端添加 Token(拦截器注入元数据)
import (
"grpc"
"grpc/metadata"
)
// 客户端拦截器:注入 Authorization 头
func authInterceptor(ctx context.Context, method string, req interface{}, reply interface{}, conn *grpc.ClientConn, opts ...grpc.CallOption) error {
// 创建元数据并添加 Token
md := metadata.New()
md.Append("authorization", "Bearer my-secret-token")
// 将元数据绑定到上下文
ctx = metadata.NewOutgoingContext(ctx, md)
// 执行原始调用
return conn.Invoke(ctx, method, req, reply, opts...)
}
// 使用拦截器连接
conn := grpc.Dial("localhost:5000",
grpc.WithTransportCredentials(creds),
grpc.WithUnaryInterceptor(authInterceptor),
)
步骤 2: 服务端 Token 验证(拦截器检查元数据)
import (
"grpc"
"grpc/metadata"
"context"
)
// 服务端拦截器:校验 Token
func authCheckInterceptor(ctx context.Context, req interface{}, info *grpc.ServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// 从上下文提取元数据
md, ok := metadata.FromContext(ctx)
if !ok {
return nil, grpc.Errorf(grpc.StatusUnauthenticated, "缺少元数据")
}
// 获取 Authorization 头
tokens := md.Get("authorization")
if len(tokens) == 0 {
return nil, grpc.Errorf(grpc.StatusUnauthenticated, "未提供 Token")
}
// 验证 Token 有效性(示例简化逻辑)
if tokens[0] != "Bearer my-secret-token" {
return nil, grpc.Errorf(grpc.StatusUnauthenticated, "无效 Token")
}
// 调用原始处理逻辑
return handler(ctx, req)
}
// 注册拦截器
server := grpc.NewServer(
grpc.WithTransportCredentials(cert),
grpc.WithUnaryInterceptor(authCheckInterceptor),
)
验证失败处理
- 客户端将收到
grpc.StatusUnauthenticated
错误(代码 401) - 可扩展拦截器实现更复杂的鉴权逻辑(如 JWT 解析、过期检查)
3. 双重安全组合(TLS + Token)
- 传输层:TLS 确保数据加密与服务器身份可信
- 应用层:Token 认证实现业务级访问控制
- 拦截器顺序:服务端应先执行 Token 校验,再处理业务逻辑
补充建议
- Token 管理
- 使用 JWT 替代静态 Token,添加过期时间与签名校验
- 定期轮换 Token 降低泄露风险
- 证书动态加载
- 监听文件变化(如
fsnotify
)实现证书热更新 - 避免服务重启中断现有连接
- 监听文件变化(如
通过上述实现,可在 Go 语言构建同时具备传输加密与应用认证的 gRPC 安全通信体系。
五:高级特性与部署实践
1. 拦截器(Interceptor)实战
gRPC 拦截器可用于实现日志记录、身份验证等中间件逻辑。
服务端拦截器示例(Go 代码)
// 日志拦截器
func logInterceptor(ctx context.Context, req interface{}, info *grpc.ServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
log.Printf("Received request: %+v", req)
start := time.Now()
resp, err = handler(ctx, req)
log.Printf("Handler duration: %s", time.Since(start))
return resp, err
}
// 注册拦截器
server := grpc.NewServer(
grpc.WithUnaryInterceptor(logInterceptor),
)
客户端拦截器示例
// 请求头注入拦截器
func authInterceptor(ctx context.Context, method string, req interface{}, reply interface{}, conn *grpc.ClientConn, opts ...grpc.CallOption) error {
md := metadata.New()
md.Append("authorization", "Bearer token123")
ctx = metadata.NewOutgoingContext(ctx, md)
return conn.Invoke(ctx, method, req, reply, opts...)
}
// 使用拦截器创建连接
conn := grpc.Dial("localhost:5000",
grpc.WithUnaryInterceptor(authInterceptor),
)
拦截器类型
- UnaryInterceptor:处理普通 RPC 请求
- StreamInterceptor:处理流式 RPC
2. 负载均衡策略
gRPC 客户端支持多种内置负载均衡模式:
conn := grpc.Dial("service.example.com",
grpc.WithLBPolicy(grpc.RoundRobinBalancer), // 轮询策略
grpc.WithResolver(dnsResolver), // DNS 解析器
)
常用策略
- RoundRobin:轮询可用服务端点
- WeightedRandom:按权重随机选择
- 自定义策略:实现
grpc.Balancer
接口
5. 部署注意事项
- 端口规划:gRPC 通常使用 5000-10000 范围端口,避免与 HTTP 服务冲突
- 资源限制:设置
grpc.MaxConnections(100)
防止服务过载 - 健康检查:集成 Prometheus 或自定义健康检查端点
- 错误重试:客户端配置
grpc.WithRetryPolicy
处理瞬态故障
6.性能优化技巧
- 启用 Protobuf 序列化缓存:减少重复结构编码开销
- 压缩头部:配置
grpc.WithCompression(headers)
降低传输体积 - 连接池复用:避免频繁建立新连接
gRPC 技术总结:核心优势与最佳实践
一、gRPC 核心优势
- 高性能通信
- 基于 Protobuf 二进制序列化,相比 JSON 体积更小、解析更快
- 内置 HTTP/2 多路复用,减少连接开销,提升并发吞吐量
- 强类型化与跨语言支持
.proto
文件定义 明确的接口契约,避免客户端/服务端语义不一致- 自动生成 多语言代码(Go、Java、Python 等),简化跨团队协作
- 流式通信能力
- 支持 单向/双向流式数据传输,适用于实时推送、分批处理等场景
- 丰富的生态系统
- 内置 拦截器、负载均衡、TLS 等模块,降低基础设施开发成本
- 与 Kubernetes、Prometheus 等现代运维工具深度集成
二、最佳实践指南
- 安全优先
- 生产环境 强制启用 TLS,避免明文传输敏感数据
- 使用 拦截器统一实现鉴权、日志,确保全局安全策略
- 协议设计原则
- 精简消息字段,避免冗余数据(Protobuf 字段删除不向后兼容)
- 明确区分普通 RPC 与流式方法,防止误用导致性能问题
- 运维与性能优化
- 客户端配置 负载均衡与重试策略,提升服务可用性
- 服务端设置 连接数限制与资源监控,防止过载崩溃
- 启用 Protobuf 序列化缓存,减少重复结构编码开销
- 代码管理规范
- 集中管理
.proto
文件,确保客户端/服务端代码版本同步 - 生成代码 禁止手动修改,变更需重新编译协议文件
- 集中管理
- 监控与调试
- 集成 OpenTelemetry 或 Prometheus,捕获 RPC 延迟、错误率等指标
- 使用 gRPC 反射接口 或工具(如
grpcurl
)进行端点调试
三、适用场景推荐
- 微服务间高速通信(替代传统 REST API)
- 实时数据管道(如金融行情推送、IoT 传感器流)
- 大规模分布式系统(利用负载均衡与健康检查机制)
gRPC 凭借其性能与契约优先的设计,已成为现代分布式系统的核心通信框架。遵循上述实践可最大化其价值,构建高效、可靠的服务网格。