学习gRPC (三)

测试gRPC例子

  • 编写proto文件
  • 实现服务端代码
  • 实现客户端代码

通过gRPC 已经编译并且安装好之后,就可以在源码目录下找到example 文件夹下来试用gRPC 提供的例子。

在这里我使用VS2022来打开仓库目录下example/cpp/helloworld目录
在这里插入图片描述

编写proto文件

下面是我改写的example/protos/helloworld.proto ,和相应的greeter_client.ccgreeter_server.cc两个文件。这里面定义了一个叫做Greeter的服务,同时这里尝试4种类型的方法。

  1. 简单 RPC ,客户端使用存根发送请求到服务器并等待响应返回,就像平常的函数调用一样。
  2. 服务器端流式 RPC , 客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。从例子中可以看出,通过在响应类型前插入 stream 关键字,可以指定一个服务器端的流方法。
  3. 客户端流式 RPC ,客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。通过在请求类型前指定 stream 关键字来指定一个客户端的流方法。
  4. 双向流式 RPC,双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:比如,服务器可以在写入响应前等待接收所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。 每个流中的消息顺序被预留。你可以通过在请求响应前加 stream 关键字去制定方法的类型。
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.examples.helloworld";
option java_outer_classname = "HelloWorldProto";
option objc_class_prefix = "HLW";

package helloworld;

// The greeting service definition.
service Greeter {
  // A simple RPC 
  rpc SayHello (HelloRequest) returns (HelloReply) {}

  // A server-side streaming RPC
  rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {}

  // A client-side streaming RPC 
  rpc StreamHelloReply (stream HelloRequest) returns (HelloReply) {}

  // A bidirectional streaming RPC
  rpc StreamHelloStreamReply (stream HelloRequest) returns (stream HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

当编辑完helloworld.proto文件之后,可以直接点击vs2022->生成->全部重新生成。此时VS会调用protocol buffer 编译器和gRPC C++ plugin生成4个客户端和服务端的文件,所有生成的文件在${projectDir}\out\build\${name}文件夹下。

如下方是我编写的手动生成这4个文件的批处理脚本

@echo off

set fileName=temp
set currentPath=%~dp0%
set target=--cpp_out
set protocPath= "Your protoc path"
set pluginPath= "Your c++ plugin path"

for %%I in (%*) do (
    for /f "tokens=1,2 delims=-/:" %%J in ("%%I%") do (
        if %%J==f (set fileName=%%K)
        if %%J==t (set target=%%K)
    )
)

if not exist "%currentPath%%fileName%" (
    echo file not exist
    goto:error
)
if %target%==python (
  set pluginPath= "Your python plugin path"
  set target=--python_out
)
if %target%==csharp (
  set pluginPath= "Your C# plugin path"
    set target=--csharp_out
)

%protocPath% --grpc_out=%currentPath% --plugin=protoc-gen-grpc=%pluginPath% --proto_path=%currentPath% %fileName%
%protocPath% %target%=%currentPath% --proto_path=%currentPath% %fileName% 
:error
pause
exit

真正起作用编译出4个文件的代码是

%protocPath% --grpc_out=%currentPath% --plugin=protoc-gen-grpc=%pluginPath% --proto_path=%currentPath% %fileName%
%protocPath% %target%=%currentPath% --proto_path=%currentPath% %fileName% 

至此${projectDir}\out\build\${name}文件夹下会生成如下4个文件

  • helloworld.pb.h 消息类的头文件
  • helloworld.pb.cc 消息类的实现
  • helloworld.grpc.pb.h 服务类的头文件
  • helloworld.grpc.pb.cc 服务类的实现

同时里面还包含所有的填充、序列化和获取请求和响应消息类型的 protocol buffer 代码,和一个名为Greeter的类,这个类中包含了:

  • 方便客户端调用的存根
  • 需要服务端实现的虚接口

实现服务端代码

以下是greeter_server.cc

#include <iostream>
#include <memory>
#include <string>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "absl/strings/str_format.h"

#include <grpcpp/ext/proto_server_reflection_plugin.h>
#include <grpcpp/grpcpp.h>
#include <grpcpp/health_check_service_interface.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using grpc::ServerWriter;
using grpc::ServerReader;
using grpc::ServerReaderWriter;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;

using std::string;

ABSL_FLAG(uint16_t, port, 50051, "Server port for the service");

// Logic and data behind the server's behavior.
class GreeterServiceImpl final : public Greeter::Service
{
  Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    string strName = request->name();
    reply->set_message("Hello : " + strName);
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    return Status::OK;
  }

  Status SayHelloStreamReply(ServerContext* context, const HelloRequest* request, ServerWriter<HelloReply>* writer)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    string strName = request->name();
    HelloReply reply;
    reply.set_message("Hello " + strName);
    writer->Write(reply);
    for (unsigned int i = 0; i < 10; i++)
    {
      reply.clear_message();
      reply.set_message("This is Server Reply : " + std::to_string(i));
      writer->Write(reply);
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    return Status::OK;
  }

  Status StreamHelloReply(ServerContext* context, ServerReader<HelloRequest>* reader, HelloReply* response)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    HelloRequest req;
    while (reader->Read(&req))
    {
      std::cout << "Server got what you said " << req.name() << std::endl;
    }
    response->set_message("Server got what you said " + req.name());
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    return Status::OK;
  }

  Status StreamHelloStreamReply(ServerContext* context, ServerReaderWriter< HelloReply, HelloRequest>* stream)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    HelloRequest req;
    HelloReply reply;
    unsigned long uiCount = 0ul;
    while (stream->Read(&req))
    {
      std::cout << "Server got what you said " << req.name() << std::endl;
      reply.clear_message();
      reply.set_message("This is Server count " + std::to_string(uiCount++));
      stream->Write(reply);
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    return Status::OK;
  }
};

