深入掌握 Protobuf 与 RPC 的高效结合:实现C++工程中的高效通信 ...

打印 上一主题 下一主题

主题 1885|帖子 1885|积分 5655

在当代分布式系统中,高效的通信至关紧张,而Protobuf(Protocol Buffers)不仅能提供数据序列化的高效性,还常被用于与RPC(长途过程调用)框架结合,优化客户端与服务器之间的通信服从。本文将具体剖析如安在C++工程中使用Protobuf,并结合RPC框架,构建高性能的分布式系统通信机制。
一、Protobuf与RPC框架的通信流程概述


为了便于明白,以下结合Protobuf和RPC的通信流程进行具体剖析:

  • 界说IDL(接口界说语言)文件:通过Protobuf的.proto文件,界说服务接口和消息格式,这是全部客户端与服务器通信的底子。
  • 编译IDL文件:使用Protobuf编译器(protoc),生成客户端与服务器的代码骨架。这一步使得开发者无需关心底层通信细节,大幅淘汰编码工作量。
  • 客户端调用服务:客户端通过调用生成的骨架代码发起请求,Protobuf负责将消息进行序列化,并通过RPC框架的协议栈传输至服务器。
  • 服务器处置惩罚请求:服务器接收客户端请求,反序列化数据,执行相应的业务逻辑,并将结果序列化为相应数据返回给客户端。
  • 客户端接收相应:客户端接收到服务器的相应后,反序列化数据并继续处置惩罚后续业务逻辑。
二、Protobuf与RPC在C++中的现实应用

2.1 界说 .proto 文件

起首,我们必要通过Protobuf界说服务接口和消息结构。比方,下面的.proto文件界说了一个简单的用户服务接口,包括获取用户信息的服务:
  1. syntax = "proto3";
  2. package example;
  3. message User {
  4.     int32 id = 1;
  5.     string name = 2;
  6.     string email = 3;
  7. }
  8. message GetUserRequest {
  9.     int32 id = 1;
  10. }
  11. message GetUserResponse {
  12.     User user = 1;
  13. }
  14. service UserService {
  15.     rpc GetUser (GetUserRequest) returns (GetUserResponse);
  16. }
复制代码
2.2 编译 .proto 文件生成C++代码

使用protoc编译器生成C++代码:
  1. protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto
复制代码
编译后生成的文件包含以下两部分:


  • 消息类:user_service.pb.h 和 user_service.pb.cc
  • 服务接口及骨架代码:user_service.grpc.pb.h 和 user_service.grpc.pb.cc
2.3 实现服务器端逻辑

服务器端必要实现生成的服务接口,并通过RPC服务器处置惩罚客户端的请求。下面是基于gRPC的C++服务器实现:
  1. #include <iostream>
  2. #include <memory>
  3. #include <string>
  4. #include <grpcpp/grpcpp.h>
  5. #include "user_service.grpc.pb.h"
  6. using grpc::Server;
  7. using grpc::ServerBuilder;
  8. using grpc::ServerContext;
  9. using grpc::Status;
  10. using example::UserService;
  11. using example::GetUserRequest;
  12. using example::GetUserResponse;
  13. using example::User;
  14. class UserServiceImpl final : public UserService::Service {
  15. public:
  16.     Status GetUser(ServerContext* context, const GetUserRequest* request,
  17.                   GetUserResponse* reply) override {
  18.         // 模拟数据库操作
  19.         User* user = reply->mutable_user();
  20.         user->set_id(request->id());
  21.         user->set_name("Alice");
  22.         user->set_email("alice@example.com");
  23.         return Status::OK;
  24.     }
  25. };
  26. void RunServer() {
  27.     std::string server_address("0.0.0.0:50051");
  28.     UserServiceImpl service;
  29.     ServerBuilder builder;
  30.     builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  31.     builder.RegisterService(&service);
  32.     std::unique_ptr<Server> server(builder.BuildAndStart());
  33.     std::cout << "Server listening on " << server_address << std::endl;
  34.     server->Wait();
  35. }
  36. int main() {
  37.     RunServer();
  38.     return 0;
  39. }
