Go语言与gRPC的完美结合

简介: Go语言与gRPC的完美结合

一、gRPC 简介

gRPC(Remote Procedure Call)是一种远程过程调用技术,通过压缩和序列化数据来优化网络通信,可以显著提高服务调用的性能和效率。

1. gRPC 的概念

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

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

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

2. gRPC 的优势

基于 HTTP/2 设计,性能高,可扩展性强

支持流式传输,低延迟

支持跨语言调用

支持双向流式通信

支持服务发现及负载均衡

Protobuf 格式高效便捷

3. gRPC 适用场景

需要高性能、低延迟的服务通信

要实现异构系统、不同语言间的调用

需要流式数据处理的场景

微服务架构下服务间的通信


 

二、gRPC 详解

1. gRPC 架构

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

Stub:客户端调用 gRPC 服务的接口

gRPC Server:实现 gRPC 服务逻辑的服务器

Channel:抽象连接,实现 Socket 级别连接及 RPC 交互

Protocol Buffers:服务接口描述语言和数据序列化机制

e4218dbfab88ebbf07db55659af4fc0d_640_wx_fmt=png&from=appmsg&wxfrom=5&wx_lazy=1&wx_co=1.png

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

2. gRPC 通信流程

gRPC 通信流程主要包括:

客户端调用 Stub 接口

Stub 序列化参数为 Protobuf 数据

数据通过 HTTP/2 协议发送给服务器

服务器获取请求数据并反序列化

服务器处理请求并序列化返回结果

通过 HTTP/2 返回序列化后的数据

客户端获取响应数据并反序列化

3. Protobuf 数据格式

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

Protobuf 的数据格式主要特点:

跨平台、语言中立

版本兼容

体积小,serialize 后数据大小只有 XML 的 1/10 到 1/3

序列化/反序列化速度快

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

4. gRPC 方法的定义

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


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

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

5. gRPC 服务的实现

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

定义服务实现结构体

在结构体中实现服务接口方法

创建 gRPC 服务器

用服务器注册服务

示例:


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)}

6. gRPC 客户端调用

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

建立到 gRPC 服务器的连接

通过连接新建客户端 stub 实例

使用 stub 调用远程服务方法

示例:


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


 

三、gRPC 高级用法

1. 流式 RPC

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

单向流式:客户端流式,只有请求是流

单向流式:服务器流式,只有响应是流

双向流式:客户端和服务器端都可以是流

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


rpc ClientStream(stream HelloRequest) returns (HelloResponse);rpc ServerStream(HelloRequest) returns (stream HelloResponse); rpc Bidirectional(stream HelloRequest) returns (stream HelloResponse);

2. 证书和认证

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

SSL/TLS 传输级安全保障

支持基于证书、Token 和 AWS IAM 等认证手段

3. 错误处理

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

OK:调用成功

Cancelled:调用被取消

Unknown:未知错误

InvalidArgument:参数无效

DeadlineExceeded:超时错误等

4. 超时和取消

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

5. gRPC 拦截器

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

客户端拦截器:拦截出站请求及响应

服务端拦截器:拦截入站请求及响应

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

6. gRPC 元数据

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

7. gRPC 路由

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

8. 和 HTTP/2 的对比

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


 

四、gRPC 实践案例

1. 简单 RPC 服务端与客户端

简单的 gRPC Server 端和 Client 端示例,演示基本的 RPC 服务开发、注册和调用流程。


// server   type Server struct{}      func (s *Server) SayHello(ctx context.Context, in *pb.StringRequest) (*pb.StringReply, error) {     return &pb.StringReply{Value: "Hello " + in.Value}, nil   }      func main() {     grpcServer := grpc.NewServer()     pb.RegisterMessageServiceServer(grpcServer, &Server{})     grpcServer.Serve(lis)    }      // client   conn, _ := grpc.Dial("localhost:1234", grpc.WithInsecure())   client := pb.NewStringServiceClient(conn)      reply, _ := client.SayHello(context.Background(), &pb.StringRequest{Value: "world"})   fmt.Println(reply.Value)

2. 带参数验证的 RPC 服务

示例在 gRPC 服务中实现参数校验逻辑,如果参数名称不符合规范,将返回错误。


// server   type Server struct{}      func (s *Server) SayHello(ctx context.Context, in *pb.StringRequest) (*pb.StringReply, error) {       if ok := validate(in.Value); !ok {           return nil, status.Error(codes.InvalidArgument, "Invalid parameter")       }          return &pb.StringReply{Value: "Hello " + in.Value}, nil    }      // client   r, err := client.SayHello(context.Background(), &pb.StringRequest{Value: "World!"})   if err != nil {     //错误处理    }

通过状态码和错误信息,客户端可以明确判断是什么原因导致的调用异常。

3. 客户端、服务端流 RPC

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


// 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()

4. 双向流 RPC

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


// servertype 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     }    }


 

总结

通过实践表明,Go 语言结合 gRPC 框架可以方便高效地实现各类 RPC 服务。gRPC 优化了网络资源利用效率,支持复杂数据交互模式,整体提高了分布式服务架构的性能。

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

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

目录
相关文章
|
12天前
|
Go
go语言map、实现set
go语言map、实现set
14 0
|
12天前
|
Go
go语言数组与切片
go语言数组与切片
17 0
|
5天前
|
存储 Go API
一个go语言编码的例子
【7月更文挑战第2天】本文介绍Go语言使用Unicode字符集和UTF-8编码。Go中,`unicode/utf8`包处理编码转换,如`EncodeRune`和`DecodeRune`。`golang.org/x/text`库支持更多编码转换,如GBK到UTF-8。编码规则覆盖7位至21位的不同长度码点。
70 1
一个go语言编码的例子
|
8天前
|
JSON 算法 测试技术
在go语言中调试程序
【6月更文挑战第29天】Go语言内置`testing`包支持单元测试、基准测试和模糊测试。`go test`命令可执行测试,如`-run`选择特定测试,`-bench`运行基准测试,`-fuzz`进行模糊测试。
17 2
在go语言中调试程序
|
6天前
|
安全 Go
Go语言的iota关键字有什么用途?
**Go语言中的`iota`是常量生成器,用于在`const`声明中创建递增的常量。`iota`在每个新的`const`块重置为0,然后逐行递增,简化了枚举类型或常量序列的定义。例如,定义星期枚举:** ```markdown ```go type Weekday int const ( Sunday Weekday = iota // 0 Monday // 1 Tuesday // 2 ... ) ``` 同样,`iota`可用于定义不同组的常量,如状态码和标志位,保持各自组内的递增,提高代码可读性。
|
11天前
|
Devops Go 云计算
Go语言发展现状:历史、应用、优势与挑战
Go语言发展现状:历史、应用、优势与挑战
|
2天前
|
监控 搜索推荐 Go
万字详解!在 Go 语言中操作 ElasticSearch
本文档通过示例代码详细介绍了如何在Go应用中使用`olivere/elastic`库,涵盖了从连接到Elasticsearch、管理索引到执行复杂查询的整个流程。
8 0
|
6天前
|
IDE Linux Go
记录一个go语言与IDE之间的问题
【7月更文挑战第1天】本文介绍在IDE中调试Go应用可能遇到的问题。当问题与IDE的自动完成有关,可以试着使用其他编辑器如Linux的vim是否无此问题。这可以验证表明IDE可能不完全兼容最新语言版本,建议使用无自动检测工具临时解决。
22 0
|
10天前
|
编译器 Go C++
必知的技术知识:go语言快速入门教程
必知的技术知识:go语言快速入门教程
|
11天前
|
编译器 Go 开发者