C++下的gRPC与protobuf使用和介绍

目录

gRPC 简介

概述

使用协议缓冲区

协议缓冲区版本

概述

服务定义

使用接口

同步与异步

RPC 生命周期

一元 RPC

服务器流式处理 RPC

客户端流式处理 RPC

双向流式处理 RPC

截止时间/超时

RPC 终止

取消 RPC

警告

元数据

渠道

GRPC的为什么?

为什么我要使用 gRPC?

谁在使用这个,为什么?

支持哪些编程语言?

如何开始使用 gRPC?

gRPC 在哪个许可证下?

我该如何贡献?

文档在哪里?

路线图是什么?

gRPC 版本的支持期限是多久?

什么是 gRPC 版本控制策略?

最新的 gRPC 版本是什么?

gRPC 发布何时发生?

如何报告 gRPC 中的安全漏洞?

我可以在浏览器中使用它吗?

我可以将 gRPC 与我最喜欢的数据格式(JSON、Protobuf、Thrift、XML)一起使用吗?

是否可以在服务网格中使用 gRPC?

gRPC 如何帮助移动应用程序开发?

为什么 gRPC 比任何二进制 blob 都优于 HTTP/2?

为什么 gRPC 比 REST 更好/更差?

gRPC 动机和设计原则

赋予动机

原则和要求

服务不是对象,消息不是引用

覆盖范围和简单性

免费和开放

互操作性和覆盖范围

通用和高性能

分层的

与有效载荷无关

阻塞和非阻塞

取消和超时

跛行

流控制

可插拔

扩展作为 API

元数据交换

标准化状态码

简介

安装

HelloWorld

编译运行


gRPC 简介

gRPC 和协议缓冲区简介。

本页介绍 gRPC 和协议缓冲区。gRPC 可以使用 协议缓冲区作为其接口定义语言 (IDL) 和基础消息 交换格式。如果你不熟悉 gRPC 和/或协议缓冲区,请阅读此内容! 如果只想深入了解并首先查看 gRPC 的运行情况,请选择一种语言并尝试其快速入门

概述

在 gRPC 中,客户端应用程序可以直接调用服务器应用程序上的方法 在不同的计算机上,就好像它是本地对象一样,使您可以更轻松地 创建分布式应用程序和服务。与许多 RPC 系统一样,gRPC 是 基于定义服务的思想,指定可以 使用其参数和返回类型远程调用。在服务器端, 服务器实现此接口并运行 gRPC 服务器来处理客户端调用。 在客户端,客户端有一个存根(在某些中称为客户端 语言),提供与服务器相同的方法。

                

gRPC 客户端和服务器可以在各种 环境 - 从 Google 内部的服务器到您自己的桌面 - 并且可以 以任何 gRPC 支持的语言编写。因此,例如,您可以轻松地 在 Java 中创建 gRPC 服务器,并使用 Go、Python 或 Ruby 中的客户端。另外 最新的 Google API 将具有其接口的 gRPC 版本,让您 轻松将 Google 功能构建到您的应用程序中。

使用协议缓冲区

默认情况下,gRPC 使用协议缓冲区,谷歌的 用于序列化结构化数据的成熟开源机制(尽管它 可以与其他数据格式(如 JSON)一起使用)。以下是如何快速介绍 它有效。如果您已经熟悉协议缓冲区,请随时跳过 继续下一节。

使用协议缓冲区的第一步是定义结构 对于要在原型文件中序列化的数据:这是普通文本 带有扩展名的文件。协议缓冲区数据结构化为消息,其中每条消息都是一个小的信息逻辑记录 包含一系列称为字段的名称-值对。这是一个简单的 例:.proto

message Person {
  string name = 1;
  int32 id = 2;
  bool has_ponycopter = 3;
}

