想不开系列 —— C++折腾gRPC小记
前言
本文记录了19下半年,因为项目需要开发一个监控采集数据的Agent程序,我使用C++折腾gRPC的经历。
1、gRPC简介
服务(Service) ,正是作为业务服务器的最根本职能,而下面是我认为的一些常见服务架构:
都2020年了,应该没有人不知道rpc是啥了吧?而说到常用的rpc框架如下:
我选择了gRPC作为与用户方通信的方式,gRPC是在HTTP/2之上实现的RPC框架, 它运行在TCP协议之上,相比于传统的REST/JSON机制有诸多的优点: (1)基于HTTP/2之上的二进制协议(Protobuf序列化机制) (2)一个连接上可以多路复用,并发处理多个请求和响应 (3)支持多种语言的类库实现(然而我选择了困难度最高的c++) (4)服务定义文件和自动代码生成(.proto文件和Protobuf编译工具) (5)pb作为通信数据交换格式,好处不用多说(感兴趣请详见文章《浅谈protobuf》)
gRPC的通信模型架构如下:
而gRPC使用了Http2.0作为协议层,大大加强了通信的性能与安全性:
我从自己通信的请求中抓了下请求的包,长这个样子:
简而言之,gGRPC把元数据放到HTTP/2 Headers里,请求参数序列化之后放到DATA Frame里。
2、安装C++ gRPC编译/执行环境
好了,谈架构和原理,大家也许都头头是道,实际上手才发现,除了google本身的golang儿子,其他语言使用起来其实都有些麻烦。。。
环境/内核要求: tlinux 2.2以上版本,gcc编译器4.8.0以上版本,我是Cent7.0+的环境安装的。
(1)安装相关依赖
yum install build-essential autoconf libtool pkg-config
yum install libgflags-dev libgtest-dev
yum install clang libc++-dev
我们可以执行/usr/bin/gcc --version
查看c库版本:
gcc (GCC) 4.8.5
(2)clone代码到机器
git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
(3)解压、初始化submodule
cd grpc
git submodule update --init
(4) 编译
export LD_LIBRARY_PATH=LDLIBRARYPATH:/usr/local/libexport:LDLIBRARYPATH=/{target-path}/grpc/thirdparty/protobuf/src/.libs/:LD_LIBRARY_PATH
make -j32
make install
完成编译后,我们可以看到,目录大致如下:
[root@host:/usr/local/grpc]$ ls
AUTHORS examples OWNERS
bazel Gemfile package.xml
bins gens PYTHON-MANIFEST.in
BUILD grpc.bzl Rakefile
build_config.rb gRPC-Core.podspec README.md
BUILD.gn gRPC-C++.podspec requirements.bazel.txt
BUILDING.md grpc.def requirements.txt
build.yaml grpc.gemspec setup.cfg
cache.mk grpc.gyp setup.py
cmake gRPC.podspec src
CMakeLists.txt gRPC-ProtoRPC.podspec summerofcode
CODE-OF-CONDUCT.md gRPC-RxLibrary.podspec templates
composer.json include test
CONCEPTS.md libs third_party
config.m4 LICENSE tools
config.w32 Makefile TROUBLESHOOTING.md
CONTRIBUTING.md MANIFEST.md WORKSPACE
doc NOTICE.txt
etc objs
其中不同gRPC版本,依赖的protobuf版本也不同,执行install后,gRPC相关库默认被安装到/usr/local/lib/
目录下(
libaddress_sorting.a libgrpc++_reflection.a
libaddress_sorting.so libgrpc++_reflection.so
libaddress_sorting.so.7 libgrpc++_reflection.so.1
libaddress_sorting.so.7.0.0 libgrpc++_reflection.so.1.20.0
libgpr.a libgrpc.so
libgpr.so libgrpc++.so
libgpr.so.7 libgrpc++.so.1
libgpr.so.7.0.0 libgrpc++.so.1.20.0
libgrpc.a libgrpc.so.7
libgrpc++.a libgrpc.so.7.0.0
libgrpc_cronet.a libgrpc_unsecure.a
libgrpc++_cronet.a libgrpc++_unsecure.a
libgrpc_cronet.so libgrpc_unsecure.so
libgrpc++_cronet.so libgrpc++_unsecure.so
libgrpc++_cronet.so.1 libgrpc++_unsecure.so.1
libgrpc++_cronet.so.1.20.0 libgrpc++_unsecure.so.1.20.0
libgrpc_cronet.so.7 libgrpc_unsecure.so.7
libgrpc_cronet.so.7.0.0 libgrpc_unsecure.so.7.0.0
libgrpc++_error_details.a libprotobuf.a
libgrpc++_error_details.so libprotobuf.la
libgrpc++_error_details.so.1 libprotobuf-lite.a
libgrpc++_error_details.so.1.20.0 libprotobuf-lite.la
libgrpcpp_channelz.a libprotoc.a
libgrpcpp_channelz.so libprotoc.la
libgrpcpp_channelz.so.1 pkgconfig
libgrpcpp_channelz.so.1.20.0
(5)基本环境变量配置
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export LD_LIBRARY_PATH=/usr/local/grpc/third_party/protobuf/src/.libs/:$LD_LIBRARY_PATH
export PATH=/usr/local/grpc/bins/opt/:$PATH
export PATH=/usr/local/grpc/bins/opt/protobuf:$PATH
export PKG_CONFIG_PATH=/usr/local/grpc/libs/opt/pkgconfig
export PKG_CONFIG_PATH=/usr/local/grpc/third_party/protobuf/:$PKG_CONFIG_PATH
export CPLUS_INCLUDE_PATH=/usr/local/grpc/third_party/protobuf/src/
export CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/local/grpc/third_party/protobuf/conformance/third_party/jsoncpp/
可以通过执行protobuf --version
,看看配置有没有生效,并且看下pb版本。我用的是3.7.0
[root@host:/data/home/wind/grpc/src/cpp]$ protoc --version
libprotoc 3.7.0
(6)demo编译测试
./grpc/examples/cpp/helloworld
目录下可以进行demo测试:
[root@host:/usr/local/grpc/examples/cpp/helloworld]$ make clean
rm -f *.o *.pb.cc *.pb.h greeter_client greeter_server greeter_async_client greeter_async_client2 greeter_async_server
[root@host:/usr/local/grpc/examples/cpp/helloworld]$ make -j8
protoc -I ../../protos --cpp_out=. ../../protos/helloworld.proto
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 greeter_client.o greeter_client.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_server.o greeter_server.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_async_client.o greeter_async_client.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_async_client2.o greeter_async_client2.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o greeter_async_server.o greeter_async_server.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o helloworld.pb.o helloworld.pb.cc
g++ -std=c++11 `pkg-config --cflags protobuf grpc` -c -o helloworld.grpc.pb.o helloworld.grpc.pb.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++ 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
g++ helloworld.pb.o helloworld.grpc.pb.o greeter_async_client.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_async_client
g++ helloworld.pb.o helloworld.grpc.pb.o greeter_async_client2.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_async_client2
g++ helloworld.pb.o helloworld.grpc.pb.o greeter_async_server.o -L/usr/local/lib `pkg-config --libs protobuf grpc++ grpc` -Wl,--no-as-needed -lgrpc++_reflection -Wl,--as-needed -ldl -o greeter_async_server
(7) 运行测试
[root@host:/usr/local/grpc/examples/cpp/helloworld]$ ./greeter_server
Server listening on 0.0.0.0:50051
[root@host:/usr/local/grpc/examples/cpp/helloworld]$ ./greeter_client
Greeter received: Hello world
有以上回显,表示编译和环境已经standby!