void RunServer(uint16_t port) {
  std::string server_address = absl::StrFormat("0.0.0.0:%d", port);
  GreeterServiceImpl service;

  grpc::EnableDefaultHealthCheckService(true);
  grpc::reflection::InitProtoReflectionServerBuilderPlugin();
  ServerBuilder builder;
  // Listen on the given address without any authentication mechanism.
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  // Register "service" as the instance through which we'll communicate with
  // clients. In this case it corresponds to an *synchronous* service.
  builder.RegisterService(&service);
  // Finally assemble the server.
  std::unique_ptr<Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;

  // Wait for the server to shutdown. Note that some other thread must be
  // responsible for shutting down the server for this call to ever return.
  server->Wait();
}

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);
  RunServer(absl::GetFlag(FLAGS_port));
  return 0;
}

可以看到,里面有两部分代码。一部分是真正实现服务接口内在逻辑的代码,逻辑都在GreeterServiceImpl 这个类中。另一部分是运行一个服务,并使它监听固定端口的代码,逻辑都在RunServer这个函数中。

实现客户端代码

以下是greeter_client.cc

#include <iostream>
#include <memory>
#include <string>
#include <chrono>
#include <thread>

#include "absl/flags/flag.h"
#include "absl/flags/parse.h"

#include <grpcpp/grpcpp.h>

#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif

ABSL_FLAG(std::string, target, "localhost:50051", "Server address");

using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using helloworld::Greeter;
using helloworld::HelloReply;
using helloworld::HelloRequest;
using std::thread;

class GreeterClient {
 public:
  GreeterClient(std::shared_ptr<Channel> channel)
      : stub_(Greeter::NewStub(channel)) {}

  // Assembles the client's payload, sends it and presents the response back
  // from the server.
  void SayHello(const std::string& user)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    // Data we are sending to the server.
    HelloRequest request;
    request.set_name(user);

    // Container for the data we expect from the server.
    HelloReply reply;

    // Context for the client. It could be used to convey extra information to
    // the server and/or tweak certain RPC behaviors.
    ClientContext context;

