Golang 27_Golang与gRPC

一、gRPC 简介

1.1 gRPC 的概念

gRPC 是一个高性能、通用的开源 RPC 框架,是一个由 Google 主导开发的 RPC 框架。其以 HTTP/2 为基础通信协议,支持多种语言,通过 protocol buffers 数据格式来实现服务之间的调用。

gRPC 使用 protocol buffers 来实现服务定义和数据序列化。Protocol buffers 是由 Google 开发的数据描述语言,跨平台、跨语言支持良好,性能好、版本兼容性高。

gRPC 框架包含了服务端和客户端两部分。服务端实现 gRPC 服务接口,客户端通过 stub 来调用远程服务。

1.2 gRPC 的优势

  • 基于 HTTP/2 设计,性能高,可扩展性强
  • 支持流式传输,低延迟
  • 支持跨语言调用
  • 支持双向流式通信
  • 支持服务发现及负载均衡
  • Protobuf 格式高效便捷

1.3 gRPC 适用场景

  • 需要高性能、低延迟的服务通信
  • 要实现异构系统、不同语言间的调用
  • 需要流式数据处理的场景
  • 微服务架构下服务间的通信

二、gRPC 详解

2.1 gRPC 架构

gRPC 基于 HTTP/2 协议设计,采用 Protocol Buffers 机制序列化结构化数据,主要包含以下组件:

  • Stub: 客户端调用 gRPC 服务的接口
  • gRPC Server: 实现 gRPC 服务逻辑的服务器
  • Channel: 抽象连接,实现 Socket 级别连接及 RPC 交互
  • Protocol Buffers: 服务接口描述语言和数据序列化机制

在服务器端通过 Protobuf 接口实现服务,客户端通过 Stub 完成远程调用。

gRPC 优化了网络资源利用效率,支持复杂数据交互模式,整体提高了分布式服务架构的性能。

gRPC 的优点包括高效、跨平台、流式传输等。但也存在需要应用 HTTP/2 特性的学习成本,以及被限制在 Protobuf 生态内等问题。

随着云原生技术体系的逐步完善, gRPC 在微服务和 Service Mesh 体系中的地位日益突出,它的重要性会持续提升。预计 gRPC 会越来越多地用于云原生基础设施的打造。

2.2 gRPC 通信流程

gRPC 通信流程主要包括:

  • 客户端调用 Stub 接口
  • Stub 序列化参数为 Protobuf 数据
  • 数据通过 HTTP/2 协议发送给服务器
  • 服务器获取请求数据并反序列化
  • 服务器处理请求并序列化返回结果
  • 通过 HTTP/2 返回序列化后的数据
  • 客户端获取响应数据并反序列化

2.3 Protobuf 数据格式

Protocol Buffer (Protobuf) 是谷歌推出的一种轻便高效的数据序列化格式,主要用于促进数据在网络间高效传输。

Protobuf 的数据格式主要特点:

  • 跨平台、语言中立
  • 版本兼容
  • 体积小,serialize 后数据大小只有 XML 的 1/10 到 1/3
  • 序列化/反序列化速度快

Protobuf 通过 .proto 文件定义数据结构,然后使用 protoc 编译器生成各目标语言的数据访问类。

2.4 gRPC 方法的定义

在 .proto 文件中可以定义服务接口和方法:

1
2
3
service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

方法可以指定请求参数消息类型和返回值消息类型。

2.5 gRPC 服务的实现

Go 语言中实现 gRPC 服务的步骤:

  • 定义服务实现结构体
  • 在结构体中实现服务接口方法
  • 创建 gRPC 服务器
  • 用服务器注册服务

示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type HelloServiceImpl struct{}
   
func (p *HelloServiceImpl) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloResponse, error) {
   // 方法实现
}
func main() {
  server := grpc.NewServer()
  pb.RegisterMessageServiceServer(server, &HelloServiceImpl{})
  server.Serve(lis)
}

2.5 gRPC 客户端调用

Go 语言 gRPC 客户端调用主要分为三步:

  • 建立到 gRPC 服务器的连接
  • 通过连接新建客户端 stub 实例
  • 使用 stub 调用远程服务方法

示例:

1
2
3
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
client := pb.NewHelloServiceClient(conn)
resp, err := client.SayHello(ctx, req) 

三、gRPC 高级用法

3.1 流式 RPC

gRPC 支持流式 RPC 调用,分为四种类型:

  • 单向流式: 客户端流式,只有请求是流
  • 单向流式: 服务器流式,只有响应是流
  • 双向流式: 客户端和服务器端都可以是流

在 proto 文件中使用 stream 关键字定义:

1
2
3
rpc ClientStream(stream HelloRequest) returns (HelloResponse);
rpc ServerStream(HelloRequest) returns (stream HelloResponse); 
rpc Bidirectional(stream HelloRequest) returns (stream HelloResponse);

以下示例实现了客户端流式 RPC 和服务器流式 RPC。客户端可以通过流方式连续发送多个请求,服务器端可以返回流式的响应。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// server
type Server struct { }

func (s *Server) ClientStream
(stream pb.StringService_ClientStreamServer) error {
    for {
        in, err := stream.Recv()
        // 处理
        stream.SendAndClose(&pb.StringReply{})
    } 
}

// client
stream, _ := client.ClientStream(context.Background()) 
for {
    stream.Send(&pb.StringRequest{}) 
}
reply, _ := stream.CloseAndRecv() 

下面演示了如何使用 gRPC 完成双向流 RPC 的编码实现。客户端和服务器端都可以独立地通过流发送多个请求或响应。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// server
type Server struct{} 
   
func (s *Server) Bidirectional(
   stream pb.StringService_BidirectionalServer) error
{ 
 for {
     in, err := stream.Recv()
     if err != io.EOF {
      // 处理请求
      stream.Send(&pb.StringReply{})
     }
  }
}

// client
stream, _ := client.Bidirectional(context.Background())

go func() {
  for {
    stream.Send(&pb.StringRequest{})
  } 
}()

for {
  reply, err := stream.Recv()
  if err != nil {
    break
  } 
}

3.2 证书和认证

gRPC 支持 SSL/TLS 安全传输及各种身份认证方式:

  • SSL/TLS 传输级安全保障
  • 支持基于证书、Token 和 AWS IAM 等认证手段

3.3 错误处理

gRPC 框架定义了状态码和错误模型,客户端可以根据状态码判断 RPC 调用是否成功:

  • OK: 调用成功
  • Cancelled: 调用被取消
  • Unknown: 未知错误
  • InvalidArgument: 参数无效
  • DeadlineExceeded: 超时错误等

3.4 超时和取消

gRPC 支持请求级别的超时控制,通过 Context 指定超时时间,还可以通过 Context 取消正在执行的 RPC。

3.5 gRPC 拦截器

gRPC 支持在服务器端和客户端使用拦截器(Interceptor)拦截请求:

  • 客户端拦截器: 拦截出站请求及响应
  • 服务端拦截器: 拦截入站请求及响应

拦截器主要用于日志记录、监控等功能。

3.6 gRPC 元数据

gRPC 通过自定义元数据提供请求上下文等附加信息。可以在请求和响应中设置和获取元数据。

3.7 gRPC 路由

gRPC 支持按服务方法特征进行请求路由,路由选择不同的后端服务。主要通过 gRPC intestine 实现。

3.8 gRPC 和 HTTP/2 的对比

功能点 gRPC HTTP/2
连接方式 persistent connection persistent connection
数据格式 Protobuf JSON
数据压缩 支持 不支持
流式传输 支持 不支持
IDL 接口定义 支持 不支持