博客园  :: 首页  :: 新随笔  :: 管理

3.3.5 gRPC远程调用

Posted on 2023-04-26 20:13  wsg_blog  阅读(93)  评论(0)    收藏  举报

Linux c/c++高性能

gRPC远程调用

RPC 即远程过程调用协议(Remote Procedure Call Protocol),可以让我们像调用本地对象一样发起远程函数调用,RPC凭借其强大的治理功能,成为解决分布式系统通信问题的一大利器。
gRPC可以实现跨语言的高效远程数据调用,基于 HTTP2.0 协议设计,序列化使用PB(Protocol Buffer),PB 是一种语言无关的高性能序列化框架,基于 HTTP2+PB 保证了的高性能。
bRCP主要用于c++,它的效率更高,如果你的系统只有使用c++那brpc更合适。

image

数据封装和数据传输问题

早期的RPCJSON的方式,目前的RPC基本上都采用类似Protobuf的二进制序列化方式;其差别在于:json的设计是给人看的,protobuf则是利于机器。

网络传输中的内容封装数据体积问题

微服务之间的服务器调用,一般采用二进制的序列化方式,比如protobuf;
protobuf作为一个以跨语言为目标的序列化方案,protobuf能做到多种语言以同一份proto文件作为约定,不用A语言写一份,B语言写一份,各个依赖的服务将proto文件原样拷贝一份即可。

.proto文件并不是代码,不能执行,要想直接跨语言是不行的,必须得有对应语言的中间代码才行,中间代码要有以下能力:

  • 将message转成对象,例如C++里是class,golang里是struct,需要各自表达后,才能被理解
  • 需要有进行编解码的代码,能解码内容为自己语言的对象、能将对象编码为对应的数据
// XXXX.proto
// rpc服务的类 service关键字, Test服务类名
service Test {
  // rpc 关键字,rpc的接口
  rpc HowRpcDefine (Request) returns (Response) ; // 定义一个RPC方法
}
// message 类,c++ class
message Request {
  //类型 | 字段名字| 标号
  int64 user_id = 1;
  string name = 2;
}
message Response {
  repeated int64 ids = 1; // repeated 表示数组
  Value info = 2; // 可嵌套对象
  map<int, Value> values = 3; // 可输出map映射
}
message Value {
  bool is_man = 1;
  int age = 2;
}

以上是一个使用样例,包含方法定义、入参、出参。可以看出有几个明确的特点:

  • 有明确的类型,支持的类型有多种
  • 每个field会有名字
  • 每个field有一个数字标号,一般按顺序排列(下文编解码会用到这个点)
  • 能表达数组、map映射等类型
  • 通过嵌套message可以表达复杂的对象
  • 方法、参数的定义落到一个.proto 文件中,依赖双方需要同时持有这个文件,并依此进行编解码

网络传输效率问题

grpc采用HTTP2.0,相对于HTTP1.0在更快的传输和更低的成本两个目标上做了改进

  • HTTP2 未改变HTTP的语义(如GET/POST等),只是在传输上做了优化
  • 引入帧、流的概念,在TCP连接中,可以区分出多个request/response
  • 一个域名只会有一个TCP连接,借助帧、流可以实现多路复用,降低资源消耗
  • 引入二进制编码,降低header带来的空间占用

HTTP1.1核心问题在于:在同一个TCP连接中,没办法区分response是属于哪个请求,一旦多个请求返回的文本内容混在一起,则没法区分数据归属于哪个请求,所以请求只能一个个串行排队发送。这直接导致了TCP资源的闲置。
HTTP2.0为了解决这个问题,提出了 的概念,每一次请求对应一个流,有一个唯一ID,用来区分不同的请求。基于流的概念,进一步提出了 ,一个请求的数据会被分成多个帧,方便进行数据分割传输,每个帧都唯一属于某一个流ID,将帧按照流ID进行分组,即可分离出不同的请求。这样同一个TCP连接中就可以同时并发多个请求,不同请求的帧数据可穿插在一起,根据流ID分组即可。HTTP2.0基于这种二进制协议的乱序模式 (Duplexing),直接解决了HTTP1.1的核心痛点,通过这种复用TCP连接的方式,不用再同时建多个连接,提升了TCP的利用效率。
image

