赞
踩
前言:针对grpc 目前在C++ 开发过程中存在初学难懂,使用没有成体系说明的问题,这里对c++下的grpc 如何在代码层面使用进行简单说明。希望能够帮到更多人。
本文后续使用代码均基于 下述的 proto文件,文件名为TestProto.proto
包含常见的使用示例
syntax = "proto3"; package TestProto; // 这个服务类是请求响应的模式即单向流 service SvrPage{ // 数据传输监控 rpc GetTransferState(GetTransferStateRequest) returns (GetTransferStateResponse) {} } // 数据传输监控请求消息 message GetTransferStateRequest { string dataType = 1; // 数据类型 string time = 2; // 数据日期(YYYY-MM-DD) } // 数据传输监控响应消息 message GetTransferStateResponse { int32 code = 1; // 响应码 string msg = 2; // 响应消息 // 结构体数组 repeated GetTransferStateDataInfo data = 3; // 数据列表 } // 数据信息 message GetTransferStateDataInfo { // 基本类型数组 repeated int32 data = 1; // 数据记录 RecordMap mapData = 2; // 字典数据 } //字段值 message FieldValueQc{ string code = 1; //编码 float value = 2; //数据值 int32 qcmark = 3; //质控码 bool lack = 4;//是否缺测 } message RecordMap{ // 字典数据 map<string, FieldValueQc> line = 1; //key为code } // 监控服务 (这个服务是双向流,即异步发送请求应答) service MonitorCenter { rpc MonitorUpgradeState(stream UpgradeStateRequest) returns (stream UpgradeStateResponse); } message UpgradeStateRequest {} message UpgradeStateResponse { int32 code = 1; string msg = 2; UpgradeData data = 3; } message UpgradeData { string state = 1; repeated LogEntry lastedLog = 2; } message LogEntry { string content = 1; string time = 2; }
服务端分两步:
也就是说:
.... // 这里只对下面初始化需要使用的部分作说明,完整说明在远程调用实现部分 class CSvrPageImpl final : public SvrPage::Service { ..... }; // ============================================= // = 本端作为服务端 调用的服务区间 定义开始 // ============================================= /** * @brief gRPC 作为服务端运行时的主体入口 * */ void RunServer() { // 这里实现指定绑定接口部分,接受string类型的对象传入参数 std::string server_address("0.0.0.0:50051"); // 这里初始化的类对象实例是在头文件中继承了`proto`文件中对应服务server 名称的类 CSvrPageImpl service; // 固定 svr 创建方法 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 () { RunServer(); return 0; }
这里着重讲述如何实现具体的远程调用逻辑不涉及如何将程序运行起来,运行部分请参考上面的初始化部分。
#ifndef _OP_TEST_HPP_ #define _OP_TEST_HPP_ // 这里引用的头文件是在安装完成grpc服务后自动安装好的 #include <grpcpp/grpcpp.h> // 这里的头文件是通过protobuffer程序生成的proto文件对应源码库 #include "../../include/proto/TestProto.grpc.pb.h" #include "../../include/proto/TestProto.pb.h" // 这里是服务端对应的必须内容 using grpc::Server; using grpc::Status; using grpc::ServerBuilder; using grpc::ServerContext; // 这里是根据proto文件定义的自己的message服务类 // 规则是:文件名:message using TestProto::GetTransferStateRequest; using TestProto::GetTransferStateResponse; using TestProto::SvrPage; using TestProto::GetTransferStateDataInfo; using TestProto::RecordMap; // 服务端类继承重载方法 class CSvrPageImpl final : public SvrPage::Service { // 接口定义,可以将定义声明分离,不必一定写成内嵌函数 // 返回值是grpc提供的自定义的状态类型,支持多种返回内容 grpc::Status GetTransferState(ServerContext *context, const GetTransferStateRequest *request, GetTransferStateResponse *response) override { // 实现获取传入的逻辑 std::string strDatatype = request->datatype(); std::string strtime = request->time(); std::cout << __LINE__ << __TIME__ << std::endl; std::cout << "strDatatype: " << strDatatype << std::endl; std::cout << "strtime: " << strtime << std::endl; // 拼接返回值 response->set_code(200); response->set_msg("success"); GetTransferStateDataInfo *sData = response->add_data(); sData->add_data(100); // 单项插入 数组尾 sData->add_data(101); // 单项插入 数组尾 sData->set_data(3,102); // 指定修改 数组 第三位的数据 为102 RecordMap *sRecordMap = sData->mutable_mapdata(); // 返回结构体对象指针,如果没有则创建 return grpc::Status::OK; } }; #endif // _OP_TEST_HPP_
客户端分三步是:
gPRC
通道注:grpc支持返回原生的grpc状态码,作为远程调用的执行结果。 这是和grpc接口的返回值不同的概念。以此可以得到接口服务的调用结果和返回内容两项,从而实现更加宽泛的业务需求。
注意:为了保证最小依赖,相关的模版容器和字符串以及线程和其他引用的声明并未列出
#ifndef _OP_TEST_HPP_ #define _OP_TEST_HPP_ // 这里引用的头文件是在安装完成grpc服务后自动安装好的 #include <grpcpp/grpcpp.h> // 这里的头文件是通过protobuffer程序生成的proto文件对应源码库 #include "../../include/proto/TestProto.grpc.pb.h" #include "../../include/proto/TestProto.pb.h" using grpc::Channel; using grpc::ClientContext; using grpc::Status; class CClientPageImpl { public: CClientPageImpl(std::shared_ptr<Channel> channel) : stub_(TestProto::NewStub(channel)) {} // 调用远程函数 Status GetTransferState(const GetTransferStateRequest &request, GetTransferStateResponse &response); private: std::unique_ptr<DeviceService::Stub> stub_; // 通道 } #endif // _OP_TEST_HPP_
接口接收一个string类型对象作为绑定地址,另外这里主要对创建方式进行介绍,相关的前置依赖和类对象类型请参见初始化。
Status CClientPageImpl::GetTransferState(const std::string &strDatatype, const std::string &strtime)
int main()
{
// 创建一个gRPC通道
std::shared_ptr<Channel> channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
CClientPageImpl client(channel);
....
return 0;
}
std::string CClientPageImpl::GetTransferState(const GetTransferStateRequest &request, GetTransferStateResponse &response) { ClientContext context; Status status = stub_->SendRTRecordData(&context, request, &response); // 对它的状态进行操作。 if (status.ok()) { return std::to_string(response.data_size()); } else { std::cout << __FILE__ << __LINE__ << std::endl; std::cout << status.error_code() << ": " << status.error_message() << std::endl; return "RPC 失败"; } ] int main() { // 创建一个gRPC通道 std::shared_ptr<Channel> channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); CClientPageImpl client(channel); ClientContext context; GetTransferStateRequest request; // 初始化传入接口 GetTransferStateResponse response; std::string strRet = context.GetTransferState(request,response); return 0; }
注意:grpc会对变量名称做优化,在使用Linux命名法、驼峰(大小驼峰)命名法时可能会出现相关函数和变量名称有些许差异的情况,请注意识别。
优化变量名排查方法:在grpc生成的对应源码文件中搜索 消息的类型定义名称,会在各个属性的类定义前有一行注释表明当前类是针对哪一个属性的。
repeated
关键字 。常见于结构体数组。repeated int32 data = 1;
map
。常见于复杂结构。由于上述内容已经对相关类型做了介绍,下面将主要以代码的方式进行说明,不再冗余介绍
set_变量名() ;
mutable_结构体变量名();
add_数组变量名();
auto *p = mutable_字典名();
(*p) [键值] = value ;
分享一个有趣的 学习链接:https://xxetb.xet.tech/s/HY8za
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。