gRPC 基础编码使用手册

简介: gRPC 基础编码使用手册

前言:针对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;
}

服务端代码

服务端分两步:

  1. 初始化:
  • 创建链接
  • 挂起等待调用

 2.远程调用实现

  • 实现具体代码逻辑

也就是说:

  1. 如果你需要在服务端实现其他的初始化内容,请在挂起等待调用前。
  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_

客户端代码

客户端分三步是:


  1. 初始化
  2. 创建一个 gPRC 通道
  3. 调用接口


注: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生成的对应源码文件中搜索 消息的类型定义名称,会在各个属性的类定义前有一行注释表明当前类是针对哪一个属性的。

读取方式

常量

常量是指proto文件中定义的对象类型是 数字、字符、这些基本类型

读取时,对应的结构体类中提供了同名的函数,返回值是对象对应值。

注意:grpc会对变量名称做优化,在使用Linux命名法、驼峰(大小驼峰)命名法时可能会出现相关函数和变量名称有些许差异的情况,请注意识别。

优化变量名排查方法:在grpc生成的对应源码文件中搜索 消息的类型定义名称,会在各个属性的类定义前有一行注释表明当前类是针对哪一个属性的。


结构体

结构体是指proto文件中定义的对象类型是嵌套结构体。即Struct 中套一个 struct。

读取时,对应的消息类中提供了同名的函数,返回值是结构体对象指针。

数组

数组是指proto文件中定义的对象类型 前又加了一个repeated关键字 。常见于结构体数组。repeated int32 data = 1;

读取时,对应的消息类中提供了同名的函数,返回值是结构体对象的引用。

字典

字典是指proto文件中定义的对象类型 关键字为map。常见于复杂结构。

读取时,对应的消息类中提供了同名的函数,返回值是字典对象的引用。


写入方式

由于上述内容已经对相关类型做了介绍,下面将主要以代码的方式进行说明,不再冗余介绍


  1. 常量
    set_变量名() ;
  2. 结构体
    mutable_结构体变量名();
  3. 数组
    add_数组变量名();
  4. 字典
    auto *p = mutable_字典名();
    (*p) [键值] = value ;

编译常见问题

同名 变量冲突

  1. 结构体变量名和服务名
  2. 结构体变量名和命名空间名
  3. 服务名和命名空间名

多三方库同名冲突

  1. 避免connect,redis,sql,这些常见场景中的同名冲突,建议增加特征字符。


分享一个有趣的 学习链接:https://xxetb.xet.tech/s/HY8za


目录
相关文章
|
NoSQL Ubuntu 测试技术
GRPC C++开发环境搭建
GRPC C++开发环境搭建
1348 1
|
测试技术 程序员 C++
C++单元测试GoogleTest和GoogleMock十分钟快速上手(gtest&gmock)
gtest是Google开源的一个跨平台的(Liunx、Mac OS X、Windows等)的 C++ 单元测试框架,可以帮助程序员测试 C++ 程序的结果预期。它提供了丰富的断言、致命和非致命判断、参数化、”死亡测试”等等。另一方面,gmock并不是一个独立的测试框架,而是gtest的辅助框架,主要用于模拟没有实现的类的操作,以便在没有完整类的情况下进行测试。通过配合使用gtest和gmock,开发者可以编写出更为复杂且健壮的C++单元测试。
2637 0
|
JSON 人工智能 数据格式
AI计算机视觉笔记二十六:YOLOV8自训练关键点检测
本文档详细记录了使用YOLOv8训练关键点检测模型的过程。首先通过清华源安装YOLOv8,并验证安装。接着通过示例权重文件与测试图片`bus.jpg`演示预测流程。为准备训练数据,文档介绍了如何使用`labelme`标注工具进行关键点标注,并提供了一个Python脚本`labelme2yolo.py`将标注结果从JSON格式转换为YOLO所需的TXT格式。随后,通过Jupyter Notebook可视化标注结果确保准确性。最后,文档展示了如何组织数据集目录结构,并提供了训练与测试代码示例,包括配置文件`smoke.yaml`及训练脚本`train.py`,帮助读者完成自定义模型的训练与评估。
7156 3
|
10月前
|
缓存 JSON 安全
Flutter 网络请求深度解析
本文深入探讨Flutter网络请求架构,对比http、dio、retrofit等库,重点详解Dio封装、拦截器、统一响应、Repository模式,并结合Riverpod状态管理与JSON序列化,构建高性能、可维护的网络层。
798 3
|
JSON iOS开发 数据格式
最新研发flutter3.32+window_manager客户端OS管理系统
原创Flutter3.32+Dart3.8+Getx+Window_Manager实战桌面客户端os系统解决方案。支持macOS和windows两种主题风格、自定义桌面栅格布局。
819 50
|
XML 人工智能 Java
注入Java Bean的方式
本文总结了 Spring Boot 中常见的 Bean 注入方式,包括字段注入(`@Autowired`)、构造器注入(推荐)、Setter 方法注入、`@Resource` 按名称注入、`@Bean` 定义复杂 Bean、`@Value` 注入配置值、XML 配置、`ApplicationContextAware` 手动获取 Bean 以及 JSR-330 的 `@Inject`。同时分析了 Setter 注入逐渐被淡化的原因,强调构造器注入的不可变性和安全性优势,并推荐结合 Lombok 简化代码。文章还提供了按需选择注解的建议和最佳实践,帮助开发者根据场景选择合适的依赖注入方式。
1337 49
|
存储 人工智能 自然语言处理
AI 剧本生成与动画创作解决方案体验报告
AI 剧本生成与动画创作解决方案体验报告
830 40
|
网络协议 Linux C++
《C++ 网络编程:高效实现 TCP/IP 与 UDP 通信》
在数字化时代,网络编程对C++开发者至关重要。本文深入探讨了C++中TCP/IP和UDP通信的高效实现,涵盖网络编程基础、TCP连接建立、I/O模型选择、数据传输优化、连接维护与关闭、UDP特点利用及数据报优化等内容,旨在帮助开发者构建高性能的网络应用程序。
1105 21
|
网络协议 Linux 网络性能优化
Linux C/C++之TCP / UDP通信
这篇文章详细介绍了Linux下C/C++语言实现TCP和UDP通信的方法,包括网络基础、通信模型、编程示例以及TCP和UDP的优缺点比较。
1060 0
Linux C/C++之TCP / UDP通信
|
缓存 Java 应用服务中间件
常见的限流降级方案
【1月更文挑战第21天】

热门文章

最新文章