    // The actual RPC.
    Status status = stub_->SayHello(&context, request, &reply);
    // Act upon its status.
    if (status.ok()) {
      std::cout << "Server said " << reply.message() << std::endl;
    } else {
      status.error_message();
      std::cout << status.error_code() << ": " << status.error_message()
                << std::endl;
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
  }

  void SayHelloStreamReply(const std::string& user)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    // Data we are sending to the server.
    HelloRequest request;
    request.set_name(user);

    // Context for the client. It could be used to convey extra information to
    // the server and/or tweak certain RPC behaviors.
    ClientContext context;

    HelloReply reply;

    // The actual RPC.
    auto Reader = stub_->SayHelloStreamReply(&context, request);
    while (Reader->Read(&reply))
    {
      std::cout << reply.message() << std::endl;
    }

    if (!Reader->Finish().ok())
    {
      std::cout << Reader->Finish().error_code() << ": " << Reader->Finish().error_message()
        << std::endl;
      std::cout <<  "RPC failed" << std::endl;
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
  }

  void StreamHelloReply(const std::string& user)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    HelloReply reply;
    ClientContext context;
    auto Writer = stub_->StreamHelloReply(&context, &reply);

    for (unsigned int i = 0; i < 10; i++)
    {
      // Data we are sending to the server.
      HelloRequest request;
      std::string strName = user + std::to_string(i);
      request.set_name(strName);
      Writer->Write(request);
    }
    Writer->WritesDone();
    if (!Writer->Finish().ok())
    {
      std::cout << Writer->Finish().error_code() << ": " << Writer->Finish().error_message()
        << std::endl;
      std::cout << "RPC failed" << std::endl;
    }
    else
    {
      std::cout << reply.message() << std::endl;
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
  }

  void StreamHelloStreamReply(const std::string& user)
  {
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
    HelloReply reply;
    ClientContext context;
    std::shared_ptr< ::grpc::ClientReaderWriter< ::helloworld::HelloRequest, ::helloworld::HelloReply>> Stream = stub_->StreamHelloStreamReply(&context);

    thread t1([Stream, user]() {
      for (unsigned int i = 0; i < 20; i++)
      {
         // Data we are sending to the server.
        HelloRequest request;
        std::string strName = user + std::to_string(i);
        request.set_name(strName);
        Stream->Write(request);
      }
      Stream->WritesDone();
    });

    while (Stream->Read(&reply))
    {
      std::cout << reply.message() << std::endl;
    }
    t1.join();

    if (!Stream->Finish().ok())
    {
      std::cout << Stream->Finish().error_code() << ": " << Stream->Finish().error_message()
        << std::endl;
      std::cout << "RPC failed" << std::endl;
    }
    std::cout << "--------" << __FUNCTION__ << "--------" << std::endl;
  }

 private:
  std::unique_ptr<Greeter::Stub> stub_;
};

int main(int argc, char** argv) {
  absl::ParseCommandLine(argc, argv);
  // Instantiate the client. It requires a channel, out of which the actual RPCs
  // are created. This channel models a connection to an endpoint specified by
  // the argument "--target=" which is the only expected argument.
  std::string target_str = absl::GetFlag(FLAGS_target);
  // We indicate that the channel isn't authenticated (use of
  // InsecureChannelCredentials()).
  GreeterClient greeter(grpc::CreateChannel(target_str, grpc::InsecureChannelCredentials()));
  std::string strName = "User";
  greeter.SayHello(strName);
  Sleep(500);
  greeter.SayHelloStreamReply(strName);
  Sleep(500);
  greeter.StreamHelloReply(strName);
  Sleep(500);
  greeter.StreamHelloStreamReply(strName);
  return 0;
}

客户端代码想要调用服务必须要使用到存根,所以我们可以在代码里看到std::unique_ptr<Greeter::Stub> stub_。这样我们在客户端调用时,就像是调用一个本地方法一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/59085.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【前端实习生备战秋招】—HTML 和 CSS面试题总结(二)