然后,一旦指定了数据结构,就可以使用协议缓冲区 编译器以首选语言生成数据访问类 () 从您的原型定义。这些为每个字段提供了简单的访问器, 喜欢和,以及序列化/解析的方法 原始字节的整个结构。因此,例如,如果您选择 语言是C++,在上面的例子上运行编译器将生成一个 类称为 。然后,可以在应用程序中使用此类来 填充、序列化和检索协议缓冲区消息。protocname()set_name()PersonPerson

定义 gRPC 服务 在普通原型文件中,RPC 方法参数和返回类型指定为 协议缓冲区消息:

// The greeter service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (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;
}

gRPC 与特殊的 gRPC 插件一起使用 从您的原型文件生成代码:您得到 生成的 gRPC 客户端和服务器代码,以及常规协议缓冲区 用于填充、序列化和检索消息类型的代码。若要了解有关协议缓冲区的详细信息,包括如何使用 所选语言的 gRPC 插件,请参阅protocprotoc协议缓冲区文档.

协议缓冲区版本

协议缓冲区已经可供开源用户使用一段时间, 本网站的大多数示例都使用协议缓冲区版本 3 (Proto3),它具有 稍微简化的语法,一些有用的新功能,并支持更多 语言。Proto3目前可用于Java,C++,Dart,Python, Objective-C,C#,一个精简运行时(Android Java),Ruby和JavaScript来自协议缓冲区 GitHub 存储库,以及来自戈朗/普罗托布夫官方套餐,还有更多语言正在开发中。您可以 在原型3语言指南参考 文档适用于每种语言。参考文档还 包括一个正式规范作为文件格式。.proto

通常,虽然您可以使用 proto2(当前默认协议缓冲区 版本),我们建议您将 proto3 与 gRPC 一起使用,因为它允许您使用 全系列的 gRPC 支持的语言,以及避免兼容性 Proto2 客户端与 Proto3 服务器通信的问题,反之亦然。

概述

服务定义

与许多 RPC 系统一样,gRPC 基于定义服务的思想, 指定可以使用其参数远程调用的方法,以及 返回类型。默认情况下,gRPC 使用协议 缓冲区作为接口 定义语言 (IDL),用于描述服务接口和 有效负载消息的结构。在以下情况下,可以使用其他替代方案 期望。

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

gRPC 允许您定义四种服务方法:

  • 一元 RPC,其中客户端向服务器发送单个请求并获得 单响应返回,就像正常的函数调用一样。
rpc SayHello(HelloRequest) returns (HelloResponse);

  • 服务器流式处理 RPC,其中客户端向服务器发送请求并获取 用于读回消息序列的流。客户端从 返回流,直到没有更多消息。gRPC 保证消息 在单个 RPC 调用中排序。
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

  • 客户端流式处理 RPC,其中客户端写入一系列消息并发送 它们到服务器,再次使用提供的流。一旦客户有 写完消息,它等待服务器读取它们并返回 它的回应。同样,gRPC 保证单个 RPC 中的消息排序 叫。
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

  • 双向流式处理 RPC,其中双方发送一系列消息 使用读写流。这两个流独立运行,因此客户端 服务器可以按照他们喜欢的任何顺序读取和写入:例如, 服务器可以等待接收所有客户端消息,然后再写入其 响应,或者它可以交替阅读消息然后编写消息,或者 读取和写入的其他一些组合。每个消息的顺序 流被保留。
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

您将在下面的 RPC 生命周期部分中了解有关不同类型的 RPC 的更多信息。

使用接口

从文件中的服务定义开始,gRPC 提供协议 生成客户端和服务器端代码的缓冲区编译器插件。gRPC 用户 通常在客户端调用这些 API 并实现相应的 API 在服务器端。.proto

  • 在服务器端,服务器实现服务声明的方法 并运行 gRPC 服务器来处理客户端调用。gRPC 基础结构解码 传入请求、执行服务方法并对服务响应进行编码。
  • 在客户端,客户端有一个称为存根的本地对象(对于某些 语言,首选术语是客户端),它实现与 服务。然后,客户端可以在本地对象上调用这些方法, 并且这些方法将调用的参数包装在适当的协议缓冲区中 消息类型,将请求发送到服务器,并返回服务器的 协议缓冲区响应。
