gRPC C++ 入门教程
之前曾经写过 Python 使用 gRPC 收发消息的教程,可以参考文章 《体验 gRPC 那些事儿》。最近计划在 C++ 项目中使用 gRPC,故写一篇文章来记录一下如何使用 C++ 语言来实现一个简单的 gRPC 服务端和客户端程序。
本教程需要先安装 gRPC,有关 gRPC 的安装教程可以参考文章 《CentOS 7 安装 gRPC》,《体验 gRPC 那些事儿》。
本文涉及的程序包括四部分,分别是客户端源代码 client.cc
,服务端源代码 server.cc
,proto 文件 mathtest.proto
以及 Makefile。
Makefile
Makefile 的作用是编译源代码,Makefile 文件如下:
1 | LDFLAGS = -L/usr/local/lib `pkg-config --libs protobuf grpc++`\ |
执行这个 Makefile 文件,会生成可执行客户端 client
和服务端 server
可执行文件。
客户端和服务端可执行文件都依赖于由 proto 文件生成的源代码,Makefile 在 clean
前面的两个规则的作用即是生成这些代码。
Makefile 文件中出现了一些特殊符号:
- $@:规则的目标名称,例如上面 Makefile 中代表
client
和server
- %:匹配字符,例如上面 Makefile 中匹配
mathtest.proto
- $<:依赖的第一个目标名称,例如上面 Makefile 中的
mathtest.proto
- $^:依赖的所有目标名称,上面 Makefile 中的
server
规则中,即代表mathtest.pb.o mathtest.grpc.pb.o server.o
有关 Makefile 的解析,可以参考阮一峰的 《Make 命令教程》。
Proto
.proto
文件用来定义客户端和服务端的消息格式。
1 | syntax = "proto3"; |
syntax
定义了使用 proto3 的语法(还有 proto2)java_package
用来定义生成 Java 类所在的包package
表示定义消息的包名,对于 C++ 程序,消息类将会被包装在对应的命名空间中,例如,使用mathtest::MathRequest
来引用MathRequest
消息service
用来定义在 RPC 调用中的服务接口,这里我们定义了一个服务接口sendRequest
,它接受一个MathRequest
消息,并返回MathReply
消息message
用来定义消息的字段,例如,MathRequest
消息包含两个整型字段a
和b
,MathReply
包含一个整型字段result
。字段后面的数字并不表示该字段的值,只是proto
程序用来生成相关源代码使用。
有关 Protocol Buffers 的介绍,可以参考官方文档,https://developers.google.com/protocol-buffers/docs/overview。
服务端
服务端代码 server.cc
如下:
1 | // |
在代码开头,我们引入了 grpc 头文件以及 proto 生成的头文件,然后我们声明了在服务端代码中使用的相应命名空间下的类型。
MathServiceImplementation
实现服务接口 sendRequest()
,该接口获取请求消息中的 a
和 b
字段的值,并返回 a * b
的积。MathServiceImplementation
是个实现类,继承于 MathTest::Service
,MathTest::Service
在 mathtest.grpc.pb.h
声明。
Run()
函数指定了服务地址 0.0.0.0:5000
,对 MathServiceImplementation
服务注册后,我们启动了服务端,并接受客户端的请求。
客户端
客户端 client.cc
代码如下:
1 | // |
MathTestClient
类提供向服务端发送请求的接口 sendRequest()
。sendRequest()
接受两个整型参数,然后通过私有成员变量 stub_
向服务端发送请求消息并接收返回结果。
Run()
函数指定服务地址并初始化 MathTestClient
对象 client
,通过对象 client
调用服务接口并获取返回结果。可以看到,在客户端调用服务端的接口时,就像是调用本地接口一样,这也是 RPC 调用的特色。
编译运行
执行命令编译客户端和服务端源代码:
1 | make |
执行服务端程序:
1 | ./server |
可以看到服务端输出:
Server listening on port: 0.0.0.0:5000
执行客户端程序:
1 | ./client |
可以看到客户端输出:
Answer received: 5 * 10 = 50
如果执行编译过程时报错,可以参考以下处理方法。
解决 pkg-config 报错
上述 Makefile 中使用了 pkg-config
命令,如果执行 make
编译代码时报错,例如:
Package protobuf was not found in the pkg-config search path.
Perhaps you should add the directory containing `protobuf.pc’
to the PKG_CONFIG_PATH environment variable
No package ‘protobuf’ found
如报错提示,这是由于 pkg-config 无法搜到 *pc 文件所在路径导致,可以通过在 ~/.bashrc 文件添加环境变量 PKG_CONFIG_PATH
:
1 | PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig |
其中,/usr/local/lib/pkgconfig
为 protobuf.pc
所在路径(可以通过 find
命令搜索到)。
添加 PKG_CONFIG_PATH
环境变量后,执行 source ~/.bashrc
,再执行编译代码,不再报错。
解决 libgrpc++.so 无法找到报错
如果运行时报 libgrpc++.so 库文件无法找到的错误,例如:
./server: error while loading shared libraries: libgrpc++.so.1: cannot open shared object file: No such file or directory
这是由于 gRPC 安装时库文件所在的路径无法被系统找到导致。可以通过增加 ld 搜索路径解决。
在 /etc/ld.so.conf.d/
目录增加 grpc.conf
文件,内容是 libgrpc++.so 文件所在的目录(可以通过 find
命令搜索到)。
1 | /usr/local/lib/ |
然后执行命令 ldconfig
,再运行程序即可。