【前端实习生备战秋招】—HTML 和 CSS面试题总结&#xff08;二&#xff09; 1.有哪些方式可以对一个 DOM 设置它的 CSS 样式&#xff1f; 外部样式表&#xff0c;引入一个外部 css 文件内部样式表&#xff0c;将 css 代码放在 <head> 标签内部内联样式&#xff0c;将 c…

PROFINET转TCP/IP网关profinet网线接头接法

大家好&#xff0c;今天要和大家分享一款自主研发的通讯网关&#xff0c;捷米JM-PN-TCPIP。这款网关可是集多种功能于一身&#xff0c;PROFINET从站功能&#xff0c;让它在通讯领域独领风骚。想知道这款网关如何实现PROFINET和TCP/IP网络的连接吗&#xff1f;一起来看看吧&…

【移动机器人运动规划】02 —— 基于采样的规划算法

文章目录 前言相关代码整理:相关文章&#xff1a; 基本概念概率路线图&#xff08;Probabilistic Road Map&#xff09;基本流程预处理阶段查询阶段 优缺点&#xff08;pros&cons&#xff09;一些改进算法Lazy collision-checking Rapidly-exploring Random Tree算法伪代码…

构建稳健的PostgreSQL数据库:备份、恢复与灾难恢复策略

在当今数字化时代&#xff0c;数据成为企业最宝贵的资产之一。而数据库是存储、管理和保护这些数据的核心。PostgreSQL&#xff0c;作为一个强大的开源关系型数据库管理系统&#xff0c;被广泛用于各种企业和应用场景。然而&#xff0c;即使使用了最强大的数据库系统&#xff0…

Unity Shader:常用的C#与shader交互的方法

俗话说久病成医&#xff0c;虽然不是专业技术美术&#xff0c;但代码写久了自然会积累一些常用的shader交互方法。零零散散的&#xff0c;总结如下&#xff1a; 1&#xff0c;改变UGUI的材质球属性 有时候我们需要改变ui的一些属性&#xff0c;从而实现想要的效果。通常UGUI上…

Kafka的配置和使用

目录 1.服务器用docker安装kafka 2.springboot集成kafka实现生产者和消费者 1.服务器用docker安装kafka ①、安装docker&#xff08;docker类似于linux的软件商店&#xff0c;下载所有应用都能从docker去下载&#xff09; a、自动安装 curl -fsSL https://get.docker.com | b…

有哪些好用的AI绘画网站?

随着人工智能技术的发展&#xff0c;人工智能绘画工具逐渐成为数字艺术领域的热门话题。人工智能绘画工具是利用深度学习和其他技术来模拟绘画过程和效果的工具&#xff0c;可以帮助用户快速创作高质量的艺术作品。除了Midjourney、除了openai等流行的AI绘画工具外&#xff0c;…

使用WiFi测量仪进行机器人定位的粒子过滤器研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

K3s vs K8s:轻量级对决 - 探索替代方案

在当今云原生应用的领域中&#xff0c;Kubernetes&#xff08;简称K8s&#xff09;已经成为了无可争议的领导者。然而&#xff0c;随着应用规模的不断增长&#xff0c;一些开发者和运维人员开始感受到了K8s的重量级特性所带来的挑战。为了解决这一问题&#xff0c;一个名为K3s的…

python 常见数据类型和方法

不可变数据类型 不支持直接增删改 只能查 str 字符串 int 整型 bool 布尔值 None None型特殊常量 tuple 元组(,,,)回到顶部 可变数据类型&#xff0c;支持增删改查 list 列表[,,,] dic 字典{"":"","": ,} set 集合("",""…

「Qt」常用事件介绍

&#x1f514; 在开始本文的学习之前&#xff0c;笔者希望读者已经阅读过《「Qt」事件概念》这篇文章了。本文会在上篇文章的基础上&#xff0c;进一步介绍 Qt 中一些比较常用的事件。 0、引言 当我们想要让控件收到某个事件时做一些操作&#xff0c;通常都需要重写相应的事件处…

