2019-12-13 16:14:45
最近由于公司项目需求,需要实现基于google的protocol buffer实现rpc远程调用。本文主要讲解如何使用.proto文件生成.pb.cc和.pb.h代码,以及如何利用生成的代码用户实现自己的rpc调用。
1、编写proto文件
proto的具体语法可以看Google官方的文档,基本的语法不再解释。链接:https://developers.google.com/protocol-buffers/docs/cpptutorial
下面看看proto文件如何书写实现rpc服务。
syntax = "proto3"; /*声明使用protocol buffer的版本3.0以上*/ option cc_generic_services = true; /*生成C++代码的services*/ /*No parameters request*/ message NoParametersRequest { /*以request结尾的message都表示输入性参数,NoParameterRequest表示该参数没有变量(void类型)*/ }
/*CurrentStatus*/ message SwPackageStateTypeResponse{ /*以response结尾的message表示输出型参数或者函数的返回值*/ enum SwPackageStateType { kTransferring = 0; kTransferred = 1; kProcessing = 2; kProcessed = 3; } SwPackageStateType state_type = 1 ; /*定义枚举类型*/ }
/*下面实现一个数组vector功能的参数*/ /*GetSwClusterInfo*/ message SwClusterInfo { /*消息有三个字段name,version,state_type,每个字段后面的数字表示唯一表示在消息定义中,每个字段都有唯一的一个数字标识符*/ string name = 1; string version = 2; enum SwClusterStateType { kPresent = 0; kAdded = 1; kUpdated = 2; kRemoved = 3; } SwClusterStateType state_type = 3; } message GetSwClusterInfoResponse { repeated SwClusterInfo swcluster_info = 1; /*repeated 关键字表示变量是一个数组或者vector*/ }
/*GetHistory*/
message GetHistoryRequest {
uint64 timestamp_ge = 1;
uint64 timestamp_lt = 2;
}
message ActionType{
enum KActionType{
kUpdate = 0;
kInstall =1;
kRemove = 2;
}
}
message ResultType{
enum KResultType{
kSuccessfull = 0;
kFailed =1;
}
}
message GetHistoryVectorType{
uint64 time = 1;
string name = 2;
string version = 3;
ActionType action = 4;
ResultType resolution = 5;
}
message GetHistoryResponse{
repeated GetHistoryVectorType history = 1;
}
/*主要看看这个实现rpc服务*/
service PackageManagementService { /*服务名称*/
/*实现3个rpc调用,第一个rpc,CurrentStatus表示方法名称,NoParametersRequest输入性参数,SwPackageStateTypeResponse输出型参数或者函数返回值*/
rpc CurrentStatus(NoParametersRequest) returns(SwPackageStateTypeResponse);
rpc GetSwClusterInfo(NoParametersRequest) returns(GetSwClusterInfoResponse);
rpc GetHistory(GetHistoryRequest) returns(GetHistoryResponse);
}
2、分析proto文件生成.pb.cc和.pb.h的代码
代码生成器的安装及使用可以去Google官方查看。
通过这个命令: protoc package_management.proto --cpp_out=. 下面分析一下生成的代码:
1、SwPackageStateTypeResponse类的部分代码
1 /*通过枚举的值,获取枚举的名称*/ 2 static inline const std::string& SwPackageStateType_Name(T enum_t_value) { 3 static_assert(::std::is_same<T, SwPackageStateType>::value || 4 ::std::is_integral<T>::value, 5 "Incorrect type passed to function SwPackageStateType_Name."); 6 return SwPackageStateTypeResponse_SwPackageStateType_Name(enum_t_value); 7 }
/*通过枚举的名称,获取枚举的值*/ 8 static inline bool SwPackageStateType_Parse(const std::string& name, 9 SwPackageStateType* value) { 10 return SwPackageStateTypeResponse_SwPackageStateType_Parse(name, value); 11 } 12 13 // accessors ------------------------------------------------------- 14 15 enum : int { 16 kStateTypeFieldNumber = 1, 17 }; 18 // .SwPackageStateTypeResponse.SwPackageStateType state_type = 1;
/*清空state_type*/ 19 void clear_state_type();
/*获取state_type的值*/ 20 ::SwPackageStateTypeResponse_SwPackageStateType state_type() const;
/*设置state_type的值*/ 21 void set_state_type(::SwPackageStateTypeResponse_SwPackageStateType value); 22 private: 23 ::SwPackageStateTypeResponse_SwPackageStateType _internal_state_type() const; 24 void _internal_set_state_type(::SwPackageStateTypeResponse_SwPackageStateType value); 25 public:
2、GetSwClusterInfoResponse类对数组vector操作的代码部分代码
1 enum : int { 2 kSwclusterInfoFieldNumber = 1, 3 }; 4 // repeated .SwClusterInfo swcluster_info = 1; 5 int swcluster_info_size() const; 6 private: 7 int _internal_swcluster_info_size() const; 8 public: 9 void clear_swcluster_info();
/*获取vector的值*/ 10 ::SwClusterInfo* mutable_swcluster_info(int index); 11 ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::SwClusterInfo >* 12 mutable_swcluster_info(); 13 private: 14 const ::SwClusterInfo& _internal_swcluster_info(int index) const; 15 ::SwClusterInfo* _internal_add_swcluster_info(); 16 public: 17 const ::SwClusterInfo& swcluster_info(int index) const;
/*增加vector的值*/ 18 ::SwClusterInfo* add_swcluster_info(); 19 const ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::SwClusterInfo >& 20 swcluster_info() const; 21 22 // @@protoc_insertion_point(class_scope:GetSwClusterInfoResponse) 23 private: 24 class _Internal; 25 26 ::PROTOBUF_NAMESPACE_ID::internal::InternalMetadataWithArena _internal_metadata_; 27 ::PROTOBUF_NAMESPACE_ID::RepeatedPtrField< ::SwClusterInfo > swcluster_info_; 28 mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; 29 friend struct ::TableStruct_package_5fmanagement_2eproto; 30 };
3、利用生成的代码用户实现rpc服务
具体的protocol buffer内部rpc的机制这里不描述了,这里说明用户需要实现的代码。
1、service端,用户需要继承PackageManagementService,实现具体的rpc调用代码
1 class PackageManagementServiceImpl : public PackageManagementService { 2 3 public: 4 PackageManagementServiceImpl(){ 5 6 std::cout <<"PackageManagementServiceImpl structure "<<std::endl; 7 } 8 ~PackageManagementServiceImpl(){ 9 10 std::cout <<"PackageManagementServiceImpl desstructure "<<std::endl; 11 } 12 13 virtual void CurrentStatus(::google::protobuf::RpcController* controller, 14 const ::NoParametersRequest* request, 15 ::SwPackageStateTypeResponse* response, 16 ::google::protobuf::Closure* done); 17 18 virtual void GetSwClusterInfo(::google::protobuf::RpcController* controller, 19 const ::NoParametersRequest* request, 20 ::GetSwClusterInfoResponse* response, 21 ::google::protobuf::Closure* done); 22 23 };
1 void PackageManagementServiceImpl :: CurrentStatus(::google::protobuf::RpcController* controller, 2 const ::NoParametersRequest* request, 3 ::SwPackageStateTypeResponse* response, 4 ::google::protobuf::Closure* done){ 5 6 std::cout << "CurrentStatus" <<std::endl; 7 const std::string enum_name = "kTransferring"; 8 9 SwPackageStateTypeResponse_SwPackageStateType value; 10 response->SwPackageStateType_Parse(enum_name, &value); 11 /*做具体的数据赋值*/ 12 response->set_state_type(value); 13 } 14 15 void PackageManagementServiceImpl::GetSwClusterInfo(::google::protobuf::RpcController* controller, 16 const ::NoParametersRequest* request, 17 ::GetSwClusterInfoResponse* response, 18 ::google::protobuf::Closure* done){ 19 20 21 std::cout << "GetSwClusterInfo" <<std::endl; 22 23 SwClusterInfo *swcluster_info ; 24 /*向vector里添加数据*/ 25 swcluster_info = response->add_swcluster_info(); 26 27 swcluster_info->set_name("ICC"); 28 swcluster_info->set_version("1.1"); 29 30 swcluster_info = response->add_swcluster_info(); 31 swcluster_info->set_name("ICM"); 32 swcluster_info->set_version("1.2"); 33 34 35 swcluster_info= response->add_swcluster_info(); 36 37 swcluster_info->set_name("SOTA"); 38 swcluster_info->set_version("1.3"); 39 40 41 }
2、客户端代码
1 TEST(CurrentStatus, PackageManagement) { 2 3 RpcChannel channel; 4 PackageManagementService::Stub package_manage_client(&channel); 5 6 NoParametersRequest request; 7 SwPackageStateTypeResponse response; 8 9 package_manage_client.CurrentStatus(nullptr, &request, &response, nullptr); 10 /*解析rpc调用返回的数据*/ 11 SwPackageStateTypeResponse_SwPackageStateType value; 12 value = response.state_type(); 13 14 ASSERT_EQ(value, 0); 15 16 } 17 18 TEST(GetSwClusterInfo, PackageManagement) { 19 20 RpcChannel channel; 21 PackageManagementService::Stub package_manage_client(&channel); 22 23 NoParametersRequest request; 24 GetSwClusterInfoResponse response; 25 26 std::cout <<"GetSwClusterInfo RPC "<<std::endl; 27 package_manage_client.GetSwClusterInfo(nullptr, &request, &response, nullptr); 28 29 int vector_num = response.swcluster_info_size(); 30 ASSERT_EQ(vector_num, 3); 31 /*遍历和解析vector数据*/ 32 ::google::protobuf::RepeatedPtrField< ::SwClusterInfo > *swcluster_info_parse = response.mutable_swcluster_info(); 33 34 ::google::protobuf::RepeatedPtrField< ::SwClusterInfo >::iterator it = swcluster_info_parse->begin(); 35 36 std::string name; 37 std::string version; 38 39 name = swcluster_info_parse->at(0).name(); 40 ASSERT_STREQ(name.c_str(), "ICC"); 41 42 version = swcluster_info_parse->at(0).version(); 43 ASSERT_STREQ(version.c_str(), "1.1"); 44 45 46 name = swcluster_info_parse->at(1).name(); 47 ASSERT_STREQ(name.c_str(), "ICM"); 48 49 version = swcluster_info_parse->at(1).version(); 50 ASSERT_STREQ(version.c_str(), "1.2"); 51 52 name = swcluster_info_parse->at(2).name(); 53 ASSERT_STREQ(name.c_str(), "SOTA"); 54 55 version = swcluster_info_parse->at(2).version(); 56 ASSERT_STREQ(version.c_str(), "1.3"); 57 58 59 /*遍历数据*/ 60 for( ; it != swcluster_info_parse->end(); ++it ){ 61 62 std::cout << "name = " << it->name() <<std::endl; 63 std::cout << "version = " << it->version() <<std::endl; 64 65 } 66 }