前言
本文根据 grpc.io/docs/langua… 进行 C++ grpc RouteGuide示例学习运行,主要是四种RPC类型的简单使用示例。更多的是学习记录,水平不高,能力有限,错漏之处,还请见谅。欢迎友好讨论。
环境信息
- 操作系统版本:CentOS 7.6
- CMake版本:4.2.0
- Git版本:2.25.0
- GCC版本:gcc 11.2
- GLIBCXX 版本:libstdc++.so.6.0.29(伴随gcc11.2安装)
- as & ld版本: 2.45
相关安装部署教程可以参考mp.weixin.qq.com/s/50Tep3mq7…
grpc官方仓库运行流程
编译
参考 mp.weixin.qq.com/s/50Tep3mq7… 进行编译grpc之后,执行如下步骤:
-
在grpc源码文件夹下执行
-
cd examples/cpp/route_guide
-
-
运行Cmake指令
-
export MY_INSTALL_DIR=xxxxxxxx # grpc本地安装路径 mkdir -p cmake/build cd cmake/build cmake -DCMAKE_C_COMPILER=/usr/local/gcc-11.2.0/bin/gcc \ # 高版本gcc位置, 如果默认版本满足需求, 则无需指定 -DCMAKE_CXX_COMPILER=/usr/local/gcc-11.2.0/bin/g++ \ # 高版本g++位置, 如果默认版本满足需求, 则无需指定 -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../.. # 执行编译 make
-
-
编译完成
运行
-
在examples/cpp/route_guide/cmake/build文件下运行服务器
-
./route_guide_server --db_path=../../route_guide_db.json
-
-
重新开一个终端,在examples/cpp/route_guide/cmake/build文件夹下运行客户端
-
./route_guide_client --db_path=../../route_guide_db.json
-
个人仓库代码运行流程
编译
参考 mp.weixin.qq.com/s/50Tep3mq7… 克隆仓库 github.com/EarthlyImmo… 并配置grpc依赖,然后执行如下操作:
-
修改 blog_code/route_guide/start_build.sh 中的gcc和g++路径
-
在blog_code/route_guide目录下执行start_build.sh
-
./start_build.sh
-
运行
-
blog_code/route_guide/build/server 文件夹下运行服务器
-
./server --db_path=../../route_guide_db.json
-
-
另起一个终端,在blog_code/route_guide/build/client文件夹下运行客户端
-
./client --db_path=../../route_guide_db.json
-
这部分代码完全是copy的grpc官方的示例,只对cmake文件和目录结构做了调整。个人仓库的好处是可以省去编译grpc的时间,直接使用我编译好的库。
下面我将对 route_guide 示例代码进行简单分析。以下分析均使用个人仓库代码测试。
代码简单分析
protobuf定义
文件:route_guide/proto/route_guide.proto
主要是定义了服务和接口名以及要用到的消息结构。
-
服务和接口名:
- 类型:service,在这个示例中如service RouteGuide
- 定义的接口:GetFeature(一元RPC);ListFeatures(服务器流式RPC); RecordRoute(客户端流式rpc); RecordRoute(双向流式 RPC)
- 生成插件:grpc插件,文件在grpc/bin/grpc_cpp_plugin
- 生成的文件:route_guide/proto/route_guide.grpc.pb.h;route_guide/proto/route_guide.grpc.pb.cc
-
消息结构
- 类型:message,如message Point等
- 生成插件:无
- 生成的文件:route_guide/proto/route_guide.pb.h;route_guide/proto/route_guide.pb.cc
暂时不对生成的文件进行分析了,目前对我来说有点复杂,后续再深入学习之后再进行分析。
辅助函数
文件:route_guide/common/helper.h;route_guide/common/helper.cpp
主要是为了从json文件中解析出Feature信息。Feature信息定义如下:
被解析的json文件路径:route_guide/route_guide_db.json。这其实就是一个json形式的Feature信息列表,代表一些城市的E7表示法经纬度信息,可以看message Point的注释。部分数据如下:
客户端和服务器代码简介
这个示例主要是为了展示grpc的四种rpc类型,即一元 RPC(Unary RPC)、服务器流式 RPC(Server streaming RPC)、客户端流式 RPC(Client streaming RPC)、双向流式 RPC(Bidirectional streaming RPC)。因此对代码的分析也分为这四种rpc类型来看。
一元 RPC(Unary RPC)
-
接口名:GetFeature
-
协议定义
-
服务器代码:
-
客户端代码
-
输出信息
-
功能介绍:客户端向服务器请求点的特征信息。客户端传入一个点的信息,获取这个点的特征信息。在客户端测试程序中测试了两个点,从返回信息中可知,这两个点一个存在特征信息(指有名字信息),一个不存在特征信息。
服务器流式 RPC(Server streaming RPC)
-
接口名:ListFeatures
-
协议定义
-
服务器代码
-
客户端代码
-
输出信息
-
功能介绍
-
这个接口是客户端传给服务器一个矩形区域(用两个点表示),然后服务器返回给客户端在这个矩形区域范围内的所有点。与一元 RPC(Unary RPC)不同的是,服务器并不是将所有的点信息在一个消息包内一次性返回的,而是分多个包流式返回。这一点可以通过抓包来发现。抓包流程可以通过 抓包操作流程 一节得到。
- ...
-
客户端流式 RPC(Client streaming RPC)
-
接口名:RecordRoute
-
协议定义
-
服务器代码
-
客户端代码
-
输出信息
-
功能介绍
-
客户端向服务器发送一定数量的点,服务器计算出一共有几个点,这些点里面还有几个特征点(指有名字信息的点位),依次走过这些点的距离是多少,以及依次获取一共花了多少时间。在示例代码中,客户端在所有点中随机出10个点,依次发给服务器,每次发送一个点随机等0.5到1.5s,作为从一个点到另一个点的时间。最后服务器返回经过了10个点,其中4个特征点,总共经过733376米,花费9s。与一元 RPC(Unary RPC)不同的是。客户端并不是将所有的点信息在一个消息包内一次性发送的,而是分多个包流式发送。这一点可以通过抓包来发现。抓包流程可以通过 抓包操作流程 一节得到。
- 在客户端发送第一个点位信息之后,客户端和服务器之间还会有几次交互,然后才发送下一个点位信息。这些交互中有些可能是tcp底层的交互,但是有些应该是带信息的业务包。不过暂时先不深究。
- 上面用到的pb解析工具可以在blog_code/route_guide/python_tool中找到
-
双向流式 RPC(Bidirectional streaming RPC)
-
接口名:RouteChat
-
协议定义
-
服务器代码
-
客户端代码
-
输出信息
-
功能介绍:客户端向服务器发送点位以及点位上的附加信息,服务器首先遍历当前接收过的所有信息,把属于同一个点位的信息返回给客户端,然后把客户端传过来的信息记录下来。在这个示例中,客户端发送信息是额外起一个线程去做的,连续发送了4条信息,其中前三条信息,因为点位各不相同,所以没有信息返回,而第4条信息因为点位和第一条信息相同,所以服务器会返回第一条信息。如果我们在不停止服务器的情况下,连续两次运行客户端,可以得到如下输出。可以发现,因为第一轮运行的时候,已经在点位(0,0) 写入First和Fourth两条信息,所以在第二轮运行再次向点位(0,0) 发送First信息的时候,对应会有两条信息从服务器返回,其他的依次类推。同样我们可以抓包分析这些信息客户端和服务器均为多次流式发送的,这里不再展开。
抓包操作流程
-
安装wireshark
-
sudo yum install -y wireshark wireshark-gnome # 这里连图形界面一起安装, 虽然暂时没有用到
-
-
使用tcpdump抓包
-
sudo tcpdump -i any -s 0 -w grpc_stream.pcap 'port 50051' # -i any: 监听所有网络接口 # -s 0: 设置快照长度(snaplen)为0,确保捕获每个数据包的完整内容,避免截断 # -w grpc_stream.pcap: 将原始数据包保存到文件,这是后续分析的基础 # port 50051: 过滤器,只捕获与该端口相关的流量,大幅减少干扰
-
-
使用tshark分析
-
tshark -r grpc_stream.pcap -V > tmp.txt
-
说明
鉴于时间和水平原因,有些通讯细节并未深究,比如在流式RPC中客户端和服务器之间应该有一些额外的交互。学习是一个逐渐深入的过程,后续会逐步搞清楚。