Python---Numpy

文章目录 1.Numpy是什么&#xff1f;2.ndarray2.1 什么是ndarray?2.2 ndarray的属性2.3 ndarray的类型 3.Numpy基本操作3.1 生成0或1的数组3.2 从现有数组生成数组拓展&#xff1a;浅拷贝和深拷贝 3.3 生成固定范围的数组3.4 生成随机数组3.4.1 正态分布3.4.2 均匀分布 3.5 形…

5款无广告的超实用软件,建议收藏!

​ 大家好,我又来了,今天向大家推荐几款软件,它们有个共同的特点,就是无广告、超级实用,大家看完之后,可以自己去搜索下载试用。 1.重复文件清理——Duplicate Cleaner ​ Duplicate Cleaner是一款用于找出硬盘中重复文件并删除的工具。它可以通过内容或文件名查找重复文档、…

多语言gRPC开发入门与避坑指南

目录 gRPC相关介绍 什么是gPRC gPRC的优点 gPRC的缺点 gPRC定位 协议缓冲区&#xff08;Protocol Buffers&#xff09; 四种调用方式 gRPC开发三大步骤 第一步&#xff1a;定义和编写proto服务文件 第二步&#xff1a;proto文件转化为gRPC代码 第三步&#xff1a;调…

IDEA中maven项目失效,pom.xml文件橙色/橘色

IDEA中maven项目失效&#xff0c;pom.xml文件橙色/橘色 IDEA中Maven项目失效 IDEA中创建的maven项目中的文件夹都变成普通格式&#xff0c;pom.xml变成橙色 右键点击橙色的pom.xml文件&#xff0c;选择add as maven project maven项目开始重新导入相应依赖&#xff0c;恢复…

QT图形视图系统 - 使用一个项目来学习QT的图形视图框架 - 终篇

QT图形视图系统 - 终篇 接上一篇&#xff0c;我们需要继续完成以下的效果&#xff1b; 先上个效果图&#xff1a; 修改背景&#xff0c;使之整体适配 上一篇我们绘制了标尺&#xff0c;并且我们修改了放大缩小和对应的背景&#xff0c;整体看来&#xff0c;我们的滚动条会和…

单篇笔记曝光248万+,素颜、寸头…小红书女性种草新趋势分析!

最近&#xff0c;小红书上刮起一阵素颜、寸头&#xff0c;拒绝美丽绑架的风潮&#xff0c;他们称之为“脱美役”&#xff0c;即脱离美丽枷锁&#xff0c;做自己&#xff0c;接纳原本的自己。这是女性觉醒的又一阵风&#xff0c;品牌要如何跟上这波种草新趋势呢&#xff1f; 单篇…

村田授权代理:共模扼流线圈针对汽车专用设备高频噪声的降噪对策

车载市场正不断扩充ADAS、自动驾驶、V2X、车载信息系统等的应用。由于此类应用要处理庞大的信息&#xff0c;因此为了执行处理&#xff0c;内部处理信号的处理速度亦不断高速化。另一方面&#xff0c;由于部件数量增多&#xff0c;安装密度增大&#xff0c;因此要求部件小型化。…

JVM之垃圾回收器

1.如何判断对象可以回收 1.1 引用计数法 什么是引用计数器法 在对象中添加一个引用计数器&#xff0c;每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是不可能再被使用的。 …

芯旺微冲刺IPO,车规级MCU竞争白热化下的“隐忧”凸显

在汽车智能化和电动化发展带来的巨大蓝海市场下&#xff0c;产业链企业迎来了一波IPO小高潮。 日前&#xff0c;上海芯旺微电子技术股份有限公司&#xff08;以下简称“芯旺微”&#xff09;在科创板的上市申请已经被上交所受理&#xff0c;拟募资17亿元&#xff0c;用于投建车…