gRPC 4种模式

一元RPC模式
服务器端流RPC模式
客户端流RPC模式
双向流RPC模式


应用实例

gRPC使用其实不是很复杂,基于proto生成的框架是不用修改的,需要我们添加的只是rpc函数中的业务逻辑,需要找到对应的接口它然后加上相关的业务就ok啦

helloworld实例

helloworld.proto

syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

protoc --proto_path=. --cpp_out=. helloworld.proto
生成helloworld.proto文件对应的helloworld.pb.h,helloworld.pb.cc文件,为序列化和参数相关代码
protoc --proto_path=. --grpc_out=. --plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin" helloworld.proto
生成helloworld.proto文件对应的helloworld.grpc.pb.h,helloworld.grpc.pb.cc文件,为rpc相关的接口文件
image

四种流模式实例

一元RPC模式
服务器端流RPC模式
客户端流RPC模式
双向流RPC模式
route_guide.proto,注意关键字stream在函数参数中的位置

// Interface exported by the server.
service RouteGuide {
  // A simple RPC.
  //
  // Obtains the feature at a given position.
  //
  // A feature with an empty name is returned if there's no feature at the given
  // position.
  一元RPC模式:定义最简单的RPC服务;提供经纬度坐标Point,返回这个位置特征名Feature
  rpc GetFeature(Point) returns (Feature) {}

  // A server-to-client streaming RPC.
  //
  // Obtains the Features available within the given Rectangle.  Results are
  // streamed rather than returned at once (e.g. in a response message with a
  // repeated field), as the rectangle may cover a large area and contain a
  // huge number of features.
  服务器端流RPC模式:请求是单个参数,响应带有stream流参数;请求一个经纬坐标区域框返回在这个区域中一系列景点的名称
  rpc ListFeatures(Rectangle) returns (stream Feature) {}

  // A client-to-server streaming RPC.
  //
  // Accepts a stream of Points on a route being traversed, returning a
  // RouteSummary when traversal is completed.
  客户器端流RPC模式:请求带有stream流参数,响应是单个参数;请求多次坐标,返回单次经典的个数统计
  rpc RecordRoute(stream Point) returns (RouteSummary) {}

  // A Bidirectional streaming RPC.
  //
  // Accepts a stream of RouteNotes sent while a route is being traversed,
  // while receiving other RouteNotes (e.g. from other users).
  双向流RPC模式:请求和响应都为stream流模式
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

image

上边两个实例都是grpc给出的example代码,业务代码并不不复杂(复杂的是框架的熟悉程度,发送和接收怎么处理):定义proto文件后,分别在client和server实现相关的接口业务逻辑即可,上边的调用都是采用同步的方式,具体使用哪个模式要看具体的业务需求(通常来说会使用一对一的一元RPC模式),一般都是在实例的基础上修改,如果对grpc足够熟悉在进行相应的优化

gRPC框架代码结构及同步与异步概念

以最简单的helloworld代码为例,能更清晰的看到gRPC框架的结构

image
上图列出了gRPC基础概念及其关系图。其中包括:Service(定义)、RPC、API、Client、Stub、Channel、Server、Service(实现)、ServiceBuilder等

gRPC异步与同步原理及多函数多类调用

image
grpc会启动多个线程的epoll来处理描述符,不管异步还是同步,每个epoll都对应一个线程。

grpc同步模型原理

image

  • 得益于SO_REUSEPORT参数,同一个listenfd可以被放到多个epoll中进行监听
  • 当一个链接成功建立后会生成acceptfd

grpc异步模型原理

异步处理的epoll方式和同步时类似的,但对于rpc函数的响应提供了更灵活的处理机制,可以将一些耗时的处理逻辑放到外部的线程池进行处理。

image

同步多函数多类调用

异步多函数多类调用