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 会越来越多地用于云原生基础设施的打造。

目录
相关文章
|
8天前
|
Go
Go 语言循环语句
在不少实际问题中有许多具有规律性的重复操作,因此在程序中就需要重复执行某些语句。
17 1
|
7天前
|
Go 开发者
探索Go语言的并发之美
在Go语言的世界里,"并发"不仅仅是一个特性,它是一种哲学。本文将带你领略Go语言中goroutine和channel的魔力,揭示如何通过Go的并发机制来构建高效、可靠的系统。我们将通过一个简单的示例,展示如何利用Go的并发特性来解决实际问题,让你的程序像Go一样,轻盈而强大。
|
8天前
|
JSON Go API
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
使用Go语言和Gin框架构建RESTful API:GET与POST请求示例
|
8天前
|
Go
go语言创建字典
go语言创建字典
|
8天前
|
NoSQL Go API
go语言操作Redis
go语言操作Redis
|
8天前
|
Unix Go
go语言获取当前时间戳
go语言获取当前时间戳
|
8天前
|
Go
go语言李mapstructure啥意思
go语言李mapstructure啥意思
|
8天前
|
Go
Go 语言接口
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。 接口可以让我们将不同的类型绑定到一组公共的方法上,从而实现多态和灵活的设计。
|
8天前
|
存储 Go
go语言字符串变小写
go语言字符串变小写
|
8天前
|
Go
8-12|go语言之输入
8-12|go语言之输入
下一篇
无影云桌面