gRPC库C++构建及示例
gRPC is a modern, open source, high-performance remote procedure call (RPC) framework that can run anywhere. gRPC enables client and server applications to communicate transparently, and simplifies the building of connected systems.
C++
开发者做什么事情都不太容易,网络编程原本已经很艰难了,想要使用gRPC
来降低难度,库构建又是一道坎儿.这里展示如何在残酷的网络环境下使用CMake
构建gRPC
库,并附带示例验证库构建结果.
如何下载gRPC
库源代码?
gRPC
自身内容很庞大,依赖也比较复杂,如果使用Github
下载,耗时费力不说,还容易中断.所幸Gitee上提供了部分镜像仓库,可以将gRPC
库自身内容快速拉取下来:
git clone -b v1.34.0 https://gitee.com/mirrors/grpc-framework.git grpc
注意,目前在Gitee
上只能找到gRPC
依赖的部分"官方"镜像仓库,网友提供的镜像仓库较旧,因而只能构造v1.34.0
版本.通过上述指令可以将v1.34.0
版本的gRPC
代码下载到grpc
目录.
[submodule "third_party/zlib"]
path = third_party/zlib
url = https://github.com/madler/zlib
# When using CMake to build, the zlib submodule ends up with a
# generated file that makes Git consider the submodule dirty. This
# state can be ignored for day-to-day development on gRPC.
ignore = dirty
使用了zlib
,在Gitee
上搜索其代码仓库为https://gitee.com/mirrors/zlib
,可以使用如下指令clone
:
git clone https://gitee.com/mirrors/zlib.git
因而替换成:
[submodule "third_party/zlib"]
path = third_party/zlib
#url = https://github.com/madler/zlib
url = https://gitee.com/mirrors/zlib.git
# When using CMake to build, the zlib submodule ends up with a
# generated file that makes Git consider the submodule dirty. This
# state can be ignored for day-to-day development on gRPC.
ignore = dirty
通过这种方法可以找到部分依赖库的最新镜像仓库,但是有一些找不到最新的,例如protobuf
等库,用户local-grpc
提供了gRPC
依赖的全部代码仓库,可以使用这些仓库(注意代码不是同步镜像,导致gRPC
只能构造相应版本),其中protobuf
链接为:
https://gitee.com/local-grpc/protobuf.git
这里将.gitmodules
修改为如下内容即可:
[submodule "third_party/zlib"]
path = third_party/zlib
#url = https://github.com/madler/zlib
url = https://gitee.com/mirrors/zlib.git
# When using CMake to build, the zlib submodule ends up with a
# generated file that makes Git consider the submodule dirty. This
# state can be ignored for day-to-day development on gRPC.
ignore = dirty
[submodule "third_party/protobuf"]
path = third_party/protobuf
#url = https://github.com/google/protobuf.git
url = https://gitee.com/local-grpc/protobuf.git
[submodule "third_party/googletest"]
path = third_party/googletest
#url = https://github.com/google/googletest.git
url = https://gitee.com/local-grpc/googletest.git
[submodule "third_party/benchmark"]
path = third_party/benchmark
#url = https://github.com/google/benchmark
url = https://gitee.com/mirrors/google-benchmark.git
[submodule "third_party/boringssl-with-bazel"]
path = third_party/boringssl-with-bazel
#url = https://github.com/google/boringssl.git
url = https://gitee.com/mirrors/boringssl.git
[submodule "third_party/re2"]
path = third_party/re2
#url = https://github.com/google/re2.git
url = https://gitee.com/local-grpc/re2.git
[submodule "third_party/cares/cares"]
path = third_party/cares/cares
#url = https://github.com/c-ares/c-ares.git
url = https://gitee.com/mirrors/c-ares.git
branch = cares-1_12_0
[submodule "third_party/bloaty"]
path = third_party/bloaty
#url = https://github.com/google/bloaty.git
url = https://gitee.com/local-grpc/bloaty.git
[submodule "third_party/abseil-cpp"]
path = third_party/abseil-cpp
#url = https://github.com/abseil/abseil-cpp.git
url = https://gitee.com/mirrors/abseil-cpp.git
branch = lts_2020_02_25
[submodule "third_party/envoy-api"]
path = third_party/envoy-api
#url = https://github.com/envoyproxy/data-plane-api.git
url = https://gitee.com/local-grpc/data-plane-api.git
[submodule "third_party/googleapis"]
path = third_party/googleapis
#url = https://github.com/googleapis/googleapis.git
url = https://gitee.com/mirrors/googleapis.git
[submodule "third_party/protoc-gen-validate"]
path = third_party/protoc-gen-validate
#url = https://github.com/envoyproxy/protoc-gen-validate.git
url = https://gitee.com/local-grpc/protoc-gen-validate.git
[submodule "third_party/udpa"]
path = third_party/udpa
#url = https://github.com/cncf/udpa.git
url = https://gitee.com/local-grpc/udpa.git
[submodule "third_party/libuv"]
path = third_party/libuv
#url = https://github.com/libuv/libuv.git
url = https://gitee.com/mirrors/libuv.git
使用如下指令拉取gRPC
所有依赖:
cd grpc
git submodule update --init
如果你希望使用CMake
的FetchContent
模块将gRPC
整合到自身工程中,可以将上述步骤下载完成的完整源代码打包成压缩包,存放在自己的ftp
等服务器上,然后使用如下Python
脚本计算出SHA512
:
import sys
import hashlib
# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536 # lets read stuff in 64kb chunks!
sha512 = hashlib.sha512()
with open(sys.argv[1], 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
sha512.update(data)
print("SHA512: {0}".format(sha512.hexdigest()))
以压缩包完整/相对路径为参数,执行上述脚本,复制得到的SHA512
内容,然后在你工程的CMakeLists.txt
中以如下方式使用:
include(FetchContent)
FetchContent_Declare(
gRPC
URL "gRPC源码压缩包服务器路径"
URL_HASH SHA512= "gRPC源码压缩包的SHA512"
##DOWNLOAD_DIR可以根据需要修改
DOWNLOAD_DIR ${CMAKE_SOURCE_DIR}/external/downloads/spdlog
)
set(FETCHCONTENT_QUIET OFF)
FetchContent_MakeAvailable(gRPC)
##工程中库使用方式
target_link_libraries(YourTarget grpc++)
注意在之前要安装必备的依赖,例如nasm.
不过上述使用方式构建时速度特别慢,以下展示如何直接构建出gRPC
库,并安装到指定路径.
如何构建gRPC
库
首先需要安装必要的依赖,例如在Windows
上需要以下内容:
-
Visual Studio 2015
或Visual Studio 2017
-
Git
-
CMake
-
nasm
,并且配置到PATH
环境变量中 -
可选的 Ninja
CMake
的使用方式大同小异,这里以Windows
为例展示,首先要进行配置,假设已经处于源代码路径中:
cmake -S . -B .build -G"Visual Studio 15 2017" -T v141 -A x64 -DgRPC_INSTALL=ON -DgRPC_BUILD_TESTS=OFF -DgRPC_BUILD_CSHARP_EXT=OFF -DCMAKE_INSTALL_PREFIX="安装路径"
上述配置使用的是Visual Studio 2017
及对应工具集,64位构建,使能安装动作,禁止构造测试用例和C#
扩展,并指定了安装路径.如果使用的是Visual Studio 2015
则替换成Visual Studio 14 2015
.
然后通过如下指令构建并安装:
cmake --build .build --target install --config Release
经过一段时间的等待,在安装路径就可以看到构建出的结果了.
这里需要说明的是,gRPC
无法将Debug
和Release
等多个配置安装到同一位置.开发者只能选择构建某一配置,然后在使用时工程构建也只能使用这一配置,通常可以选择Release
构造,如果面临调试需求,可以选择RelWithDebInfo
,即上述指令修改位:
cmake --build .build --target install --config RelWithDebInfo
上述**.build**路径为官方示例建议的路径,可以自行修改,但无必要.
经过上述操作,在安装路径下就有了可以使用的gRPC
库了.下面来看一下如何使用它.
如何运行Hello World
在gRPC
源代码的\examples\cpp\helloworld
路径下有如下代码文件:
greeter_server.cc
greeter_client.cc
greeter_async_server.cc
greeter_async_client.cc
greeter_async_client2.cc
在\examples\protos
下有对应的helloworld.proto
文件.
将上述文件拷贝到示例目录,例如helloworld
目录下,并添加CMakeList.txt
工程配置,最终目录结构如下:
greeter_server.cc
greeter_client.cc
greeter_async_server.cc
greeter_async_client.cc
greeter_async_client2.cc
helloworld.proto
CMakeLists.txt
将CMakeLists.txt
修改为类似如下内容:
cmake_minimum_required(VERSION 3.15)
#工程名,可自行修改
project(grpc-examples CXX)
#以下三个find_package需要添加,否则找不到对应的target会报错
find_package(Threads REQUIRED)#注意不要加CONFIG
find_package(protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
##添加共享的静态库,包含helloworld.proto中定义的RPC协议代码
add_library(helloworld)
target_sources(helloworld
PRIVATE "helloworld.proto"
)
##生成helloworld.proto对应的C++代码
protobuf_generate(TARGET helloworld LANGUAGE cpp)
##获取proto的grpc插件
get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION)
##生成helloworld.proto对应的gRPC-C++代码,以此来支持gRPC协议
protobuf_generate(TARGET helloworld LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${grpc_cpp_plugin_location}")
target_link_libraries(helloworld
PRIVATE gRPC::grpc++
)
##上述protobuf_generate将自动生成的代码存放于该位置,需要添加到include路径
target_include_directories(helloworld
PUBLIC ${CMAKE_CURRENT_BINARY_DIR}
)
##遍历列表创建应用程序
foreach(_target
greeter_client greeter_server
greeter_async_client greeter_async_client2 greeter_async_server)
add_executable(${_target} "${_target}.cc")
target_link_libraries(${_target}
PRIVATE helloworld
gRPC::grpc++ gRPC::grpc++_reflection
)
endforeach()
这里需要强调,官方文档在Windows
下构建存在问题,必须添加gRPC::grpc++_reflection
依赖,否则构建示例会报如下错误.
无法解析的外部符号 "void __cdecl grpc::reflection::InitProtoReflectionServerBuilderPlugin(void)
CMake
的protobuf_generate
模块可以用来辅助代码生成动作,开发者只需要将.proto
文件作为源代码添加到target
中,然后protobuf_generate
会根据配置自动生成对应代码,在.proto
文件发生变化时能够自动刷新.详细信息参见gRPC and Plugin support in CMake.
在输出目录找到生成的应用程序,例如greeter_server.exe
:
./greeter_server.exe
然后启动客户端:
./greeter_client.exe
客户端输出如下内容并退出:
Greeter received: Hello world
现在就可以查阅官方教程来学习gRPC
了.