网上有很多例子,但是都不符合我的要求。
这里的目标是搭建grpc + cpp在Linux下Demo工程(企业开发可直接修改使用)。
1. 我需要将grpc打包成静态库放到项目工程中,但是网上的例子都是将环境装到了系统里。
单开发机可能很爽,多机器开发的时候,就难受了…
2. 我需要确定grpc依赖的东西与grpc目录无关(它应该有单独的目录)
简单来讲就是grpc依赖了protobuf,这种情况下,我希望将protobuf的静态库单独放一个目录。
参考教程: https://grpc.io/docs/languages/cpp/quickstart/
一、拉取grpc源码
github地址:https://github.com/grpc/grpc
gitee地址(镜像):https://gitee.com/tkxiong/grpc
一般来说在国内,GitHub下载都很慢,所以都是用gitee镜像来下载的。
1 |
git clone -b v1.36.4 https://gitee.com/tkxiong/grpc |
P.s. 这里一定要记得指定版本号,不能用master分支。
二、拉取submodule
没错,submodule都单独写了一小节。
原因是submodule拉下来也是github的地址,下载很慢。所以依次做了gitee镜像。
修改submodule的地址:
1 2 3 |
cd grpc git submodule init vim .git/config |
我这里修改后的config文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true [submodule] active = . [remote "origin"] url = https://gitee.com/tkxiong/grpc.git fetch = +refs/heads/*:refs/remotes/origin/* [branch "master"] remote = origin merge = refs/heads/master [submodule "third_party/abseil-cpp"] url = https://gitee.com/tkxiong/abseil-cpp.git [submodule "third_party/benchmark"] url = https://gitee.com/tkxiong/benchmark.git [submodule "third_party/bloaty"] url = https://gitee.com/tkxiong/bloaty.git [submodule "third_party/boringssl-with-bazel"] url = https://gitee.com/tkxiong/boringssl.git [submodule "third_party/cares/cares"] url = https://gitee.com/tkxiong/cares.git [submodule "third_party/envoy-api"] url = https://gitee.com/tkxiong/envoy-api.git [submodule "third_party/googleapis"] url = https://gitee.com/tkxiong/googleapis.git [submodule "third_party/googletest"] url = https://gitee.com/tkxiong/googletest.git [submodule "third_party/libuv"] url = https://gitee.com/tkxiong/libuv.git [submodule "third_party/protobuf"] url = https://gitee.com/tkxiong/protobuf.git [submodule "third_party/protoc-gen-validate"] url = https://gitee.com/tkxiong/protoc-gen-validate.git [submodule "third_party/re2"] url = https://gitee.com/tkxiong/re2.git [submodule "third_party/udpa"] url = https://gitee.com/tkxiong/udpa.git [submodule "third_party/zlib"] url = https://gitee.com/tkxiong/zlib.git |
不要直接复制, 不同版本submodule也不一样!!!看清楚之后把submdule挨个替换掉即可。
P.s. 如果grpc更新版本之后有了新的submodule, 也要同样做镜像, 修改文件内容
更新submodule
1 |
git submodule update |
三、编译grpc
目前路径在 grpc 项目根目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#创建编译文件夹cmake/build & 最终生成静态库的文件夹release mkdir -p cmake/build mkdir -p release/ cd cmake/build # 编译 cmake -DgRPC_INSTALL=ON \ -DgRPC_BUILD_TESTS=OFF \ -DCMAKE_INSTALL_PREFIX=../../release \ ../.. make -j make install |
这里主要参考了 grpc.io 的编译方法,但是修改输出目录地址为release目录。
make -j 命令编译过程中报错:
1 |
c++: fatal error: Killed signal terminated program cc1plus |
解决办法:
- 扩大机器内存
- 多执行几次 make -j 命令 (我8G虚拟机内存, 也就执行了十几次)
make install 命令执行完后,就可以在 grpc/release 目录里面得到四个文件夹。
1 2 3 4 5 6 7 |
[xiong@AMDServer release]$ tree -L 1 . ├── bin ├── include ├── lib └── share 4 directories, 0 files |
四、搭建Demo工程
这里我们需要在项目中同时实现Server和Client两部分。
不过在此之前,我们先单独实现Client和Server,然后将他们集成到同一个项目中。
P.S> 一个真正的线上项目,必然同时有收和发两个功能的。
比如我们上线一个grpc服务,它一定会有提供grpc服务的功能(后台项目, 接收),同时它也一定会有向服务发现服务注册的功能(发送)。
1、文件夹框架
创建一个文件夹,名称为 GrpcProjectDemo
在文件夹内创建 bin, protos, src, third_party文件夹。
1 2 3 4 5 6 |
. ├── bin ├── protos ├── src └── third_party 4 directories, 0 files |
这四个文件夹分别用来存放:
- bin: 编译出来的文件+配置文件
- prtos: grpc 协议源文件
- src: 开发源代码
- third_party: 第三方库
为了让项目编译能稍微快一些,一般做法是将静态库.a文件放到第三方库中。
现在我们先把grpc的release文件中,我们需要的部分放到third_party中。
1 2 3 4 5 6 7 8 9 10 11 12 |
[xiong@AMDServer GrpcServerDemo]$ tree -L 3 . ├── bin ├── protos ├── src └── third_party └── grpc ├── bin ├── include ├── lib └── share 9 directories, 0 files |
2. grpc Proto协议
进入protos目录,写一个简单的Hello协议,作为Demo使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
syntax = "proto3"; package Hello; service Hello { rpc Hello (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; } |
接下来我们要给它写一个hello协议的脚本 generate.sh ,生成对应的pb.cc 和 pb.h 文件。
我们设置生成的位置是 src/lib/hello_protos 文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#!/bin/bash # proto文件目录 PROTO_FILE_PATH=./ # 生成C++协议目录 OUT_PUT_CXX_PATH=../src/lib/hello_protos #删除C++目录下旧文件 rm -f ${OUT_PUT_CXX_PATH}/*pb.cc rm -f ${OUT_PUT_CXX_PATH}/*pb.h GENERATE_PROTO_FILES=("hello") #判断创建文件夹 if [ ! -d ${OUT_PUT_CXX_PATH} ];then mkdir -p ${OUT_PUT_CXX_PATH}; fi echo "start generate C++ proto code!! " for filename in ${GENERATE_PROTO_FILES[@]} do ../third_party/grpc/bin/protoc \ -I ${PROTO_FILE_PATH} \ --grpc_out=${OUT_PUT_CXX_PATH} \ --plugin=protoc-gen-grpc=../third_party/grpc/bin/grpc_cpp_plugin \ ${PROTO_FILE_PATH}/${filename}.proto ../third_party/grpc/bin/protoc \ -I ${PROTO_FILE_PATH} \ --cpp_out=${OUT_PUT_CXX_PATH} \ ${PROTO_FILE_PATH}/${filename}.proto done echo "generate over!! " |
需要注意的是, 这个脚本需要用到 protoc 和 grpc_cpp_plugin 这两个东西,所以我们需要指定这两个文件的路径,需要注意到我这里使用的是相对路径。
给文件加上执行权限, 执行一下试试
1 2 3 4 |
[xiong@AMDServer protos]$ chmod +x generate.sh [xiong@AMDServer protos]$ ./generate.sh start generate C++ proto code!! generate over!! |
再看一次文件夹目录关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[xiong@AMDServer GrpcServerDemo]$ tree -L 3 . ├── bin ├── protos │ ├── generate.sh │ └── hello.proto ├── src │ └── lib │ └── hello_protos │ ├── hello.grpc.pb.cc │ ├── hello.grpc.pb.h │ ├── hello.pb.cc │ └── hello.pb.h └── third_party └── grpc ├── bin ├── include ├── lib └── share 11 directories, 2 files |
这里实际上我执行了两次 tree命令, 一次是 -L 4 目的是将src/lib/hello_protos 文件夹下的四个文件展示出来给你们看。
所以结果跟我的不一致也正常…
3. 实现客户端 && 服务端
客户端代码地址: https://gitee.com/tkxiong/grpc-client-demo
服务端代码地址:https://gitee.com/tkxiong/grpc-server-demo
它们的代码结构是完全一致的,唯一不同的是main函数的实现。
P.s. 经过试验是这样, 我在Ubuntu 20下面编译通过的代码,到了CentOS机器上,会有grpc的问题导致编译不通过…
所以CentOS的用户需要自己编译grpc…
另外: 如果你的grpc编译出来多一个lib64文件夹的话, 这个文件夹肯定需要Copy过去的。CMakeLists.txt 文件代码也需要加一行
1 2 3 |
# grpc CMake Folder SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "third_party/grpc/lib/cmake") SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "third_party/grpc/lib64/cmake") |