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

数据封装和数据传输问题
早期的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的利用效率。

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相关的接口文件

四种流模式实例
一元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) {}
}

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

上图列出了gRPC基础概念及其关系图。其中包括:Service(定义)、RPC、API、Client、Stub、Channel、Server、Service(实现)、ServiceBuilder等
gRPC异步与同步原理及多函数多类调用

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

- 得益于SO_REUSEPORT参数,同一个listenfd可以被放到多个epoll中进行监听
- 当一个链接成功建立后会生成acceptfd
grpc异步模型原理
异步处理的epoll方式和同步时类似的,但对于rpc函数的响应提供了更灵活的处理机制,可以将一些耗时的处理逻辑放到外部的线程池进行处理。

浙公网安备 33010602011771号