>

介绍

grpc 是 Google 在 2015 年 2 月底时发布的一款开源 RPC 框架,其源码是由 C 语言编写的。
按照 Google 的说法,grpc 是:A high performance, open source, general RPC framework that puts mobile and HTTP/2 first.
简单来说,它是一个高性能,开源,将移动和 HTTP/2 放在首位的通用的 RPC 框架.

由于它构建于 HTTP/2 标准,因此它有很多优秀的特点,主要如下:

  • bidirectional streaming
  • flow control
  • header compression
  • multiplexing requests over a single TCP connection

这些特性在移动设备上节约电池使用时间和数据使用,加速服务和运行在云上的web应用。

源码编译及安装

源码地址:https://github.com/grpc/grpc

  1. 安装依赖
  • 配置工具:build-essential, autoconf, libtool
  • 测试支持:libgflags-dev
  • 编译工具:clang, libc++-dev

Tip: when building, you may want to explicitly set the LIBTOOL and LIBTOOLIZE environment variables when running make to ensure the version installed by brew is being used:

1
$ LIBTOOL=glibtool LIBTOOLIZE=glibtoolize make

protoc
GRPC 默认使用 protobuf 作为消息格式,为 protoc 是 protobuf 协议的编译器,因此,在构建 GRPC 之前确保 protoc 已经安装。

注:gRpc 源码中的 Makefile 文件中会自动检测当前系统是否已经安装了 protoc,如果没有安装,那么就会自动从其项目中的第三方库源码目录中进行安装。

  1. 编译

执行下列命令进行编译构建和安装

1
2
3
4
5
git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
cd grpc
git submodule update --init
make
sudo make install

注意到第一行命令是安装 release 分支中的版本,如果想安装最新的 master HEAD 上的版本,那么直接

1
$ git clone https://github.com/grpc/grpc

注意:
编译过程中可能会遇到 openssl1.1.0 与 老版本 openssl1.0.1不兼容的问题(grpc 使用的老版本 openssl1.0.1),也就是说,如果本机环境使用的是 openssl1.1.0,那么编译 grpc 时会出现报错,可以去 Google 中搜索解决方案。

  1. 编译完成后生成的文件

构建完成之后,想要知道生成了哪些文件,我们可以通过查看 .gitignore 文件来获知。

打开 .gitignore 文件,可以看到如下内容:

1
2
3
4
5
6
# C/C++ build outputs
.build/
bins
gens
libs
objs

根据注释含义可知,编译成功后会在根目录下新建上述目录,不同的目录包含了不同的文件,这里主要来看 libs, bins 这两个目录,
bins 目录中包含了所有编译构建后生成二进制程序文件。
查看 bins/opt/ 会发现,生成了下列二进制程序,它们都是 grpc 对各个语言默认插件的实现:

1
2
3
4
5
6
7
grpc_cpp_plugin
grpc_csharp_plugin
grpc_node_plugin
grpc_objective_c_plugin
grpc_php_plugin
grpc_python_plugin
grpc_ruby_plugin

