1. gRPC C C++开发环境搭建
特别需要强调,grpc需要6.3以上的gcc/g++版本
cmake、gcc版本 ubuntu16.04默认不支持
1 安装必要依赖工具
sudo apt-get install autoconf automake libtool
如果cmake低于3.15 ,gcc/g++低于7.0, 请按照文档安装
#检查cmake版本
cmake --version
#检查gcc/g++版本
gcc -v
g++ -v
# 安装cmake
#1 卸载已经安装的旧版CMake
sudo apt-get autoremove cmake
#2 文件下载解压
wget https://cmake.org/files/v3.23/cmake-3.23.0-linux-x86_64.tar.gz
#3 解压
tar -zxf cmake-3.23.0-linux-x86_64.tar.gz
#4 查看解压后目录
tree -L 2 cmake-3.23.0-linux-x86_64
cmake-3.23.0-linux-x86_64
├── bin
│ ├── ccmake
│ ├── cmake
│ ├── cmake-gui
│ ├── cpack
│ └── ctest
├── doc
│ └── cmake
├── man
│ ├── man1
│ └── man7
└── share
├── aclocal
├── applications
├── bash-completion
├── cmake-3.23
├── emacs
├── icons
├── mime
└── vim
15 directories, 5 files
#5 创建软连接
注: 文件路径是可以指定的,一般选择在/opt 或 /usr路径下,这里选择/opt
sudo mv cmake-3.23.0-linux-x86_64 /opt/cmake-3.23.0
sudo ln -sf /opt/cmake-3.23.0/bin/* /usr/bin/
#6 测试版本
cmake --version
#安装gcc g++ 7
sudo apt-get install -y software-properties-common
sudo add-apt-repository ppa:ubuntu-toolchain-r/test
sudo apt update
sudo apt install g++-7 -y
#建立软连接并检查
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7
sudo update-alternatives --config gcc
gcc -v
g++ -v
#查看版本
2 编译grpc
#1 下载源码
git clone https://github.com/grpc/grpc
#2 查看版本并选择合适的版本,这里选择v1.45.2相对较新的版本
git tag
git checkout v1.45.2
#3 查看此时grpc目录内容的大小 du -h --max-depth=1,可以看到427M左右
king@ubuntu:~/grpc$ du -h --max-depth=1
268K ./etc
284K ./bazel
3.0M ./third_party
1.9M ./include
34M ./src
1.5M ./templates
1.5M ./doc
455M ./.git
64K ./.github
32K ./summerofcode
4.0K ./spm-core-include
4.0K ./spm-cpp-include
8.0K ./.bazelci
5.0M ./examples
24M ./test
80K ./cmake
6.5M ./tools
533M .
#4 编译与安装
mkdir -p cmake/build
cd cmake/build
cmake ../..
make
sudo make install
3 protobuf安装
不用手动安装protobuf,不然版本可能和grpc不匹配,必须在grpc执行git submodule update --init 命令之后
生成的third_party/protobuf 里面编译安装对应的protobuf。
cd third_party/protobuf/
./autogen.sh
./configure --prefix=/usr/local
make
sudo make install
sudo ldconfig #使得新安装的动态库能被加载
protoc --version
显示3.19.4
4 测试环境
编译helloworld
cd grpc/examples/cpp/helloworld/
mkdir build
cd build/
cmake ..
make登陆后复制
#启动服务和客户端,监听在50051端口
./greeter_server
server listening on 0.0.0.0:50051
#启动客户端,服务端返回Hello world
./greeter_client
Greeter recevived:Hello world
如何测试 grpc中的route_guide?
编译好以后如果直接执行会崩溃
要去上级目录导入json(作为参数)
grpc源码
2. gRPC原理
2.1 什么是RPC
RPC即远程过程调用协议(Remote Procedure Call Protocol),可以让我们像调用本地对象一样发起远程调用。RPC凭借其强大的治理功能,成为解决分布式系统通信问题的一大利器。
gRPC是一个现代的,高性能,开源的和语言无关的通用RPC框架,基于HTTP2协议设计,序列化使用PB(Protocol Buffer),PB时一种语言无关的高性能序列化框架,基于HTTP2 + PB保证了高性能。
向服务端请求, 就像调用本地函数一样
2.2gRPC的一些特性
1.gRPC基于服务的思想:定义一个服务,描述这个服务的方法以及入参出参,服务器端有这个服务的具体实 现,客户端保有一个存根,提供与服务端相同的服务
2.&RPC默认采用protocol buffer作为IDL(interface Description Lanage)接口描述语言,服务之间通信的数据序 列化和反序列化也是基于protocol buffer的,因为protocol buffer的特殊性,所以gRPC框架是跨语言的通信 框架(与编程语言无关性),也就是说用lava开发的基于gRPC的服务,可以用GoLang编程语言调用
3.gRPC同时支持同步调用和异步调用,同步RPC调用时会一直阻塞直到服务端处理完成返回结果,异步RPC是 客户端调用服务端时不等待服务段处理完成返回,而是服务端外理完成后主动回调客户端告诉客户端外理完成 4.gRPC是基于htto2协议实现的,http2协议提供了很多新的特性,并且在性能上也比htp1提搞了许多,所以 gRPC的性能是非常好的
5.gRPC并没有直接实现负载均衡和服务发现的功能,但是已经提供了自己的设计思路。已经为命名解析和负载 均衡提供了接口
6.基于http2协议的特性:gRPC允许定义如下四类服务方法 1.一元RPC:客户端发送一次请求,等待服务端响应结构,会话结束,就像一次普通的函数调用这样简单 2.服务端流式RPC:客户端发起一起请求,服务端会返回一个流,客户端会从流中读取一系列消息,直到没 有结果为止 3.客户端流式RPC:客户端提供一个数据流并写入消息发给服务端,一旦客户端发送完毕,就等待服务器读 取这些消息并返回应答 4.双向流式RPC:客户端和服务端都一个数据流,都可以通过各自的流进行读写数据,这两个流是相互独立 的,客户端和服务端都可以按其希望的任意顺序独写
2.3 gRPC支持的编程语言
C++,java(包括对Android的支持),Objective-C(对于i0S),Python,Ruby,Go,C#,Node.js都在GA中,并 遵循语义版本控制。我
2.4 gRPC的使用场景
低延迟,高度可扩展的分布式系统 开发与云服务器通信的客户端 设计一个准确,高效,且与语言无关的新协议时 分层设计,以实现扩展,例如。身份验证,负载平衡,日志记录和监控等
2.5 谁在使用gRPC
谷歌长期以来一直在gRPC中使用很多基础技术和概念。目前正在谷歌的几个云产品和谷歌面向外部的API中使用。 Square,Netflix,CoreOs,Docker,CockroachDB,Cisco,Juniper Networks以及许多其他组织和个人也在使 用它。
2.6 gRPC设计之初的动机和原则
1.自由,开放:让所有人,所有平台都能使用,其实就是开源,跨平台,跨语言 2.协议可插拔:不同的服务可能需要使用不同的消息通信涉型和编码机制,例如JSONXML和Thirt.所以协 议应允许司插瑞报型,经咨前费均演,服各发通:日店,控部支持可插进机制 阻塞和非阻塞:支持客户端机服务器交换的消息字列的是一和同步处理,这对于在某些平台上广展和处您至关 重要 4、职消和超时:一次RPC):作可能是持久并且易贵的,应该允许客户端设里取消RPC通信村对这次通信加上一个 超时时阵 ;肘组:心明刘开服务器通过在继续处理清求的同时拍后新求8市下北职省 6.流处理:存储系统依靠流和流控制采表达大型数据织,其地服务,如语首到飞本或股票行情,应秘于流来表示 与时间相关的消息序列 7流担围;计管能力和网络客量在它中院和所务县之间通意县不平省的。而控制许更好的授冲区管理 (座过 度活跌印对算能办电网导(姆在亡 8.元数据交换、认定象而和寻统州跨领械行盛依盐一一等于哪各产明接口的数据产挣、依赖于他们将这些传性 演进到服务 谋雏常 9.标准化状态码:变中独通常以有面的下电四I心NP围用限回山量理。应门本秋营召名称空司,以使这些错湿外 理决策更加清晰。如果通要更丰票的方式物应A月,则可以使用元数东协不研名资设出 10_互通性:报口协识(Wire Protocol)须遵循普通互联网基础框架
3.数据封装和数据传输问题
3.1 网络传输中的内容封装数据体积问题
早期的RPCISON的方式, 自前的RPC基本上都采用类似Protobuf的二进制序列化方式: 其差别在于:json的设计是给人看的,protobuf则是利于机器:
JSON 优点:在body中用ISON对内容进行编码,极易跨语言,不需要约定特定的复杂编码格式和Stub文件。在版本兼容 性上非常友好,扩展也很容易。 缺点:JSON难以表达复杂的参数类型,如结构体等;数据冗余和低压缩率使得传输性能差。 (微服务之间的服务器调用,一般采用二进制的序列化方式,比
Protobuf gRPC对此的解决方案是丢弃json、xml这种传统策略,使用Protocol Buffer,是Go0gle开发的一种跨语言、跨平 台、可扩展的用于序列化数据协议。
// 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 t
repeated int64 ids =l;//repeated 长示数维
// 可嵌套对象
Vaue info =2:
map<int,Value>values=3;//可输出map映射
message value {
bool is man = l;
int age = 2;
以上是一个使用样例,包含方法定义、入参、出参。可以看出有几个明确的特点:
有明确的类型,支持的类型有多种
每个field会有名字
每个field有一个数字标号,一般按顺序排列(下文编解码会用到这个点)
能表达数组、map映射等类型
通过嵌套message可以表达复杂的对象
方法、参数的定义落到一个.proto文件中,以来双方需要同时持有这个文件,并依此进行编解码
protobuf作为一个以跨语言为目标的序列化方案,protobuf能做到多种语言以同一份proto文件作为约定,不用A 语言写一份,B语言写一份,各个依赖的服务将proto文件原样拷贝一份即可。 但.proto文件并不是代码,不能执行,要想直接跨语言是不行的,必须得有对应语言的中间代码才行,中间代码要 有以下能力: 。将message转成对象,例如C++里是class,golang里是struct,需要各自表达后,才能被理解 。需要有进行编解码的代码,能解码内容为自己语言的对象、能将对象编码为对应的数据 更多protobuf的讲解请参考 课程《应用层协议设计protobuf》
3.2 网络传输效率问题
grpc采用HTTP2.0,相对于HTTP1.0在灭快的传输和更低的成本两个目标上做了改进。有以下几个基本点:
HTTP2 未改变HTTP的语义(如GET/POST等),只是在传输上做了优化
引入帧、流的概念,在TCP连接中,可以区分出多个requesUresponse
一个域名只会有一个TCP连接,借助帧、流可以实现多路复用,降低资源消耗
引入二进制编码,降低header带来的空间占用
HTTP1.1核心问题在于:在同一个TCP连接中,没办法区分response是属于哪个请求,一旦多个请求返回的文本内容混在一起,则没法区分数据归属于哪个请求,所以请求只能一个个串行排队发送。这直接导致了TCP资源的闲置。HTTP2为了解决这个问题,提出了流的概念,每一次请求对应一个流,有一个唯一ID,用来区分不同的请求。基于流的概念,进一步提出了帧,一个请求的数据会被分成多个帧,方便进行数据分割传输,每个帧都唯一属于某-个流ID,将帧按照流ID进行分组,即可分离出不同的请求。这样同一个TCP连接中就可以同时并发多个请求,不同清求的帧数据可穿插在一起,根据流ID分组即可。HTTP2.0基于这种二进制协议的乱序模式(Duplexing),直接解决了HTTP1.1的核心痛点,通过这种复用TCP连接的方式,不用再同时建多个连接,提升了TCP的利用效率。
3.3 RPC的4种模式
1.一元RPC模式
2.服务器端流RPC模式
3.客户端流RPC模式
4.双端流RPC模式
4.gRPC异步同步
4 GRPC异步同步 具体参考:examples/cpp/helloworld
Quick start _c++gRPC https://grpc.io/docs/language/cpp/guickstart/
编译 .proto文件
protoc --proto_path =() --cpp_out=() xxx.proto
4.3 流相关概念
可以按照 Client 和 Server 一次发送/返回的是单个消息还是多个消息,将 gRPC 分为:
Unary RPC(一元)
Server streaming RPC
Client streaming RPC
Bidirectional streaming RPC
对应的proto文件见:grpc/examples/cpp/route_guide范例对应的proto文件
syntax ="proto3";
option java_multiple_files = true;
option java_package ="io.grpc.examples .routeguide";
option java_outer_classname ="RouteGuideProto";
option objc_class_prefix ="RTG"
package routeguide;
具体如何实现流模式分析见源码
rpc GetFeature(Point)returns(Feature){}
//A server-to-client streaming RPc,带存 stream 的RPC服务,返回是stream
11
// obtains the Features available within the given Rectangle. Resuits 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.
// Rectangle
rpc ListFeatures(Rectangle)returns(stream Feature){}
//A client-to-server streaming RPc.带有 stream 的RPC服务,客户端たstream
1
// Accepts a stream of Points on a route being traversed, returning a
//Routesummary when traversal is completed.
//多个坐标stream Point,
Routesummary: 有多少个著名景点返回
rpc RecordRoute(stream Point)returns(Routesummary){}
//A Bidirectional streaming RPc, 2瑞都是stream的RPC徽务
11
// Accepts a stream of RouteNotes sent while a route is being traversed.
// while receiving other RouteNotes (e.g. from other users).
// 路线注释
rpc Routechat(stream RouteNote)returns(stream RouteNote){}
// Points are represented as latitude-longitude pairs in the e7 representation
//(degrees multiplied by 10**7 and rounded to the nearest integer).
//Latitudes should be in the range +/- 90 degrees and //longitude should be in
// the range +/- 180 degrees(inclusive).
//经纬度坐标
同步方式
helloworld案例的设计
proto文件和cmake文件内容如下
server端:
多个epoll监听请求,同步响应,类似reactor模型
ps:一个server可以有多个services,只要注册即可
如何进入debug模式?
在cmake文件中加入
异步方式
server端:
异步跑多线程
每个线程拿到了就执行,i标识执行的是哪个线程