掌握 gRPC:从安装到构建第一个C++ 和Python微服务
一、前言
1. gRPC的概念和用途
在现代软件开发中,gRPC已经成为一个重要的通信框架。作为一个由Google开发并维护的开源项目,gRPC允许不同的服务和应用之间进行有效的远程过程调用(RPC)。它基于HTTP/2协议,不仅提高了通信的效率,还简化了跨语言服务的互操作性。gRPC使用Protocol Buffers作为其接口定义语言,这是一种语言中立的、平台中立的接口描述语言,用于序列化结构化数据。
gRPC的用途广泛,从微服务架构中的服务间通信到支持移动应用和浏览器客户端的后端服务。它的设计使其非常适合于构建分布式系统和云服务,尤其是在需要高效、低延迟通信的场景中。
2. gRPC的优势
gRPC之所以在现代应用开发中受到青睐,主要归功于它的几个关键优势。
- 首先,它基于HTTP/2,这不仅意味着更高的性能,还包括了对多路复用、服务器推送等现代网络技术的支持。这使得gRPC在处理大量并发请求时表现出色,同时减少了通信过程中的延迟。
- 其次,gRPC的跨平台和跨语言支持极大地简化了不同服务和应用之间的集成。无论是C++、Java、Python还是任何其他支持的语言,gRPC都能确保不同语言编写的服务可以无缝交互。
- 此外,gRPC通过使用Protocol Buffers,提供了一种强类型的接口定义方式。这不仅使得服务间的通信更加清晰,还提高了数据传输的效率。与此同时,gRPC支持双向流和流控制,允许在一个持久连接中进行双向通信,这对于构建实时交互应用来说是一个巨大的优势。
3. gRPC的应用场景
gRPC适用于多种应用场景,特别是那些需要高效、可靠通信的场景。在微服务架构中,gRPC常被用于服务之间的通信,提供了一种比传统HTTP RESTful API更高效的方法。此外,它也被广泛用于构建API,特别是那些需要支持多种语言客户端的API。
在云计算和分布式系统领域,gRPC的高效性和低延迟特性使其成为组件间通信的理想选择。同时,在物联网(IoT)领域,gRPC因其轻量级和高效性而被用于设备间的通信。
总的来说,gRPC凭借其高性能、跨语言支持和强类型接口,已经成为现代软件开发中不可或缺的一部分。
二、gRPC的基本原理
1. RPC(远程过程调用)简介
远程过程调用(RPC)是一种使得在不同计算机环境中运行的程序能够相互调用函数或方法的技术。简而言之,RPC允许一台计算机上的程序调用另一台计算机上的程序,就像调用本地程序一样,而无需关心底层网络技术的细节。RPC抽象了网络通信的复杂性,使开发者能够专注于业务逻辑的实现,而不是通信细节。
2. Protocol Buffers的作用
Protocol Buffers,简称Protobuf,是由Google开发的一种语言无关、平台无关的序列化框架。它用于序列化结构化数据,类似于XML或JSON,但更小、更快、更简单。在gRPC中,Protobuf用作接口定义语言(IDL),用于定义服务接口和消息格式。
Protobuf的主要优势包括:
- 高效的编码:生成的数据非常紧凑,这使得它在网络传输中非常高效。
- 跨语言支持:支持多种编程语言,包括Java、C++、Python等。
- 清晰的结构定义:使用.proto文件定义数据结构,使得数据模型在不同的服务和应用之间清晰且一致。
- 向后兼容性:可以在不破坏已部署程序的情况下更新数据结构。
3. gRPC与传统HTTP/REST服务的比较
gRPC和传统的HTTP/REST服务在多个方面有显著的不同:
- 传输协议:
- gRPC基于HTTP/2,支持双向流、多路复用、服务器推送等特性,这使得gRPC在性能上优于基于HTTP/1.1的REST服务。
- HTTP/REST服务通常使用HTTP/1.1,每个请求/响应循环都需要一个新的TCP连接,这可能导致更高的延迟。
- 数据格式:
- gRPC使用Protobuf作为数据交换格式,这是一种二进制格式,更加高效。
- 传统的REST服务通常使用JSON或XML,这些是文本格式,相比于Protobuf,它们更加冗长,解析速度也更慢。
- API设计:
- gRPC更倾向于使用严格定义的Protobuf来描述服务接口,这提供了更强的类型检查和结构清晰性。
- REST则更灵活,可以使用不同的URL和HTTP方法(如GET、POST)来描述操作,但这可能导致API的不一致性。
- 流控制:
- gRPC原生支持流控制,允许在单个连接中发送连续的消息流。
- 在REST中实现流通常更复杂,通常需要使用WebSockets或长轮询等技术。
总体而言,gRPC提供了一种更高效、更严格、更适合高性能需求的通信方式,特别是在微服务和分布式系统中。而HTTP/REST服务则在简单性和通用性方面有优势,更适合公共API和Web应用程序。
三、安装gRPC
参考grpc官网
1. 系统要求和前置条件
在开始安装gRPC之前,确保您的系统满足以下要求:
操作系统:Linux(如Ubuntu)、macOS 或 Windows。
编译工具:如gcc、g++(对于Linux/macOS)或 Visual Studio(对于Windows)。
Git:用于克隆gRPC仓库。
Python:如果打算使用Python版本的gRPC。
其他依赖:如autoconf、libtool、pkg-config等。
2. 安装步骤概述
安装gRPC涉及以下几个主要步骤:
安装依赖项。
克隆gRPC仓库。
编译和安装gRPC。
安装gRPC C++插件。
安装Protocol Buffers编译器。
(可选)安装Python版本的gRPC。
- 安装高级版本的cmake
安装使用gprc最低版本为3.15,目前使用sudo apt install cmake
得到的只有3.10,因此要卸载掉旧版本的cmake,源码编译高版本的。具体方法参考cmake github网站,这里不详述。 - 安装依赖项
在Linux系统(如Ubuntu)上,可以使用以下命令安装必要的依赖,常安装类似的库的可以忽略:
sudo apt update
sudo apt install build-essential autoconf libtool pkg-config
- 克隆gRPC仓库
使用Git克隆gRPC的GitHub仓库:
- 下载源码
git clone https://github.com/grpc/grpc
- 查看版本并选择合适的版本,这里选择v1.45.2相对较新的版本
git tag
git checkout v1.45.2
- 下载第三方依赖库,下载完后会发现整个grpc目录内容明显变大
git submodule update --init
- 编译和安装
MY_INSTALL_DIR=$HOME/.local
echo $MY_INSTALL_DIR
cd grpc
mkdir -p cmake/build
pushd cmake/build
cmake -DgRPC_INSTALL=ON \
-DgRPC_BUILD_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
../..
make -j $(nproc)
sudo make install
popd
特别说明一下,官网不建议直接安装在/usr/local里,怕不好卸载,所有选择
$HOME/.local
- protobuf安装
不用手动安装protobuf,不然版本可能和grcp不匹配,必须在 grpc 执行 git submodule update --init 命令之后生成的 third_party/protobuf 里面编译安装对应的 protobuf。
cd third_party/protobuf/
./autogen.sh
./configure --prefix=$HOME/.local # 路径选择之前安装grpc的路径
make
sudo make install
sudo ldconfig # 使得新安装的动态库能被加载
protoc --version
# 显示3.19.4
- 安装Python版本的gRPC
如果打算在Python项目中使用gRPC,可以通过pip安装:
pip install grpcio
为了生成Python的gRPC代码,还需要安装gRPC的Python插件:
pip install grpcio-tools
完成以上步骤后,应该已经成功在系统上安装了gRPC及其相关工具。
四、创建第一个gRPC c++项目
1. 设置项目结构
一个基本的gRPC项目通常包括服务定义(Protocol Buffers)、服务端实现和客户端实现。以下是一个C++项目的示例结构:
grpc-example/
│
├── protos/
│ └── my_service.proto
│
├── servers/
│ └── server.cpp
│
└── clients/
└── client.cpp
- protos/ 目录包含所有 .proto 文件,定义了gRPC服务和消息格式。
- server/ 目录包含服务端的实现代码。
- client/ 目录包含客户端的实现代码。
编写Protocol Buffers文件
在 protos/ 目录下创建一个 .proto 文件,如 my_service.proto。
2. 定义服务和消息
syntax = "proto3";
package myservice;
// 定义一个简单的消息
message MyRequest {
string name = 1;
}
message MyResponse {
string greeting = 1;
}
// 定义服务
service MyService {
// 定义一个RPC方法
rpc SayHello (MyRequest) returns (MyResponse);
}
3. 生成服务代码
- 修改环境变量
export PATH=$HOME/.local/bin/:$PATH
- 使用 protoc 编译器生成C++服务代码:
protoc -I protos/ protos/my_service.proto --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin`
这将在相应的目录中生成C++代码。
我们为了项目的需要,将cc文件改为cpp文件,生成的文件复制到protos目录下
4. 实现gRPC服务端
在 servers/ 目录下创建 server.cpp。
实现 .proto 文件中定义的服务接口。例如:
#include <grpcpp/grpcpp.h>
#include "my_service.grpc.pb.h"
using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using myservice::MyRequest;
using myservice::MyResponse;
using myservice::MyService;
// 实现服务类
class MyServiceImpl final : public MyService::Service {
Status SayHello(ServerContext* context, const MyRequest* request,
MyResponse* reply) override {
std::string prefix("Hello ");
reply->set_greeting(prefix + request->name());
return Status::OK;
}
};
void RunServer() {
std::string server_address("0.0.0.0:50051");
MyServiceImpl service;
ServerBuilder builder;
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<Server> server(builder.BuildAndStart());
std::cout << "Server listening on " << server_address << std::endl;
server->Wait();
}
int main(int argc, char** argv) {
RunServer();
return 0;
}
5. 实现gRPC客户端
在 clients/ 目录下创建 client.cpp。
实现代码以调用服务端的RPC方法。例如:
#include <grpcpp/grpcpp.h>
#include "my_service.grpc.pb.h"
using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using myservice::MyRequest;
using myservice::MyResponse;
using myservice::MyService;
class MyClient {
public:
MyClient(std::shared_ptr<Channel> channel)
: stub_(MyService::NewStub(channel)) {}
std::string SayHello(const std::string& user) {
MyRequest request;
request.set_name(user);
MyResponse reply;
ClientContext context;
Status status = stub_->SayHello(&context, request, &reply);
if (status.ok()) {
return reply.greeting();
} else {
std::cout << status.error_code() << ": " << status.error_message()
<< std::endl;
return "RPC failed";
}
}
private:
std::unique_ptr<MyService::Stub> stub_;
};
int main(int argc, char** argv) {
MyClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("World");
std::string reply = client.SayHello(user);
std::cout << "Client received: " << reply << std::endl;
return 0;
}
6. 创建Makefile并编译运行
- 根据上面的工程创建一个Makefile
CXX = g++
CXXFLAGS = -std=c++14 -g -O0 -I/home/ai/.local/include -Iinclude
LDFLAGS = -L/home/ai/.local/lib `pkg-config --libs grpc++ grpc`\
-lgrpc++_reflection\
-lprotobuf -lpthread -ldl
PROTOC = protoc
GRPC_CPP_PLUGIN = grpc_cpp_plugin
GRPC_CPP_PLUGIN_PATH ?= `which $(GRPC_CPP_PLUGIN)`
PROTOS_PATH = protos
OBJS_PATH = objs
vpath %.proto $(PROTOS_PATH)
vpath %.cpp servers:clients:$(PROTOS_PATH)
all: server client
server: $(OBJS_PATH)/my_service.pb.o $(OBJS_PATH)/my_service.grpc.pb.o $(OBJS_PATH)/server.o
$(CXX) $^ $(LDFLAGS) -o $@
client: $(OBJS_PATH)/my_service.pb.o $(OBJS_PATH)/my_service.grpc.pb.o $(OBJS_PATH)/client.o
$(CXX) $^ $(LDFLAGS) -o $@
$(OBJS_PATH)/%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJS_PATH)/*.o server client
- 修改环境变量
export LD_LIBRARY_PATH=$HOME/.local/lib:$LD_LIBRARY_PATH
下面就可以正常的编译运行了。
完成以上步骤后,gRPC项目基本结构就搭建好了。可以编译并运行服务端代码,然后编译并运行客户端代码来测试RPC方法的调用。
7. cmake编译运行
同时附上CMakeLists.txt的内容,供读者参考。
cmake_minimum_required(VERSION 3.8)
project(YourGrpcProject)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0")
# 包含 gRPC 的头文件
include_directories(/home/ai/mkdi.local/include)
include_directories(include)
# 设置 gRPC 的库路径
link_directories(/home/ai/.local/lib)
# 查找 gRPC 包
find_package(Protobuf REQUIRED) # 要先找Protobuf,否则会报错
find_package(gRPC REQUIRED)
# 如果 gRPC 没有被正确找到,您可能需要设置 gRPC_DIR
# set(gRPC_DIR /path/to/grpc/lib/cmake/grpc)
# 设置源文件
set(SERVER_SOURCE_FILES servers/server.cpp protos/my_service.grpc.pb.cpp protos/my_service.pb.cpp)
set(CLIENT_SOURCE_FILES clients/client.cpp protos/my_service.grpc.pb.cpp protos/my_service.pb.cpp)
# 添加可执行文件
add_executable(server ${SERVER_SOURCE_FILES})
add_executable(client ${CLIENT_SOURCE_FILES})
# 链接 gRPC 和相关库
target_link_libraries(server
gRPC::grpc++ gRPC::grpc++_reflection
protobuf pthread dl z)
target_link_libraries(client
gRPC::grpc++ gRPC::grpc++_reflection
protobuf pthread dl z)
# 如果需要从 .proto 文件生成 .cpp 和 .h 文件,可以使用以下命令,这块暂时没试过,仅供参考。
# find_program(PROTOC_EXECUTABLE protoc)
# find_program(GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin)
# add_custom_command(
# OUTPUT "${PROTOS_PATH}/my_service.grpc.pb.cc"
# "${PROTOS_PATH}/my_service.pb.cc"
# COMMAND ${PROTOC_EXECUTABLE}
# ARGS --grpc_out="${PROTOS_PATH}"
# --cpp_out="${PROTOS_PATH}"
# --plugin=protoc-gen-grpc="${GRPC_CPP_PLUGIN_EXECUTABLE}"
# -I "${PROTOS_PATH}"
# "${PROTOS_PATH}/my_service.proto"
# DEPENDS "${PROTOS_PATH}/my_service.proto")
五、创建一个gRPC python项目
1. 设置项目结构
同上一节c++项目
2. 定义服务和消息
同上一节c++项目
3. 生成服务代码
使用 protoc 编译器生成服务代码。根据您使用的编程语言,命令会有所不同。例如,对于Python:
python -m grpc_tools.protoc -I protos/ protos/my_service.proto --python_out=. --grpc_python_out=.
这将在相应的目录中生成Python代码。
4. 实现gRPC服务端
创建服务端代码文件server.py。
from concurrent import futures
import grpc
import my_service_pb2
import my_service_pb2_grpc
class MyServiceServicer(my_service_pb2_grpc.MyServiceServicer):
def SayHello(self, request, context):
response = my_service_pb2.MyResponse()
response.greeting = 'Hello, ' + request.name
return response
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
my_service_pb2_grpc.add_MyServiceServicer_to_server(MyServiceServicer(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
5. 实现gRPC客户端
在 client/ 目录下创建客户端代码文件。
实现代码以调用服务端的RPC方法。例如,使用Python:
import grpc
import my_service_pb2
import my_service_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = my_service_pb2_grpc.MyServiceStub(channel)
response = stub.SayHello(my_service_pb2.MyRequest(name='World'))
print("Client received: " + response.greeting)
if __name__ == '__main__':
run()
完成以上步骤后,python gRPC项目基本结构就搭建好了。可以运行服务端代码,然后运行客户端代码来测试RPC方法的调用。
六、总结
在本系列文章中,我们深入探讨了 gRPC 的基本概念、安装步骤以及如何在 C++ 和 Python 项目中实际应用 gRPC。从 gRPC 的基础出发,我们首先介绍了其与传统通信框架的比较,突出了 gRPC 在现代应用程序中提供的高效和可靠的 RPC 解决方案。
我们详细讨论了 gRPC 的安装过程,包括在不同操作系统上的安装指南和潜在的挑战。这为读者提供了一个坚实的基础,以便在自己的开发环境中顺利部署 gRPC。
接着,文章重点介绍了如何在 C++ 和 Python 项目中使用 gRPC。通过具体的项目案例,我们展示了在这两种语言中编写、编译和运行 gRPC 服务端和客户端的步骤。这些案例不仅提供了实际的代码示例,还讲解了如何在项目中有效地应用 gRPC,从而使读者能够更好地理解和运用这一技术。
总体而言,本系列文章旨在提供一个全面的 gRPC 学习指南,从基本概念到实际应用,特别是在 C++ 和 Python 项目中的应用。通过这些内容,读者可以获得必要的知识和技能,以在自己的项目中有效地利用 gRPC,实现高效和可靠的服务间通信。
七、参考资料
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!