libs 目录中则包含了动态库,静态库等文件。查看 `libs/opt/`` 可以看到以 libgrpc, libgrpc++ 等为前缀的许多静态和动态库文件,这里不一一列举:

1
2
3
4
5
6
7
8
9
libgpr.a
libgpr.so.4.0.0-dev
libgrpc.a
libgrpc++.a
libgrpc++.so.1.5.0-dev
libgrpc.so.4 -> libgrpc.so.4.0.0-dev
libgrpc.so.4.0.0-dev
pkgconfig/
...
  1. 安装

执行 make install 就会自动安装上述编译所生成的库及二进制程序。这一步非常简单,其实质只是把相应的文件拷贝的系统目录而已。
想要知道具体会安装哪些文件,查看 Makefile 即可。
安装内容相当的复杂,实际上,我们没有必要去了解每个细节,只要知道哪些主要文件会被安装,以及他们会被安装在哪个目录就可以了。

我在这里对整个安装过程做了一个总结,如下:

(1)要安装的文件
按照文件类型,我把要安装或者说拷贝到系统目录的文件主要有以下几个部分:

    1. grcp 的相关头文件
    1. 静态库,动态库
    1. 可执行文件 (也即以 _plugin 结尾的各语言的 grpc 插件)

(2) 安装至的目录 (默认 /usr/local

如果没有指定安装目录的 prefix,那么默认的 prefix 是 /usr/local。如果想使用其他 prefix 比如 /usr,那么可以编辑 Makefile 文件修改 prefix 变量。
或者 make install DISTDIR=/usr/local 。

It depends on the package. If the Makefile is generated by GNU autotools (./configure) you can usually set the target location like so:
./configure –prefix=/usr/local
If the Makefile is not generated by autotools, but distributed along with the software, simply open it up in an editor and change it. The install target directory is probably defined in a variable somewhere.

从上文可知,如果源码包没有提供 configure 配文件,最可靠的办法就是编辑 Makefile 文件来指定 prefix。

按照不同的类型区别,它们分别将安装在如下目录

(1)头文件
$prefix/include
注:源码文件通常会在 $prefix/src 中,但通常没有必要安装源码。

(2)静态库及动态库 (.a 文件,.so,.so.x.x 文件)
对不同语言的支持,会有不同的静态库及动态库文件。
$prefix/lib

(3) plugins,也就是二进制文件
$prefix/bin

(4) pkg-config 文件
$prefix/lib/pkg-config

(5)证书文件 certs
$prefix/share/grpc

比如,根证书文件为:
$prefix/share/grpc/roots.pem

GRPC 实例

这里编写一个 C++ 使用 GRPC 的实例来试验 GRPC 的使用。

官方的 grpc 源码中提供了示例,进入 examples/cpp/helloworld 目录,执行 make 来编译所提供的 cpp 例子(greeter_server.cc, greeter_client.cc, etc):

可能会出现如下错误提示:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ g++ helloworld.pb.o helloworld.grpc.pb.o greeter_client.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_client
Package grpc++ was not found in the pkg-config search path.
Perhaps you should add the directory containing `grpc++.pc'
to the PKG_CONFIG_PATH environment variable
No package 'grpc++' found
Package grpc was not found in the pkg-config search path.
Perhaps you should add the directory containing `grpc.pc'
to the PKG_CONFIG_PATH environment variable
No package 'grpc' found
/usr/bin/ld: helloworld.pb.o: undefined reference to symbol '_ZN6google8protobuf8internal16RegisterAllTypesEPKNS0_8MetadataEi'
/usr/lib/libprotobuf.so.13: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
make: *** [Makefile:42: greeter_client] Error 1

根据提示可知,执行 pkg-config --libs protobuf grpc++ grpc 出错了,因为 PKG_CONFIG_PATH 没有设置。

解决办法很简单,只需按如下设置该变量即可:

1
$ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig

设置完成之后,执行 pkg-config --libs protobuf grpc++ grpc 就会显示如下结果:

1
-L/usr/local/lib -lprotobuf -pthread -lpthread -lgrpc++ -lgrpc

现在,在 examples/cpp/helloworld 目录执行 make 就成功了,执行结果如下:

1
2
3
4
5
6
7
8
$ protoc -I ../../protos --cpp_out=. ../../protos/helloworld.proto
$ g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o helloworld.pb.o helloworld.pb.cc
$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/helloworld.proto
$ g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o helloworld.grpc.pb.o helloworld.grpc.pb.cc
$ g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_client.o greeter_client.cc
$ g++ helloworld.pb.o helloworld.grpc.pb.o greeter_client.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_client
$ g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_server.o greeter_server.cc
$ g++ helloworld.pb.o helloworld.grpc.pb.o greeter_server.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_server

Makefile 中指定了,先执行 protoc 生成 --cpp_out--grpc_out 两类文件,均是 cpp 头文件,它们是:helloworld.pb.hhelloworld.grpc.pb.h
实际上,helloworld.pb.h 是定义 protobuf Message 类型相关的代码,而 helloworld.grpc.pb.h 头文件是定义 grpc Service 相关的代码

需要注意的是:在 cpp 中,这两部分内容是分开放在两个头文件中的,而用 grpc-go 生成的 Message 和 Service 代码则是在同一个 go 文件中。

编译好 greeter_client, greeter_server 等可执行程序之后,执行 ./greeter_client,会出现如下结果:

1
2
3
$ ./greeter_client
./greeter_client: error while loading shared libraries: libgrpc++.so.1: cannot open shared object file: No such file or directory
./greeter_client: error while loading shared libraries: libgrpc++_reflection.so.1: cannot open shared object file: No such file or directory

libgrpc++.so 是一个运行时库,上述错误显示,greeter_client 动态连接该库时没找到这个库,这个库的搜索目录是在 LD_LIBRARY_PATH环境变量中定义里的,因此如果没在这个目录中,那么就会找不到,如果已经在这个目录中,可以执行 sudo ldconfig -v 来更新 /etc/ld.so.conf 的 cache(也即 /etc/ld.so.cache 文件)。

由于安装时,所有的 libgrpc 相关的静态库和动态库都会安装在 /usr/local/lib 目录中,而这个目录已经加入 LD_LIBRARY_PATH 环境变量,经过查看发现,/usr/local/lib 目录中确实没有 libgrpc++.so.1,原因是什么呢?

原来,在 grpc 源码目录的构建生成的 libs/opt 目录下, libgrpc++.so.1 这个文件确实是存在的(它是一个 symlink 文件,真正指向的是 libgrpc++.so.1.5.0-dev)。
但是执行 make install 安装的时候,却变成了 libgrpc++.so.4,那就扯淡了。那么,最简单的解决办法就是把构建目录中的 libgrpc++.so.1 拷贝到 /usr/local/lib 中去。或者把 libgrpc++.so.4 改成 libgrpc++.so.1

最后执行

1
2
g++ -std=c++11 `pkg-config --cflags protobuf grpc`  -c -o route_guide.grpc.pb.o route_guide.grpc.pb.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o route_guide_client.o route_guide_client.cc

全文完!


如果你对我的文章感兴趣,欢迎留言或者关注我的专栏。

微信公众号:“知辉”

搜索“deliverit”或

扫描二维码