一、gRPC介绍
1. gRPC框架
gRPC 是由 google 开发,高性能、开源、支持多语言的 RPC 框架。
在gRPC框架中,运行在不同机器上的客户端应用可以直接调用服务器端上「提供的方法」,使得我们可以更容易的创建一个分布式系统。
gRPC基于定义服务(Service)的思想,指定可以使用其参数和返回类型远程调用的方法;在服务器端,服务器实现这个接口并运行一个gRPC服务器来处理客户端调用;在客户端,客户端有一个存根(stub),它提供与服务器相同的方法。
cpp版的文档地址为:https://grpc.io/docs/languages/cpp/quickstart/。
2. protocol buffers
gRPC默认使用protocol buffers,是Google开源的一种简洁、高效的结构化数据存储格式。
使用protocol的第一步是在proto文件(.proto)中定义需要序列化的数据结构,:
代码语言:javascript复制message Person {
string name = 1;
int32 id = 2;
bool has_ponycopter = 3;
}
然后,我们可以使用protoc编译器来生成指定语言的数据结构(比如cpp中上述定于会生成Person类),protocol将会对每个成员提供简单的访问方法,比如 name()
和 set_name()
,这些方法将会完成原始数据和序列化数据的转换。
二、安装gRPC和Protocol buffers
设置
代码语言:javascript复制export MY_INSTALL_DIR=$HOME/develop/gRPC
mkdir -p $MY_INSTALL_DIR
添加到环境变量:
代码语言:javascript复制export PATH="$MY_INSTALL_DIR/bin:$PATH"
安装cmake(> 3.13)
代码语言:javascript复制wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.19.6/cmake-3.19.6-Linux-x86_64.sh
sh cmake-linux.sh -- --skip-license --prefix=$MY_INSTALL_DIR
rm cmake-linux.sh
安装其它工具
代码语言:javascript复制sudo apt-get install -y build-essential autoconf libtool pkg-config
拉取gRPC源码
代码语言:javascript复制git clone --recurse-submodules -b v1.41.0 https://github.com/grpc/grpc
编译并安装protocol buffers
代码语言:javascript复制cd grpc
mkdir -p cmake/build
pushd cmake/build
cmake -DgRPC_INSTALL=ON
-DgRPC_BUILD_TESTS=OFF
-DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR
../..
make -j8
make install
popd
三、helloworld
1. 编译
进入示例文件夹:
代码语言:javascript复制cd examples/cpp/helloworld
编译示例文件:
代码语言:javascript复制$ mkdir -p cmake/build
$ pushd cmake/build
$ cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../..
$ make -j
2. 运行
进入目录examples/cpp/helloworld/cmake/build
,可以看到编译出的可执行文件:
运行服务器:
代码语言:javascript复制./greeter_server
运行客户端:
代码语言:javascript复制./greeter_client
四、自定义gRPC服务
gRPC服务使用 protocol buffer 来定义,在.proto
文件中,接下来的示例中,服务端和客户端都有 SayHello() 这个方法,该方法传入一个 HelloRequest 参数,返回一个 HelloResponse 参数。
1. 在proto文件中定义方法
打开examples/protos/helloworld.proto
文件,内容如下:
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;
}
添加一个RPC方法:
代码语言:javascript复制package helloworld;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (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;
}
保存退出。
2. 重新生成gRPC代码
进入helloworld编译文件夹,编译:
代码语言:javascript复制cd examples/cpp/helloworld/cmake/build
make -j8
编译会重新生成 helloworld.pb.{h,cc} 和 helloworld.grpc.pb.{h,cc} 文件,包含客户端类和服务端类:
3. 更新服务端
编辑文件greeter_server.cc
,实现刚刚在proto中定义的新方法SayHelloAgain():
// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service {
Status SayHello(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello ");
reply->set_message(prefix request->name());
return Status::OK;
}
Status SayHelloAgain(ServerContext* context, const HelloRequest* request,
HelloReply* reply) override {
std::string prefix("Hello Again ");
reply->set_message(prefix request->name());
return Status::OK;
}
};
4. 更新客户端
编辑文件greeter_client.cc
,在 GreeterClient 类中添加新定义的方法SayHelloAgain
:
std::string SayHelloAgain(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The actual RPC.
Status status = stub_->SayHelloAgain(&context, request, &reply);
// Act upon its status.
if (status.ok()) {
return reply.message();
} else {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
最后,在main函数中调用该函数:
代码语言:javascript复制reply = greeter.SayHelloAgain(user);
std::cout << "Greeter received: " << reply << std::endl;
5. 运行
进入helloworld示例编译目录:
代码语言:javascript复制make -j8
运行服务端:
运行客户端: