gRPC配置与用法

本文中介绍了如何配置gRPC和brpc。

gRPC的配置真的是蛋疼。主要原因是官方的推荐方式是从源码编译。于是我首先花了一个下午用小水管clone了下gRPC和它的十来个三方库。

安装和配置protobuf

gRPC依赖于protobuf,但我们不能盲目安装protobuf,例如Ubuntu默认的protobuf是2.6.1,而Google给的helloworld示例需要protobuf 3才能编译,所以不能用apt安装。此外即使从源码编译装,也没必要去protobuf的官方仓库。注意gRPC不会自动帮你make install protobuf,所以需要到gRPC的third_party目录下找到protobuf的源码编译安装。更新gRPC中的子模块的命令如下

1
git submodule update --init --recursive

进入protobuf目录

1
2
3
4
5
6
./autogen
./configure
make
make check
sudo make install
sudo ldconfig

注意点

  1. ./autogen里面会下载gmock,可能会失败,这里可以直接进去注释掉相关语句。
  2. 需要安装libtool,否则会出现undefined macro: AC_PROG_LIBTOOL
  3. 需要安装autoconf,否则会出现autoreconf: command not found
  4. 由于protobuf的安装版本不带调试信息,有时候会出现Can't parse message of type的情况,不能assert然后调试dump,可参考https://github.com/protocolbuffers/protobuf/issues/1102编译调试版本。

gRPC项目的编译

编译gRPC应当遵循BUILDING.md

1
2
3
4
5
6
7
8
apt-get install build-essential autoconf libtool pkg-config
apt-get install libgflags-dev libgtest-dev
apt-get install clang libc++-dev
git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
cd grpc
git submodule update --init
make
make install

在编译gRPC项目时,对于客户端会生成.pb.h.pb.cc两个文件;但是服务端则需要.grpc.pb.h.grpc.pb.cc两个文件。这四个文件是由protoc通过不同的指令生成的,如下所示,这里grpc_out即表示生成服务端需要使用的带.grpc.pb系文件,plugin字段需要我们指明grpc_cpp_plugin这个插件所在的位置。如果我们从源码编译安装的话,这个插件一般会在/usr/local/lib里面,我们一般需要将这个路径export出来

1
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

当然也可以运行这个脚本来一劳永逸地解决问题。

注意点

  1. 注意使用git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc来下载grpc的release版本对应的repo。
  2. unused-variable的错误,可见https://github.com/grpc/grpc/issues/16739,不过解决不了问题,我是替换掉所有-Werror`解决的

Makefile

如果直接使用Makefile来编译,我们需要先按照下面的规则生成四个文件,此外我们还需要将程序链接到protobufgrpc++grpc这三个库上。gRPC的官方Git仓库中提供了Makefile的demo。

1
2
$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/route_guide.proto
$ protoc -I ../../protos --cpp_out=. ../../protos/route_guide.proto

CMake

gRPC的官方Git仓库也提供了CMakeLists的Demo,不过我并没有能够成功进行编译,它提示缺少gRPCConfig.cmake或grpc-config.cmake文件,于是我放弃了来自官方的CMakeLists。在我先前的protobuf试用中,我了解了cmake中的PROTOBUF_GENERATE_CPP宏可以编译出.pb.h.pb.cc两个文件,现在我们需要依葫芦画瓢搞出一个PROTOBUF_GENERATE_GRPC_CPP宏就行了。在爆栈网的一篇回答中我找到了一个实用的实现,借助于它我实现了自己的CMakeLists。注意目前gRPC的编译需要C++11标准的支持,所以这里我使用了SET(CMAKE_CXX_COMPILER /usr/bin/g++-7 CACHE PATH "" FORCE)来强制设置了编译器。

可以在https://github.com/CalvinNeo/ARPC/blob/master/test/grpc/中找到我的一个完整的配置

gRPC使用的坑

14 Connection Refused

Linux下的Connection Refused这是由于我们配置了http_proxy的缘故

Server::Shutdown

根据https://github.com/grpc/grpc/issues/10324,这个函数也会阻塞

gRPC服务类型

gRPC支持Unary RPC、Server streaming RPC、Client streaming RPC和Bidirectional streaming RPC。

常用类

服务端

一般服务器端的类包含ServiceImpl类(一般派生自Service类)、grpc::ServerBuilder类、Server类。我们一般可以用下面的类进行封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct Context{
ServiceImpl * service;
std::unique_ptr<Server> server;
ServerBuilder * builder;
Context(/* 这里可以加上一点用来初始化ServiceImpl类的上下文 */){
service = new ServiceImpl(/* 上下文 */);
builder = new ServerBuilder();
builder->AddListeningPort(/* 地址和端口 */, grpc::InsecureServerCredentials());
builder->RegisterService(service);
server = std::unique_ptr<Server>{builder->BuildAndStart()};
wait_thread = std::thread([&](){server->Wait();});
};
~Context(){
server->Shutdown();
wait_thread.join();
delete service;
delete builder;
}
std::thread wait_thread;
}

Service

::package_name::ServiceName::Service(简称为Service)类是由Protobuf根据我们proto文件自动生成的类,实际上是用C++描述了ServiceName这个service。我们一般会从Service中派生出一个类ServiceImpl: Service类来实现这些方法。

1
2
3
4
struct ServiceImpl: public ::package_name::ServiceName::Service{
Status RPC1(grpc::ServerContext* context, const package_name::RPC1Request*, package_name::RPC1Response*);
/* ... */
}

Status

grpc::Status类一般用来维护RPC调用的状态

Server

Server类

gRPC线程模型

gRPC提供了异步和同步的API。
https://github.com/grpc/grpc/pull/10919/files

brpc的编译安装

brpc依赖gflags、protobuf、openssl和leveldb。其中gflags按照以下方式安装

1
2
3
4
5
git clone https://github.com/gflags/gflags.git
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DBUILD_SHARED_LIBS=ON -DGFLAGS_NAMESPACE=google ../
make
sudo make install

openssl通过以下命令安装

1
apt install libssl-dev openssl

接下来make,注意一定要加上--with-glog不然例如braft里面的test的用到glog的都有问题

1
2
sh config_brpc.sh --headers="/usr/local/include /usr/include" --libs=/usr/local/lib --cxx=g++ --cc=gcc --nodebugsymbols --with-glog
make

在gRPC 3.7版本后,GoogleOnceInit被用std::call_once取代了,这时候编译brpc就会出现问题。这里推荐使用3.6的版本,特别注意,在换版本之后一定要先make clean掉已经生成的pd文件。

这里注意一下,我们可以同时从源码和从apt安装protobuf的,这时候我们就要指定我们要用哪个版本的protobuf。一般apt安装在不带local的文件夹下。

特别地,gflags可能和glog产生冲突,这时候可以先apt purge掉gflags再编译glog。或者可以采用./configure CPPFLAGS="-I/usr/local/include" LDFLAGS="-L/usr/local/lib"命令编译。