同步与异步

在响应从服务器到达之前阻止的同步 RPC 调用是 最接近过程调用 RPC 的抽象 渴望。另一方面,网络本质上是异步的,并且在许多 能够在不阻塞当前的情况下启动 RPC 非常有用的方案 线。

大多数语言的 gRPC 编程 API 都有同步和 异步风格。您可以在每种语言的教程中找到更多信息,并且 参考文档(完整的参考文档即将推出)。

RPC 生命周期

在本部分中,你将详细了解 gRPC 客户端发生的情况 调用 gRPC 服务器方法。有关完整的实现详细信息,请参阅 特定于语言的页面。

一元 RPC

首先考虑客户端发送单个请求的最简单类型的 RPC 并得到一个回复。

  1. 一旦客户端调用存根方法,服务器 通知已使用此调用的客户端元数据、方法名称和指定的截止时间调用 RPC,如果 适用。
  2. 然后,服务器可以发回自己的初始元数据(必须 在任何响应之前发送)立即,或等待客户的请求 消息。首先发生的是特定于应用程序的。
  3. 一旦服务器收到客户端的请求消息,它就会做任何工作 需要创建和填充响应。然后返回响应 (如果成功)与状态详细信息(状态代码和 可选状态消息)和可选的尾随元数据。
  4. 如果响应状态为“正常”,则客户端将获得响应,即 在客户端完成调用。
服务器流式处理 RPC

服务器流式处理 RPC 类似于一元 RPC,不同之处在于服务器返回 响应客户端请求的消息流。发送完所有后 消息、服务器的状态详细信息(状态代码和可选状态消息) 并将可选的尾随元数据发送到客户端。这样就完成了处理 在服务器端。客户端在拥有服务器的所有消息后完成。

客户端流式处理 RPC

客户端流式处理 RPC 类似于一元 RPC,不同之处在于客户端发送 发送到服务器的消息流,而不是单个消息。服务器 使用单个消息响应(以及其状态详细信息和可选 尾随元数据),通常但不一定要在它收到所有 客户端的消息。

双向流式处理 RPC

在双向流式处理 RPC 中,调用由客户端发起 调用方法和接收客户端元数据的服务器,方法名称, 和截止日期。服务器可以选择发回其初始元数据或 等待客户端开始流式传输消息。

客户端和服务器端流处理是特定于应用程序的。由于两者 流是独立的,客户端和服务器可以读取和写入消息 任何订单。例如,服务器可以等到它收到所有 客户端的消息在写入其消息之前,或者服务器和客户端可以播放 “乒乓球” – 服务器收到请求,然后发回响应,然后 客户端根据响应发送另一个请求,依此类推。

截止时间/超时

gRPC 允许客户端指定他们愿意等待 RPC 的时间 在 RPC 因错误而终止之前完成。上 服务器端,服务器可以查询查看特定 RPC 是否已超时, 或完成 RPC 还剩多少时间。DEADLINE_EXCEEDED

指定截止时间或超时是特定于语言的:某些语言 API 可以工作 在超时(持续时间)方面,某些语言 API 在超时方面工作 的截止日期(固定时间点),可能有也可能没有默认截止日期。

RPC 终止

