gRPC原理详解

Posted Golang JAVA进阶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gRPC原理详解相关的知识,希望对你有一定的参考价值。

概述

RPC 只是一种概念、一种设计,就是为了解决 不同服务之间的调用问题, 它一般会包含有 传输协议 和 序列化协议 这两个。
gRPC是一个高性能、开源通用的RPC框架,它的核心就是基于“HTTP/2+Protocol buffer”,保障了RPC调用的高性能。带来诸如双向流、流控、头部压缩、单TCP连接上的多路复用机制。


为什么使用 gRPC?

有了 gRPC, 我们可以一次性的在一个 .proto 文件中定义服务并使用任何支持它的语言去实现客户端和服务器,反过来,它们可以在各种环境中,从Google的服务器到你自己的平板电脑—— gRPC 帮你解决了不同语言及环境间通信的复杂性。使用 protocol buffers 还能获得其他好处,包括高效的序列号,简单的 IDL 以及容易进行接口更新。


相比其他开源的RPC框架,gRPC有如下几个特点

(1)支持多种语言。

(2)基于 IDL 文件定义服务,通过 proto3 工具生成指定语言的数据结构、服务端接口及客户端Stub。

(3)通信协议基于标准的HTTP/2设计,支持双向流、消息头压缩、单TCP的多路复用、服务端推送等特性。

(4)序列化支持Protocol buffer和JSON,Protocol buffer是一种语言无关的高性能序列化框架,基于“HTTP/2+Protocol buffer”,保障了RPC调用的高性能。



调用模型

1、客户端(gRPC Stub)调用 A 方法,发起 RPC 调用。

2、对请求信息使用 Protobuf 进行对象序列化压缩(IDL)。

3、服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。

4、对响应结果使用 Protobuf 进行对象序列化压缩(IDL)。

5、客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。


调用方式

gRPC 允许你定义四类服务方法:

  • 单项 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。

    rpc SayHello(HelloRequestreturns (HelloResponse){
  • 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){}


  • 客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。

    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {}


  • 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。

    rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){}



一、Unary RPC:一元 RPC

Chat.proto

syntax = "proto3";
// The Go package name is "timestamppb".option go_package = "google.golang.org/protobuf/types/known/timestamppb";
package chat;

message Message { string name = 1; int32 age = 2;}
message ResponseMessage { int32 status = 1; string body = 2;}

service ChatService { rpc SayHello (Message) returns (Message){};}

//通过命令行生成对应的代码protoc --go_out=plugins=grpc:chat chat.proto


Chat.go


type Server struct { Body string}
func (s *Server) SayHello(ctx context.Context, message *Message) (*Message, error) { log.Printf("Received message form client: %s", message.Name)
  //return &Message{Body: "Hello From The Server!"}, nil return &Message{Name: "Wendy", Age: 30}, nil}


Server.go

func main() { fmt.Println("Go gRPC Beginners Tutorial!")
lis, err := net.Listen("tcp", ":9000") if err != nil { log.Fatalf("Failed to listen on port 9090: %v", err) }
s := chat.Server{}
grpcServer := grpc.NewServer()
chat.RegisterChatServiceServer(grpcServer, &s)
if err := grpcServer.Serve(lis); err != nil { log.Fatalf("Failed to serve GRPC: %v", err) }}


  • 创建 gRPC Server 对象,你可以理解为它是 Server 端的抽象对象。

  • 将 ChatService(其包含需要被调用的服务端接口)注册到 gRPC Server。的内部注册中心。这样可以在接受到请求时,通过内部的 “服务发现”,发现该服务端接口并转接进行逻辑处理。

  • 创建 Listen,监听 TCP 端口。

  • gRPC Server 开始 lis.Accept,直到 Stop 或 GracefulStop。


Client.go


func main() {
var conn *grpc.ClientConn
conn, err := grpc.Dial(":9000", grpc.WithInsecure())
if err != nil { log.Fatalf("did not connect: %s", err) } defer conn.Close()
c := chat.NewChatServiceClient(conn)
response, err := c.SayHello(context.Background(), &chat.Message{Name: "Hello From Client!", Age: 20})
if err != nil { log.Fatalf("Error when calling SayHello: %s", err) } log.Printf("Response from server: %+v", response)
}




总结



预留


预留






以上是关于gRPC原理详解的主要内容,如果未能解决你的问题,请参考以下文章

gRPC 实现原理

grpc原理

小米gRPC 系列——grpc 超时传递原理

gRPC:客户端创建和调用原理

RPC基础系列3gRPC简单示例

gRPC 本地服务搭建