介紹
grpc 是 Google 在 2015 年 2 月底時(shí)發(fā)布的一款開源 RPC 框架,其源碼是由 C 語(yǔ)言編寫的。
按照 Google 的說(shuō)法,grpc 是:A high performance, open source, general RPC framework that puts mobile and HTTP/2 first.
簡(jiǎn)單來(lái)說(shuō),它是一個(gè)高性能,開源,將移動(dòng)和 HTTP/2 放在首位的通用的 RPC 框架.
由于它構(gòu)建于 HTTP/2 標(biāo)準(zhǔn),因此它有很多優(yōu)秀的特點(diǎn),主要如下:
- bidirectional streaming
- flow control
- header compression
- multiplexing requests over a single TCP connection
這些特性在移動(dòng)設(shè)備上節(jié)約電池使用時(shí)間和數(shù)據(jù)使用,加速服務(wù)和運(yùn)行在云上的web應(yīng)用。
源碼編譯及安裝
源碼地址:https://github.com/grpc/grpc
- 安裝依賴
- 配置工具:build-essential, autoconf, libtool
- 測(cè)試支持: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:
$ LIBTOOL=glibtool LIBTOOLIZE=glibtoolize make
protoc
GRPC 默認(rèn)使用 protobuf 作為消息格式,為 protoc 是 protobuf 協(xié)議的編譯器,因此,在構(gòu)建 GRPC 之前確保 protoc 已經(jīng)安裝。
注:gRpc 源碼中的 Makefile 文件中會(huì)自動(dòng)檢測(cè)當(dāng)前系統(tǒng)是否已經(jīng)安裝了 protoc,如果沒(méi)有安裝,那么就會(huì)自動(dòng)從其項(xiàng)目中的第三方庫(kù)源碼目錄中進(jìn)行安裝。
- 編譯
執(zhí)行下列命令進(jìn)行編譯構(gòu)建和安裝
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 上的版本,那么直接
$ git clone https://github.com/grpc/grpc
注意:
編譯過(guò)程中可能會(huì)遇到 openssl1.1.0
與 老版本 openssl1.0.1
不兼容的問(wèn)題(grpc 使用的老版本 openssl1.0.1),也就是說(shuō),如果本機(jī)環(huán)境使用的是 openssl1.1.0,那么編譯 grpc 時(shí)會(huì)出現(xiàn)報(bào)錯(cuò),可以去 Google 中搜索解決方案。
- 編譯完成后生成的文件
構(gòu)建完成之后,想要知道生成了哪些文件,我們可以通過(guò)查看 .gitignore
文件來(lái)獲知。
打開 .gitignore
文件,可以看到如下內(nèi)容:
# C/C++ build outputs
.build/
bins
gens
libs
objs
根據(jù)注釋含義可知,編譯成功后會(huì)在根目錄下新建上述目錄,不同的目錄包含了不同的文件,這里主要來(lái)看 libs, bins 這兩個(gè)目錄,
bins 目錄中包含了所有編譯構(gòu)建后生成二進(jìn)制程序文件。
查看 bins/opt/
會(huì)發(fā)現(xiàn),生成了下列二進(jìn)制程序,它們都是 grpc 對(duì)各個(gè)語(yǔ)言默認(rèn)插件的實(shí)現(xiàn):
grpc_cpp_plugin
grpc_csharp_plugin
grpc_node_plugin
grpc_objective_c_plugin
grpc_php_plugin
grpc_python_plugin
grpc_ruby_plugin
libs 目錄中則包含了動(dòng)態(tài)庫(kù),靜態(tài)庫(kù)等文件。查看 `libs/opt/`` 可以看到以 libgrpc, libgrpc++ 等為前綴的許多靜態(tài)和動(dòng)態(tài)庫(kù)文件,這里不一一列舉:
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/
...
- 安裝
執(zhí)行 make install
就會(huì)自動(dòng)安裝上述編譯所生成的庫(kù)及二進(jìn)制程序。這一步非常簡(jiǎn)單,其實(shí)質(zhì)只是把相應(yīng)的文件拷貝的系統(tǒng)目錄而已。
想要知道具體會(huì)安裝哪些文件,查看 Makefile 即可。
安裝內(nèi)容相當(dāng)?shù)膹?fù)雜,實(shí)際上,我們沒(méi)有必要去了解每個(gè)細(xì)節(jié),只要知道哪些主要文件會(huì)被安裝,以及他們會(huì)被安裝在哪個(gè)目錄就可以了。
我在這里對(duì)整個(gè)安裝過(guò)程做了一個(gè)總結(jié),如下:
(1)要安裝的文件
按照文件類型,我把要安裝或者說(shuō)拷貝到系統(tǒng)目錄的文件主要有以下幾個(gè)部分:
- grcp 的相關(guān)頭文件
- 靜態(tài)庫(kù),動(dòng)態(tài)庫(kù)
- 可執(zhí)行文件 (也即以 _plugin 結(jié)尾的各語(yǔ)言的 grpc 插件)
(2) 安裝至的目錄 (默認(rèn) /usr/local
)
如果沒(méi)有指定安裝目錄的 prefix,那么默認(rèn)的 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.
從上文可知,如果源碼包沒(méi)有提供 configure 配文件,最可靠的辦法就是編輯 Makefile 文件來(lái)指定 prefix。
按照不同的類型區(qū)別,它們分別將安裝在如下目錄
(1)頭文件
$prefix/include
注:源碼文件通常會(huì)在 $prefix/src 中,但通常沒(méi)有必要安裝源碼。
(2)靜態(tài)庫(kù)及動(dòng)態(tài)庫(kù) (.a 文件,.so,.so.x.x 文件)
對(duì)不同語(yǔ)言的支持,會(huì)有不同的靜態(tài)庫(kù)及動(dòng)態(tài)庫(kù)文件。
$prefix/lib
(3) plugins,也就是二進(jìn)制文件
$prefix/bin
(4) pkg-config 文件
$prefix/lib/pkg-config
(5)證書文件 certs
$prefix/share/grpc
比如,根證書文件為:
$prefix/share/grpc/roots.pem
GRPC 實(shí)例
這里編寫一個(gè) C++ 使用 GRPC 的實(shí)例來(lái)試驗(yàn) GRPC 的使用。
官方的 grpc 源碼中提供了示例,進(jìn)入 examples/cpp/helloworld
目錄,執(zhí)行 make 來(lái)編譯所提供的 cpp 例子(greeter_server.cc
, greeter_client.cc
, etc):
可能會(huì)出現(xiàn)如下錯(cuò)誤提示:
$ 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
根據(jù)提示可知,執(zhí)行 pkg-config --libs protobuf grpc++ grpc
出錯(cuò)了,因?yàn)?PKG_CONFIG_PATH
沒(méi)有設(shè)置。
解決辦法很簡(jiǎn)單,只需按如下設(shè)置該變量即可:
$ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
設(shè)置完成之后,執(zhí)行 pkg-config --libs protobuf grpc++ grpc
就會(huì)顯示如下結(jié)果:
-L/usr/local/lib -lprotobuf -pthread -lpthread -lgrpc++ -lgrpc
現(xiàn)在,在 examples/cpp/helloworld
目錄執(zhí)行 make 就成功了,執(zhí)行結(jié)果如下:
$ 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 中指定了,先執(zhí)行 protoc 生成 --cpp_out
和 --grpc_out
兩類文件,均是 cpp 頭文件,它們是:helloworld.pb.h
和 helloworld.grpc.pb.h
實(shí)際上,helloworld.pb.h 是定義 protobuf Message 類型相關(guān)的代碼,而 helloworld.grpc.pb.h
頭文件是定義 grpc Service 相關(guān)的代碼
需要注意的是:在 cpp 中,這兩部分內(nèi)容是分開放在兩個(gè)頭文件中的,而用 grpc-go 生成的 Message 和 Service 代碼則是在同一個(gè) go 文件中。
編譯好 greeter_client
, greeter_server
等可執(zhí)行程序之后,執(zhí)行 ./greeter_client
,會(huì)出現(xiàn)如下結(jié)果:
$ ./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
是一個(gè)運(yùn)行時(shí)庫(kù),上述錯(cuò)誤顯示,greeter_client
動(dòng)態(tài)連接該庫(kù)時(shí)沒(méi)找到這個(gè)庫(kù),這個(gè)庫(kù)的搜索目錄是在 LD_LIBRARY_PATH
環(huán)境變量中定義里的,因此如果沒(méi)在這個(gè)目錄中,那么就會(huì)找不到,如果已經(jīng)在這個(gè)目錄中,可以執(zhí)行 sudo ldconfig -v
來(lái)更新 /etc/ld.so.conf
的 cache(也即 /etc/ld.so.cache
文件)。
由于安裝時(shí),所有的 libgrpc 相關(guān)的靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)都會(huì)安裝在 /usr/local/lib
目錄中,而這個(gè)目錄已經(jīng)加入 LD_LIBRARY_PATH
環(huán)境變量,經(jīng)過(guò)查看發(fā)現(xiàn),/usr/local/lib
目錄中確實(shí)沒(méi)有 libgrpc++.so.1
,原因是什么呢?
原來(lái),在 grpc 源碼目錄的構(gòu)建生成的 libs/opt
目錄下, libgrpc++.so.1
這個(gè)文件確實(shí)是存在的(它是一個(gè) symlink 文件,真正指向的是 libgrpc++.so.1.5.0-dev
)。
但是執(zhí)行 make install
安裝的時(shí)候,卻變成了 libgrpc++.so.4
,那就扯淡了。那么,最簡(jiǎn)單的解決辦法就是把構(gòu)建目錄中的 libgrpc++.so.1
拷貝到 /usr/local/lib
中去。或者把 libgrpc++.so.4
改成 libgrpc++.so.1
。
最后執(zhí)行
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
全文完