gRPC庫C++構建及示例

2021-03-02 軟體工程師之路

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目錄.

gRPC的依賴是通過git的submodules來關聯的,代碼下載下來之後可以看到.gitmodules文件,內部的git倉庫地址都需要替換成Gitee的,例如:

[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

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了.

相關焦點

  • 使用gRPC, NATS, CockroachDB構建EventSourcing/CQRS的微服務
    在這篇文章中,我將演示Go中的一個簡單的EventSourcing/CQRS示例,演示如何解決基於微服務的分布式系統的實際挑戰。這篇文章的目的不是介紹EventSourcing和CQRS的最佳實踐,而是通過在Go中編寫一個簡單的示例來介紹這兩種架構和設計模式,它為構建基於微服務的分布式系統提供了解決方案,以便處理事務和數據。
  • 了解gRPC框架
    --grpc_python_out=.# clone the repository$ git clone -b v1.37.1 https://github.com/grpc/grpc# say "hello, world"$ cd grpc/examples/python/helloworld[4] 運行示例程序
  • Nginx宣布正式支持gRPC,附示例代碼
    通過 NGINX 來管理 gRPC 服務下面的示例對 gRPC 的 Hello World 快速入門教程進行了修改,用它來創建一個簡單的客戶端到伺服器端應用。例子中提供了 NGINX 的配置信息,而把應用程式的實現留給讀者,不過文中還是會給出一些提示。
  • gRPC-Web 正在進行GA
    我代表雲原生計算基金會,很高興地宣布gRPC-Web的GA版本,這是一個JavaScript客戶端庫,使Web應用程式能夠直接與後端gRPC服務通信,而不需要HTTP伺服器充當中介。這意味著你現在可以通過使用.proto文件定義客戶端和伺服器端數據類型和服務接口,輕鬆構建真正的端到端gRPC應用程式體系結構。
  • gRPC介紹
    開篇語關於grpc用法我只是基本的使用,如果我用的方式不對,煩請指出來,一起討論。介紹imggRPC是一個由google開發的,跨語言的,高性能遠程調用框架,使客戶端和服務端應用程式可以透明的進行通訊,並簡化了連接系統的構建,使用http/2作為通信協議,使用protocol buffers作為序列化協議。客戶端應用程式可以直接在其他計算機上的伺服器應用程式上調用該方法,就好像它是本地對象一樣。
  • Envoy和gRPC-Web:REST的鮮新替代方案
    gRPC-Web是一個JavaScript客戶機庫,它允許web應用程式使用Envoy來與後端gRPC服務交互,而不是使用自定義HTTP伺服器作為中介
  • Springboot整合Google開發的gRPC
    多語言支持(C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java)gRPC 支持多種語言,並能夠基於語言自動生成客戶端和服務端功能庫。正如您將在我們的示例中更詳細地看到的那樣,您可以在普通的 proto 文件中定義 gRPC 服務,並將 RPC 方法參數和返回類型指定為協議緩衝區消息:// The greeter service definition.
  • gRPC是什麼?你知道嗎?
    gRPC提供了一種簡單的方法來精確的定義服務,並且為客戶端和服務端自動生成可靠的功能庫。在gRPC客戶端可以直接調用不同伺服器上的遠程程序,使用姿勢看起來就像調用本地程序一樣,很容易去構建分布式應用和服務。和很多RPC系統一樣,服務端負責實現定義好的接口並處理客戶端的請求,客戶端根據接口描述直接調用需要的服務。客戶端和服務端可以分別使用gRPC支持的不同語言實現。
  • gRPC Golang/Python 使用
    目前提供 C、Java 和 Go 語言版本,分別是:grpc,grpc-java,grpc-go.其中C版本支持C,C++,Node.js,Python,Ruby,Objective-C,PHP 和 C# 支持.gRPC 基於 HTTP/2 標準設計,帶來諸如雙向流、流控、頭部壓縮、單 TCP連接上的多復用請求等特。這些特性使得其在行動裝置上表現更好,更省電和節省空間佔用。
  • Dapr 交通流量控制示例
    ,因此今天給大家分享一個交通控制的示例程序,幫助大家對Dapr 的理解更進一步。這個示例是github上的一位荷蘭的 MVP 寫的https://github.com/EdwinVW/dapr-traffic-control ,我把它翻譯成中文介紹給大家,示例的場景是用於使用 Dapr 模擬流量控制系統。對於此示例,我們將使用超速攝像頭裝置,該裝置可在多個荷蘭高速公路上找到。
  • 不再僅僅支持HTTP:gRPC來到Cloud Run
    你只需要把一個簡單的Dockerfile和運行幾個命令:docker build -t gcr.io/my-project/my-grpc-app:latest .docker push gcr.io/my-project/my-grpc-app:latestgcloud run deploy --image gcr.io/my-project
  • 淺議C#客戶端和服務端通信的幾種方法:Rest和GRPC和其他
    實際上,默認情況下,ASP.NET Web API被構建為REST Web服務。對於.NET Core 3.0,有一個完全託管的庫,稱為.NET的gRPC[15]。對於其中的任何內容,您都可以使用gRPC C#[16],它是使用本機代碼構建的。這並不意味著適用於.NET的gRPC可以替代gRPC C#。
  • 樹莓派編譯構建 OpenCV C++ 項目
    OpenCV 是流行的計算機視覺庫。最近因項目需要,要在樹莓派上編譯構建 OpenCV C++ 項目。踩了不少坑,分享出來。
  • 阿里雲 IP 地理位置庫(淘寶IP庫)實踐(後篇)
    阿里雲 IP 地理位置庫(淘寶IP庫)實踐(後篇)上篇文章提到如何在容器環境中使用阿里雲離線IP地理位置庫,前文中測試性能看起來滿足日常離線小樣本、低頻率私密調用性能沒有大的問題,但是針對大量數據的場景,再不搭建集群多實例的情況下,顯然是無法滿足需求的。
  • 【每周一庫】- Tonic 基於Rust的gRPC實現
    創建該庫的目的是為了對async/await具有一流的支持,並充當用Rust編寫的生產系統的核心構建塊。特性入門本教程作為Tonic的入門指導,前提是你對Rust有基礎了解並且使用過protocol buffers。
  • C++之字符串類學習總結
    答案是c++中並沒有提供原生的字符串類型。二、在C++標準庫中提供了string類型:string直接支持字符串連接string直接支持字符串的大小比較string直接支持字符串查找和提取string直接支持字符串的插入和替換代碼示例:#include <iostream>#include <string>using namespace std;
  • 用 CMake 構建 Python C/C++ 擴展
    ''',    ext_modules=[module1])這種方式對於絕大多數簡單的項目應該是足夠了,而當你需要用到一些 C/C++ 第三方庫的時候可能會遇到因為某些原因需要將三方庫的源碼和項目源碼一起進行編譯的情況(比如 abseil-cpp[5]),這個情況下往往會遇到 C/C++ 依賴管理的問題,CMake
  • gRPC Java 服務端實現簡析
    2.2 啟動和初始化流程啟動一個gRPC服務是一件非常簡單的事情,官方給出了範例:io.grpc.examples.helloworld.HelloWorldServer#startio.grpc.netty.NettyServerHandler#start
  • C++之標準庫的學習總結
    它的意義是將整數1按位左移2位,即:0000 0001   演變成      0000 0100重載左移操作符,將變量或者常量左移到一個對象中代碼示例二、c++標準庫:1、標準庫的特性:2、C++編譯環境的組成:
  • C#調用Python代碼通過gRPC的實現
    本文介紹的例子中實現完整的代碼需要:Python部分:grpc 庫、grpc-toolsC#部分:(NuGet包)Grpc.net.Client、Google.Protobuf、Grpc.Tools本文通過實現「一個簡單的兩個整數相的python代碼,使用C#客戶端傳入參數調用這個python代碼,獲取返回值並列印」這樣的例子來展示如何通過gRPC