复制代码
2.4 实现客户端逻辑

客户端通过调用生成的代码与服务器进行通信。以下是gRPC客户端的实当代码:
  1. #include <iostream>
  2. #include <memory>
  3. #include <grpcpp/grpcpp.h>
  4. #include "user_service.grpc.pb.h"
  5. using grpc::Channel;
  6. using grpc::ClientContext;
  7. using grpc::Status;
  8. using example::UserService;
  9. using example::GetUserRequest;
  10. using example::GetUserResponse;
  11. class UserServiceClient {
  12. public:
  13.     UserServiceClient(std::shared_ptr<Channel> channel)
  14.         : stub_(UserService::NewStub(channel)) {}
  15.     GetUserResponse GetUser(int id) {
  16.         GetUserRequest request;
  17.         request.set_id(id);
  18.         GetUserResponse response;
  19.         ClientContext context;
  20.         Status status = stub_->GetUser(&context, request, &response);
  21.         if (!status.ok()) {
  22.             std::cerr << "RPC failed: " << status.error_message() << std::endl;
  23.         }
  24.         return response;
  25.     }
  26. private:
  27.     std::unique_ptr<UserService::Stub> stub_;
  28. };
  29. int main() {
  30.     UserServiceClient client(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));
  31.     int user_id = 1;
  32.     GetUserResponse response = client.GetUser(user_id);
  33.     std::cout << "User ID: " << response.user().id() << std::endl;
  34.     std::cout << "Name: " << response.user().name() << std::endl;
  35.     std::cout << "Email: " << response.user().email() << std::endl;
  36.     return 0;
  37. }
复制代码
2.5 使用CMake构建工程

使用CMake构建Protobuf和gRPC项目,确保将生成的代码自动编译到项目中:
  1. cmake_minimum_required(VERSION 3.14)
  2. project(ProtobufRPCExample)
  3. find_package(Protobuf REQUIRED)
  4. find_package(gRPC REQUIRED)
  5. set(CMAKE_CXX_STANDARD 14)
  6. protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS user_service.proto)
  7. grpc_generate_cpp(GRPC_SRCS GRPC_HDRS user_service.proto)
  8. add_executable(server server.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS})
  9. target_link_libraries(server PRIVATE protobuf::libprotobuf gRPC::grpc++)
  10. add_executable(client client.cpp ${PROTO_SRCS} ${PROTO_HDRS} ${GRPC_SRCS} ${GRPC_HDRS})
  11. target_link_libraries(client PRIVATE protobuf::libprotobuf gRPC::grpc++)
复制代码
2.6 编译与运行


  • 安装依靠
    确保已安装Protobuf和gRPC库。可以参考以下步骤进行安装:
    1. # 安装gRPC及其依赖
    2. git clone -b v1.53.0 https://github.com/grpc/grpc
    3. cd grpc
    4. git submodule update --init
    5. mkdir -p build
    6. cd build
    7. cmake .. -DCMAKE_BUILD_TYPE=Release
    8. make -j4
    9. sudo make install
    10. sudo ldconfig
    复制代码
  • 编译项目
    在项目根目次下创建构建目次并编译:
    1. mkdir build
    2. cd build
    3. cmake ..
    4. make
    复制代码
  • 运行服务器
    在一个终端中启动服务器:
    1. ./server
    复制代码
    输出:
    1. Server listening on 0.0.0.0:50051
    复制代码
  • 运行客户端
    在另一个终端中运行客户端:
    1. ./client
    复制代码
    输出:
    1. User ID: 1
    2. Name: Alice
    3. Email: alice@example.com
    复制代码
2.7 关键组件剖析