在 gRPC 中,客户端和服务器都对 电话的成功,他们的结论可能不匹配。这意味着, 例如,您可能有一个在服务器端成功完成的 RPC (“我已经发送了我所有的回复!但在客户端失败(“响应 在我的截止日期之后到达!服务器也可以决定 在客户端发送其所有请求之前完成。

取消 RPC

客户端或服务器可以随时取消 RPC。取消 立即终止 RPC,以便不再执行任何进一步的工作。

取消之前所做的更改不会回滚。

警告
元数据

元数据是有关特定 RPC 调用(如身份验证)的信息详细信息)以键值对列表的形式,其中 键是字符串,值通常是字符串,但可以是二进制数据。

键不区分大小写,由 ASCII 字母、数字和特殊字符 组成,并且不得以 (为 gRPC 本身保留)开头。 二进制值键以 结尾,而 ASCII 值键不以结尾。-_.grpc--bin

gRPC 不使用用户定义的元数据,这允许客户端提供信息 与对服务器的调用相关联,反之亦然。

对元数据的访问取决于语言。

渠道

gRPC 通道提供与指定主机上的 gRPC 服务器的连接,并且 港口。它在创建客户端存根时使用。客户端可以指定通道 用于修改 gRPC 默认行为(如切换消息)的参数 打开或关闭压缩。通道具有状态,包括和 。connectedidle

gRPC 如何处理关闭通道取决于语言。有些语言也 允许查询通道状态。

GRPC的为什么?

为什么我要使用 gRPC?

主要使用场景:

  • 低延迟、高度可扩展的分布式系统。
  • 开发与云服务器通信的移动客户端。
  • 设计一个需要准确、高效且独立于语言的新协议。
  • 分层设计以实现扩展,例如。身份验证、负载平衡、日志记录和监控等。

谁在使用这个,为什么?

gRPC 是一个云原生计算基础(CNCF)项目。

长期以来,Google 一直在 gRPC 中使用许多底层技术和概念。当前的实现正在谷歌的几个云产品和谷歌面向外部的API中使用。它也被广场,网飞,核心操作系统,码头工人,蟑螂数据库,思科,瞻博网络以及许多其他组织和个人。

支持哪些编程语言?

请参阅官方支持的语言和平台

如何开始使用 gRPC?

可以按照此处的说明从安装 gRPC 开始。或前往gRPC GitHub org page,选择您感兴趣的运行时或语言,然后按照自述文件说明进行操作。

gRPC 在哪个许可证下?

所有实现均根据以下权限进行许可阿帕奇 2.0.

我该如何贡献?

非常欢迎贡献者,存储库托管在 GitHub 上。我们期待社区的反馈、补充和错误。个人贡献者和企业贡献者都需要签署我们的 CLA。如果你对有关 gRPC 的项目有想法,请阅读指南并提交这里.我们在gRPC 生态系统GitHub 上的组织。

文档在哪里?

在 grpc.io 上查看文档

路线图是什么?

gRPC 项目有一个 RFC 流程,通过该流程设计并批准新功能以供实施。他们被跟踪在此存储库.

gRPC 版本的支持期限是多久?

gRPC 项目不执行 LTS 版本。鉴于上述滚动发布模型,我们支持当前、最新版本和在此之前的版本。这里的支持意味着错误修复和安全修复。

什么是 gRPC 版本控制策略?

请参阅 gRPC 版本控制策略这里.

最新的 gRPC 版本是什么?

最新版本标签是 v1.54.0。

gRPC 发布何时发生?

gRPC 项目在主分支的尖端始终稳定的模型中工作。该项目(跨各种运行时)的目标是尽最大努力每 6 周发布一次检查点版本。查看发布时间表这里.

如何报告 gRPC 中的安全漏洞?

若要报告 gRPC 中的安全漏洞,请按照记录的过程进行操作这里.

我可以在浏览器中使用它吗?

gRPC-Web项目已正式发布。

我可以将 gRPC 与我最喜欢的数据格式(JSON、Protobuf、Thrift、XML)一起使用吗?

是的。gRPC 设计为可扩展以支持多种内容类型。初始版本包含对Protobuf的支持,以及对其他内容类型(如FlatBuffers和Thrift)的外部支持,具有不同的成熟度。

是否可以在服务网格中使用 gRPC?

是的。gRPC 应用程序可以像任何其他应用程序一样部署在服务网格中。gRPC 还支持xDS 接口这允许在没有挎斗代理的服务网格中部署 gRPC 应用程序。列出了 gRPC 中支持的无代理服务网格功能这里.

gRPC 如何帮助移动应用程序开发?

gRPC 和 Protobuf 提供了一种简单的方法来精确定义服务,并为 iOS、Android 和提供后端的服务器自动生成可靠的客户端库。客户端可以利用高级流式处理和连接功能,这些功能有助于节省带宽,在更少的TCP连接上执行更多操作,并节省CPU使用率和电池寿命。

为什么 gRPC 比任何二进制 blob 都优于 HTTP/2?

这在很大程度上是 gRPC 在线内容。但是,gRPC 也是一组库,它们将跨平台一致地提供更高级别的功能,而普通 HTTP 库通常不会提供这些功能。此类功能的示例包括:

  • 与应用层的流控制交互
  • 级联呼叫取消
  • 负载平衡和故障转移

为什么 gRPC 比 REST 更好/更差?

gRPC 主要遵循 HTTP/2 上的 HTTP 语义,但我们明确允许全双工流式传输。我们偏离了典型的 REST 约定,因为我们在调用调度期间出于性能原因使用静态路径,因为从路径、查询参数和有效负载正文解析调用参数会增加延迟和复杂性。我们还正式化了一组错误,我们认为这些错误比 HTTP 状态代码更直接适用于 API 用例

gRPC 动机和设计原则

赋予动机

十多年来,Google 一直在使用一个名为 Stubby 的通用 RPC 基础设施来连接数据中心内和数据中心之间运行的大量微服务。我们的内部系统长期以来一直采用微服务架构,如今越来越受欢迎。拥有统一的跨平台 RPC 基础设施,可以在车队范围内推出效率、安全性、可靠性和行为分析方面的改进,这对于支持该时期的惊人增长至关重要。

Stubby 有许多很棒的功能 - 但是,它不基于任何标准,并且与我们的内部基础设施耦合得太紧密,不适合公开发布。随着SPDY,HTTP / 2和QUIC的出现,许多相同的功能已经出现在公共标准中,以及Stubby不提供的其他功能。很明显,是时候重新设计Stubby以利用这种标准化,并将其适用性扩展到移动,物联网和云用例。

原则和要求

服务不是对象,消息不是引用

推广系统之间粗粒度消息交换的微服务设计理念,同时避免分布式对象的陷阱忽视网络的谬误.

覆盖范围和简单性

该堆栈应该在每个流行的开发平台上可用,并且易于某人为其选择的平台构建。它应该在 CPU 和内存有限的设备上可行。

免费和开放

让所有基本功能免费供所有人使用。将所有项目作为开源工作发布,并具有许可,应促进而不是阻碍采用。

互操作性和覆盖范围

有线协议必须能够在通用互联网基础设施上幸存下来。

通用和高性能

该堆栈应适用于广泛的用例类别,同时与特定于用例的堆栈相比,牺牲的性能很少。

分层的

堆栈的关键方面必须能够独立发展。对连线格式的修订不应中断应用程序层绑定。

与有效载荷无关

不同的服务需要使用不同的消息类型和编码,如协议缓冲区、JSON、XML 和 Thrift;协议和实现必须允许这样做。同样,对有效载荷压缩的需求因用例和有效载荷类型而异:协议应允许可插拔压缩机制。

存储系统依靠流和流量控制来表达大型数据集。其他服务(如语音转文本或股票行情)依赖于流式处理来表示与时间相关的消息序列。

阻塞和非阻塞

支持异步和同步处理客户端和服务器交换的消息序列。这对于在某些平台上扩展和处理流至关重要。

取消和超时

操作可能成本高昂且持续时间长 - 取消允许服务器在客户端行为良好时回收资源。当跟踪工作的因果链时,取消可能会级联。客户端可以指示调用超时,这允许服务根据客户端的需求调整其行为。

跛行

必须允许服务器通过拒绝新请求同时继续处理正在进行的请求来正常关闭。

流控制

客户端和服务器之间的计算能力和网络容量通常不平衡。流量控制允许更好的缓冲区管理,并通过过度活跃的对等体提供 DOS 保护。

可插拔

线路协议只是正常运行的 API 基础结构的一部分。大型分布式系统需要安全性、运行状况检查、负载平衡和故障转移、监视、跟踪、日志记录等。实现应提供扩展点,以允许插入这些功能,并在有用的情况下提供默认实现。

扩展作为 API

在可能的情况下,需要在服务之间协作的扩展应优先使用 API 而不是协议扩展。此类型的扩展可能包括运行状况检查、服务侦测、负载监控和负载均衡分配。

元数据交换

常见的横切关注点(如身份验证或跟踪)依赖于不属于服务声明接口的数据交换。部署依赖于它们以不同的速率将这些功能发展到服务公开的各个 API 的能力。

标准化状态码

客户端通常以有限数量的方式响应 API 调用返回的错误。应约束状态代码命名空间,以使这些错误处理决策更清晰。如果需要更丰富的域特定状态,可以使用元数据交换机制来提供该状态。

简介

gRPC是一种由Google推出的RPC框架,开源,跨语言,跨平台,高性能。

gRPC主要是基于protobuf设计实现的。

本文主要介绍如何在C++中使用gRPC。

安装

不像Java,配置一个maven插件,添加相应的maven依赖,就可以搭建好gRPC环境。

C++一般需要下载gRPC的源码,然后编译构建,得到需要的库文件,protoc编译器,以及gRPC插件。

下载源码

git clone --recurse-submodules -b v1.41.0 https://github.com/grpc/grpc
cd grpc

创建cmake构建目录

mkdir -p cmake/build
cd cmake/build

  1. 生成makefile

cmake -DgRPC_INSTALL=ON \
      -DgRPC_BUILD_TESTS=OFF \
      -DCMAKE_INSTALL_PREFIX=$MY_INSTALL_DIR \
      ../..

MY_INSTALL_DIR变量指定了最终生成的库文件,protoc的安装位置,linux系统一般为/usr/local

构建

make -j

安装

make install

此命令会根据第三部指定的MY_INSTALL_DIR的,将构建出来的库安装到相应的位置。比如protoc就放在${MY_INSTALL_DIR}/bin目录下,

头文件就放在${MY_INSTALL_DIR}/include/grpc目录下。

当然,执行上述命令需要安装g++, cmake, git等工具。

HelloWorld

使用gRPC首先需要写proto文件,描述rpc,供客户端和服务端使用。

proto文件接口定义

hello.proto

// protobuf版本
syntax = "proto3";

// rpc请求的定义
message HelloRequest {
  optional string name = 1;
}

// rpc响应的定义
message HelloReply {
  optional string message = 1;
}

// rpc服务的定义,两个函数
service HelloWorld {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

服务端

proto仅定义了接口,还需要在服务端写程序,实现rpc。http://server.cc

#include "hello.grpc.pb.h"
#include <string>
#include <grpcpp/grpcpp.h>

// rpc服务实现
class HelloServiceImpl : public HelloWorld::Service
{
    grpc::Status SayHello(grpc::ServerContext* context, const HelloRequest* req, HelloReply* rsp)
    {
        std::cout << "Request SayHello From " << context->peer() << std::endl;
        rsp->set_message("hello " + req->name() + "!");
        return grpc::Status::OK;
    }

    grpc::Status SayHelloAgain(grpc::ServerContext* context, const HelloRequest* req, HelloReply* rsp)
    {
        std::cout << "Request SayHelloAgain From " << context->peer() << std::endl;
        rsp->set_message("hello " + req->name() + " again!!");
        return grpc::Status::OK;
    }

};

// 启动运行
int main(int argc, char** argv)
{
    std::string address("localhost:5000");
    HelloServiceImpl service;

    grpc::ServerBuilder builder;
    builder.AddListeningPort(address, grpc::InsecureServerCredentials());
    builder.RegisterService(&service);

    std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
    std::cout << "Server Listening on port: " << address << std::endl;

    server->Wait();

    return 0;
}

客户端

客户端调用服务端的,http://client.cc

#include "hello.grpc.pb.h"
#include <string>
#include <iostream>
#include <sstream>
#include <grpcpp/grpcpp.h>

// 客户端
class HelloClient
{
public:
    HelloClient(std::shared_ptr<grpc::Channel> channel) : _stub(HelloWorld::NewStub(channel))
    {}

    std::string SayHello(const std::string& name)
    {
        HelloRequest req;
        HelloReply rsp;
        req.set_name(name);
        grpc::ClientContext context;
        grpc::Status status = _stub->SayHello(&context, req, &rsp);
        if(status.ok())
        {
            return rsp.message();
        }
        else
        {
             std::ostringstream out;
             out << status.error_code() << " : " << status.error_message();
             return out.str();
        }
    }

    std::string SayHelloAgain(const std::string& name)
    {
        HelloRequest req;
        HelloReply rsp;
        req.set_name(name);
        grpc::ClientContext context;
        grpc::Status status = _stub->SayHelloAgain(&context, req, &rsp);
        if(status.ok())
        {
            return rsp.message();
        }
        else
        {
            std::ostringstream out;
            out << status.error_code() << " : " << status.error_message();
            return out.str();
        }
    }

private:
    std::unique_ptr<HelloWorld::Stub> _stub;
};

// 调用rpc
int main(int argc, char** argv)
{
    std::string address("0.0.0.0:5000");
    HelloClient client(grpc::CreateChannel(address, grpc::InsecureChannelCredentials()));
    std::string name("bazel");
    std::cout << client.SayHello(name) << std::endl;
    std::cout << client.SayHelloAgain(name) << std::endl;
    return 0;
}

客户端是通过gRPC提供的Stub类对象调用服务端的函数。上述代码对stub做了一层封装(HelloClient),使得代码结构更简洁清晰。

下面的代码,是未封装时,一般使用过程。

// 服务器ip和端口
string address("0.0.0.0:5000");
// 连接服务器, channel代表客户端与服务端的连接,是对socket等的一层封装
shared_ptr<grpc::Channel> channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials());
// 创建stub, 用于调用服务端的函数
std::unique_ptr<HelloWorld::Stub> stub = HelloWorld::NewStub(channel);

// 构造请求
// 调用服务端的函数
grpc::Status status = stub->SayHello(&context, req, &rsp);
// 处理响应

编译运行

proto编译

protoc --cpp_out=. --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` hello.proto

上述命令会生成hello.pb.h/cc和hello.grpc.pb.h/cc

服务端

g++ -std=c++11 \
    `pkg-config --cflags protobuf grpc` \
    `pkg-config --libs protobuf grpc++` \
    -framework CoreFoundation \ // 仅MacOS需要加
    -o server \
    hello.pb.cc hello.grpc.pb.cc server.cc

其中pkg-config --cflags protobuf grpc指定了头文件的位置,pkg-config --libs protobuf grpc++指定了库文件的位置,-framework CoreFoundation仅在MacOS中需要添加,这是由于grpc的abseil的time库在MacOS下用的是CoreFoundation中的

然后./server运行

客户端

g++ -std=c++11 \
    `pkg-config --cflags protobuf grpc` \
    `pkg-config --libs protobuf grpc++`  
    -framework CoreFoundation \ // 仅MacOS需要加
    -o client \ 
    hello.pb.cc hello.grpc.pb.cc client.cc

然后./client运行

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

踏马潜行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值