介绍
所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数一样。如下图所示就是一个典型的RPC结构图
gRPC vs. Restful API
gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不一定)。不过gRPC还是有些特有的优势,如下:
- gRPC可以通过protobuf来定义接口,从而可以有更加严格的接口约束条件。关于protobuf可以参见笔者之前的小文Google Protobuf简明教程
- 另外,通过protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。
- gRPC可以方便地支持流式通信(理论上通过http2.0就可以使用streaming模式, 但是通常web服务的restful api似乎很少这么用,通常的流式数据应用如视频流,一般都会使用专门的协议如HLS,RTMP等,这些就不是我们通常web服务了,而是有专门的服务器应用。
使用场景
- 需要对接口进行严格约束的情况,比如我们提供了一个公共的服务,很多人,甚至公司外部的人也可以访问这个服务,这时对于接口我们希望有更加严格的约束,我们不希望客户端给我们传递任意的数据,尤其是考虑到安全性的因素,我们通常需要对接口进行更加严格的约束。这时gRPC就可以通过protobuf来提供严格的接口约束。
- 对于性能有更高的要求时。有时我们的服务需要传递大量的数据,而又希望不影响我们的性能,这个时候也可以考虑gRPC服务,因为通过protobuf我们可以将数据压缩编码转化为二进制格式,通常传递的数据量要小得多,而且通过http2我们可以实现异步的请求,从而大大提高了通信效率。
但是,通常我们不会去单独使用gRPC,而是将gRPC作为一个部件进行使用,这是因为在生产环境,我们面对大并发的情况下,需要使用分布式系统来去处理,而gRPC并没有提供分布式系统相关的一些必要组件。而且,真正的线上服务还需要提供包括负载均衡,限流熔断,监控报警,服务注册和发现等等必要的组件。不过,这就不属于本篇文章讨论的主题了。
安装
本文通过源码方式安装grpc。参考:https://grpc.io/docs/languages/cpp/quickstart/#install-grpc
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 42
| git clone --recurse-submodules -b v1.39.0 https://github.com/grpc/grpc
# 配置安装的路径,并添加到环境变量 export MY_INSTALL_DIR=/opt/grpc mkdir -p $MY_INSTALL_DIR export PATH="$MY_INSTALL_DIR/bin:$PATH"
# 安装依赖包 sudo apt install -y build-essential autoconf libtool pkg-config
# 开始编译安装 # build and locally install gRPC, Protocol Buffers, and Abseil:
cd grpc mkdir -p cmake/build pushd cmake/build cmake -DgRPC_INSTALL=ON \ -DgRPC_BUILD_TESTS=OFF \ -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \ ../.. make make install popd
mkdir -p third_party/abseil-cpp/cmake/build pushd third_party/abseil-cpp/cmake/build cmake -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \ -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE \ ../.. make -j make install popd
cd third_party/abseil-cpp ./autogen.sh ./configure make -j8
sudo make install
sudo ldconfig protoc --version
|
sql安装
1
| sudo apt-get install mysql-server
|
使用
编译源码中的helloworld例子:
1 2 3 4 5
| cd examples/cpp/helloworld mkdir -p cmake/build pushd cmake/build cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../.. make -j
|
运行server:
打开另一个终端运行客户端,接受到输出如下:
1 2
| ./greeter_client Greeter received: Hello world
|
issue
make过程中出现该问题:fatal error: absl/synchronization/mutex.h: No such file or directory
35 | #include "absl/synchronization/mutex.h"
解决方法:修改该文件夹中的CMakeLists.txt的include_directories为以下:
1 2 3 4
| include_directories("${CMAKE_CURRENT_BINARY_DIR}" ../../../third_party/abseil-cpp )
|
CmakeLists.txt使用
方案一
需要提供的cmake文件。
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 42 43 44
| cmake_minimum_required(VERSION 3.5.1)
project(demo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") set(root_dir ${CMAKE_CURRENT_SOURCE_DIR})
set(PROTO_DIR proto) list(APPEND CMAKE_MODULE_PATH ${root_dir}/cmake)
find_package(Threads REQUIRED) find_package(ProtobufWithTargets REQUIRED) find_package(gRPC REQUIRED) message(STATUS "Using protobuf ${Protobuf_VERSION}") message(STATUS "Using gRPC ${gRPC_VERSION}")
include(GRPCProtoGenerate)
set(GRPC_IMPORT_DIRS "${PROTO_DIR}/comm") set(GRPC_GENERATE_CPP_APPEND_PATH TRUE) grpc_generate_cpp(GRPC_SRCS GRPC_HDRS "${PROTO_DIR}/pum/pum.proto" "${PROTO_DIR}/comm/comm.proto" ) message("grpc heads: ${GRPC_HDRS}") message("grpc srcs: ${GRPC_SRCS}")
add_executable(${PROJECT_NAME} ${avi_dir}/main.cpp ${GRPC_SRCS} )
target_include_directories(${PROJECT_NAME} PUBLIC ${GRPC_HDRS}/proto ${GRPC_HDRS}/proto/comm )
target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf gRPC::grpc++_reflection gRPC::grpc++ )
|
方案二
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 42 43 44 45 46 47 48 49 50 51
| cmake_minimum_required(VERSION 3.5.1)
project(Demo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(root_dir ${CMAKE_CURRENT_SOURCE_DIR})
find_package(Threads REQUIRED)
find_package(Protobuf CONFIG REQUIRED) message(STATUS "Using protobuf ${Protobuf_VERSION}")
find_package(gRPC CONFIG REQUIRED) message(STATUS "Using gRPC ${gRPC_VERSION}")
set(proto_path ${root_dir}/proto) set(proto_files "comm" "pum") set(proto_output_path ${proto_path}/cpp) set(proto_output_list) # reserver the file from proto cpp foreach(svc ${proto_files}) set(protodir ${proto_path}/${svc}) set(protofile ${protodir}/${svc}.proto) set(outputdir ${proto_output_path}/${svc}) list(APPEND proto_output_list "${outputdir}/${svc}.pb.cc" "${outputdir}/${svc}.pb.h" "${outputdir}/${svc}.grpc.pb.cc" "${outputdir}/${svc}.grpc.pb.h" ) endforeach(svc)
add_executable(${PROJECT_NAME} demo.cpp ${proto_output_list} )
target_include_directories(${PROJECT_NAME} PUBLIC ${proto_path} ${proto_path}/cpp/comm )
target_link_libraries(${PROJECT_NAME} protobuf::libprotobuf gRPC::grpc++_reflection gRPC::grpc++ # cryptopp )
|
方案三
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| cmake_minimum_required(VERSION 3.5.1) project(pumpkin)
cmake_policy(SET CMP0077 NEW)
set(CMAKE_CXX_STANDARD 11)
find_package(Threads REQUIRED)
set(protobuf_MODULE_COMPATIBLE TRUE) find_package(Protobuf CONFIG REQUIRED) message(STATUS "Using protobuf ${Protobuf_VERSION}")
set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) set(_REFLECTION gRPC::grpc++_reflection)
if(CMAKE_CROSSCOMPILING) find_program(_PROTOBUF_PROTOC protoc) else() set(_PROTOBUF_PROTOC $<TARGET_FILE:protobuf::protoc>) endif()
find_package(gRPC CONFIG REQUIRED) message(STATUS "Using gRPC ${gRPC_VERSION}")
set(_GRPC_GRPCPP gRPC::grpc++)
if(CMAKE_CROSSCOMPILING) find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) else() set(_GRPC_CPP_PLUGIN_EXECUTABLE $<TARGET_FILE:gRPC::grpc_cpp_plugin>) endif()
set(SRCS) set(HDRS) set(PROTO_FILES "datacenter/data.proto" "datacenter/datacenter.proto" "pum/pum.proto") set(GRPC_GENERATE_CPP_APPEND_PATH TRUE)
if(${GRPC_GENERATE_CPP_APPEND_PATH}) # Create an include path for each file specified foreach(FIL ${PROTO_FILES}) get_filename_component(ABS_FIL ${FIL} ABSOLUTE) get_filename_component(ABS_PATH ${ABS_FIL} PATH) list(FIND _grpc_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1) list(APPEND _grpc_include_path -I ${ABS_PATH}) endif() endforeach() else() set(_grpc_include_path -I ${CMAKE_CURRENT_SOURCE_DIR}) endif()
if(DEFINED GRPC_IMPORT_DIRS) foreach(DIR ${GRPC_IMPORT_DIRS}) get_filename_component(ABS_PATH ${DIR} ABSOLUTE) list(FIND _grpc_include_path ${ABS_PATH} _contains_already)
if(${_contains_already} EQUAL -1) list(APPEND _grpc_include_path -I ${ABS_PATH}) endif() endforeach() endif()
foreach(FIL ${PROTO_FILES}) get_filename_component(ABS_FIL ${FIL} ABSOLUTE) get_filename_component(FIL_WE ${FIL} NAME_WE)
# if(NOT GRPC_GENERATE_CPP_APPEND_PATH) get_filename_component(FIL_DIR ${FIL} DIRECTORY)
if(FIL_DIR) set(FIL_WE "${FIL_DIR}/${FIL_WE}") endif()
message(${FIL_WE})
# endif() set(out_dir ${CMAKE_CURRENT_SOURCE_DIR}/cpp) list(APPEND SRCS "${out_dir}/${FIL_WE}.pb.cc") list(APPEND SRCS "${out_dir}/${FIL_WE}.grpc.pb.cc") list(APPEND HDRS "${out_dir}/") list(APPEND HDRS "${out_dir}/")
# message(${SRCS}) file(MAKE_DIRECTORY ${out_dir}/${FIL_DIR}) add_custom_command( OUTPUT "${out_dir}/${FIL_WE}.pb.cc" "${out_dir}/${FIL_WE}.pb.h" "${out_dir}/${FIL_WE}.grpc.pb.cc" "${out_dir}/${FIL_WE}.grpc.pb.h" COMMAND protobuf::protoc ARGS "--cpp_out=${out_dir}/${FIL_DIR}" ${_grpc_include_path} ${ABS_FIL} COMMAND protobuf::protoc ARGS "--grpc_out=${out_dir}/${FIL_DIR}" "--plugin=protoc-gen-grpc=$<TARGET_FILE:gRPC::grpc_cpp_plugin>" ${_grpc_include_path} ${ABS_FIL} DEPENDS ${ABS_FIL} protobuf::protoc gRPC::grpc_cpp_plugin COMMENT "Running C++ protocol buffer compiler on ${FIL}" VERBATIM ) endforeach()
message("src: ${SRCS}") message("har: ${HDRS}")
add_library(${PROJECT_NAME} ${SRCS} )
target_include_directories(${PROJECT_NAME} PRIVATE ${HDRS} )
target_link_libraries(${PROJECT_NAME} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF} )
|