以下是各组件在上述实现中的作用:

  • client(客户端)

    • 客户端代码位于 client.cpp,通过 UserServiceClient 类发起RPC请求。
    • serialize(序列化):Protobuf自动处置惩罚消息的序列化,将 GetUserRequest 转换为字节流。
    • deserialize(反序列化):接收 GetUserResponse 字节流并转换为 User 对象。

  • server(服务器)

    • 服务器代码位于 server.cpp,通过 UserServiceImpl 类实现服务逻辑。
    • serialize/deserialize:Protobuf自动处置惩罚接收的请求数据反序列化和相应数据序列化。

  • protocol stack(协议栈)

    • 在本示例中,gRPC基于HTTP/2协议栈处置惩罚网络通信细节,负责数据的传输和连担当理。

  • idl(接口界说语言)

    • user_service.proto 文件界说了服务接口和消息结构,作为IDL文件。

  • compiler(编译器)

    • protoc 编译器与gRPC插件一起,将.proto文件编译生成C++代码,生成消息类和服务骨架代码。

  • skeleton(骨架代码)

    • 生成的 user_service.grpc.pb.h 和 user_service.grpc.pb.cc 文件包含服务接口的骨架代码。
    • 服务器实现类 UserServiceImpl 继续自生成的骨架类,完成业务逻辑。
    • 客户端通过生成的 UserService::Stub 类进行长途调用,隐蔽了底层的通信细节。

2.8 序列化与反序列化的实现

在上述示例中,序列化与反序列化过程由gRPC和Protobuf库自动处置惩罚,开发者无需手动编写相关代码。然而,明白这一过程对于优化和调试至关紧张。


  • 序列化

    • 客户端在调用 stub_->GetUser(&context, request, &response) 时,Protobuf将 GetUserRequest 对象序列化为二进制格式,通过gRPC协议栈发送到服务器。

  • 反序列化

    • 服务器接收到字节流后,Protobuf将其反序列化为 GetUserRequest 对象,并通报给 GetUser 方法进行处置惩罚。

  • 相应过程

    • 服务器将 GetUserResponse 对象序列化后,通过gRPC协议栈发送回客户端,客户端再将其反序列化为 GetUserResponse 对象。

三、关键实现与剖析

通过上述代码示例,我们可以看到Protobuf和RPC的高效结合体如今以下几个方面:

  • 自动化生成代码:通过Protobuf界说的IDL文件(.proto),可以自动生成C++类,省去手动编写通信代码的复杂性。
  • 数据序列化与反序列化:Protobuf通过高效的二进制格式,淘汰了数据传输的开销,保证了性能和数据传输的一致性。
  • RPC框架的透明性开发者不必关心底层通信协议,gRPC框架负责数据的传输和连担当理,使得程序员可以大概像调用本地函数一样完发展途调用。
四、工程实践中的最佳实践


  • 代码生成自动化:集成Protobuf的代码生成步骤至构建系统,确保.proto文件变动后,自动生成与其匹配的代码。
  • 性能调优:对于大规模消息传输,可思量使用Protobuf的流式剖析功能,并优化网络带宽。
  • 版本兼容性:在计划Protobuf消息结构时,只管避免粉碎兼容性,确保后续扩展性。比方,避免删除字段或改变字段编号。
  • 错误处置惩罚机制:在客户端和服务器端实现完备的错误处置惩罚机制,确保网络异常和序列化失败时的回退策略。
五、总结

Protobuf与RPC框架的结合为当代分布式系统提供了一种高效的通信方案。在C++工程中,通过公道计划和使用Protobuf进行数据序列化,再借助gRPC完发展途过程调用,可以大幅提升通信服从,并简化系统的开发与维护。
附录:常用Protobuf与gRPC下令



  • 编译Protobuf文件
    1. protoc --cpp_out=. user_service.proto
    复制代码
  • 编译gRPC Protobuf文件
    1. protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto
    复制代码
  • 生成全部代码(包括消息和gRPC服务)
    1. protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user_service.proto
    复制代码
参考资料



  • Protocol Buffers 官方文档
  • gRPC 官方文档
  • Protobuf 编码原理详解
  • 0voice · GitHub

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

美食家大橙子

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表