grpc部分

系列文章目录

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
例如:第一章 Python 机器学习入门之pandas的使用


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档


grpc简介

gRPC 的核心作用确实是使客户端和服务器端之间进行高效的网络通信。它是一个由 Google 开发的开源远程过程调用 (RPC) 框架,旨在使分布式系统的开发变得更加简单和高效。
特点

  1. 跨语言支持:gRPC 支持多种编程语言,包括 C++, Java, Python, Go, Ruby 等。开发者可以使用不同的语言来编写客户端和服务器,而 gRPC 负责处理跨语言的通信问题。
  2. 高效的通信协议:gRPC 使用 HTTP/2 作为其传输协议,这使得它比传统的基于 HTTP/1.1 的通信更加高效。HTTP/2 支持多路复用、头部压缩和双向流,这些特性使得 gRPC 在处理大量并发请求时具有更好的性能。
  3. 基于 Protocol Buffers (protobuf):gRPC 使用 Protocol Buffers 作为其接口定义语言 (IDL) 和消息格式。这种格式不仅紧凑且高效,而且支持向后兼容,适合在不同版本的客户端和服务器之间传输数据。
  4. 双向流:gRPC 支持多种通信模式,包括简单的请求-响应、服务器流、客户端流和双向流。双向流允许客户端和服务器同时发送和接收数据,适用于实时通信场景。
  5. 自动生成代码:gRPC 根据 .proto 文件(定义服务和消息结构)自动生成客户端和服务器端的代码,这使得开发变得更加简单和一致。
  6. 负载均衡与服务发现:gRPC 可以与现代的服务发现和负载均衡框架集成,使得服务在分布式系统中的扩展和管理更加容易。
    使用场景
  7. 微服务架构:在一个微服务架构中,不同的服务可能由不同的编程语言编写,并且需要进行高效的通信。gRPC 可以提供一种统一的通信框架来连接这些服务。
  8. 实时通信:由于 gRPC 支持双向流,它非常适合需要实时数据交换的应用程序,如视频流、在线游戏、聊天应用等。
  9. 移动和嵌入式设备:gRPC 使用的 Protocol Buffers 数据格式非常紧凑和高效,适合资源受限的移动和嵌入式设备。

grpc使用

gRPC 基于定义服务的思想,指定可以远程调用的方法及其参数和返回类型。在服务器端,服务器实现这个接口并运行一个gRPC服务器来处理客户端调用。
在这里插入图片描述

这个图展示了 gRPC 通信模型,其中一个 gRPC 服务器和多个客户端(用不同的编程语言编写)进行通信。
gRPC 服务器:图中的服务器是用 C++ 实现的。它是 gRPC 服务的服务器端。
gRPC Stub:在客户端这边,gRPC 使用 “Stub” 来抽象出与服务器通信的过程。Stub 是客户端的代理,通过它客户端可以像调用本地函数一样发送请求给服务器。图中有两个客户端:一个是 Ruby 编写的客户端,另一个是用 Java 编写的 Android 客户端。
Proto Request:客户端发送给服务器的请求。这个请求使用 Protocol Buffers(简称 Proto)进行序列化,Proto 是 gRPC 使用的一种高效的数据交换格式。
Proto Response:服务器处理请求后,返回给客户端的响应,同样是用 Proto 格式进行序列化。
两个客户端(Ruby 和 Android Java)通过 gRPC Stub 向服务器发送 Proto 请求,服务器处理请求并返回 Proto 响应。
总结来说,这个图表清晰地展示了 gRPC 如何实现跨平台、跨语言的远程过程调用(RPC)。客户端可以用不同的语言编写,通过 gRPC 与服务器通信。gRPC 负责处理底层的网络通信、序列化和反序列化,使开发者能够专注于业务逻辑。

grpc结合protobuf使用

这张图展示了 gRPC 的核心概念和工作流程,分为客户端和服务器端,重点在于如何通过 .proto 文件定义服务,并使用 HTTP/2 协议和 Protocol Buffers(protobuf)进行通信。

  1. 服务定义 (.proto 文件):.proto 文件是用来定义服务的协议文件。它包含了服务的接口以及消息的格式。gRPC 使用这个 .proto 文件来生成客户端存根(stub)和服务器端的骨架代码(skeleton)。
  2. 生成客户端存根:根据 .proto 文件,gRPC 自动生成客户端存根(grpc stub)。客户端应用程序通过这个存根与服务器进行通信。存根封装了客户端调用 gRPC 服务的所有细节,使得客户端调用远程服务就像调用本地方法一样。
  3. 生成服务器端骨架:同样地,gRPC 也会根据 .proto 文件生成服务器端的骨架代码(grpc skeleton)。服务器开发者可以在这个骨架上实现具体的服务逻辑。
  4. 基于 HTTP/2 的 Protocol Buffers 通信:gRPC 使用 HTTP/2 协议来进行客户端与服务器之间的通信,传输的数据格式是 Protocol Buffers。客户端通过 grpc stub 发送请求,服务器通过 grpc skeleton 处理请求并返回响应。

服务端和客户端的调用和实现

服务器端

实现服务方法:服务器端会实现 .proto 文件中定义的服务方法。这些方法是服务逻辑的核心部分。
这些方法接受解码后的请求消息,并返回编码后的响应消息。
运行 gRPC 服务器:
服务器会启动一个 gRPC 服务器实例,并将实现了服务方法的类注册到这个服务器上。
这个服务器实例会监听指定的端口,等待客户端的调用请求。
gRPC 基础设施处理请求:
当客户端发送请求时,gRPC 基础设施负责解码传入的请求消息,将其转换为服务方法可以理解的参数。
服务方法执行完毕后,gRPC 基础设施会将返回的响应消息编码并发送回客户端。

客户端

存根(Stub)对象:
客户端使用 gRPC 编译器生成的代码创建一个存根(Stub)对象。这是客户端与服务器进行通信的接口。
存根对象提供与服务器端服务相同的方法。
调用服务方法:
客户端在存根对象上调用方法时,gRPC 客户端库会将调用的参数封装成协议缓冲区消息(Protocol Buffers)。
这些封装后的消息会通过网络发送到服务器端。
接收响应:
服务器处理请求并返回响应消息,客户端的存根对象接收到这个响应后,gRPC 客户端库会解码消息并将结果返回给客户端应用程序。

案例

假设我们定义了一个简单的服务 Greeter,有一个 SayHello 方法:

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

服务器端:
实现服务方法:服务器端会实现 SayHello 方法,例如:

class GreeterServiceImpl final : public Greeter::Service {
  grpc::Status SayHello(grpc::ServerContext* context, const HelloRequest* request, HelloReply* reply) override {
    std::string prefix("Hello ");
    reply->set_message(prefix + request->name());
    return grpc::Status::OK;
  }
};

运行 gRPC 服务器:

GreeterServiceImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort("localhost:50051", grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
server->Wait();

客户端
创建存根对象

std::unique_ptr<Greeter::Stub> stub = Greeter::NewStub(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));

调用服务方法
HelloRequest request;
request.set_name(“World”);

HelloReply reply;
grpc::ClientContext context;

grpc::Status status = stub->SayHello(&context, request, &reply);

if (status.ok()) {
std::cout << "Greeter received: " << reply.message() << std::endl;
} else {
std::cout << “RPC failed” << std::endl;
}
在这个例子中,客户端会调用 SayHello 方法,并通过存根对象发送请求。服务器会处理这个请求并返回响应。客户端随后会解码响应并将结果返回给用户。
这个机制使得 gRPC 在分布式系统中成为一种高效且强大的工具,尤其适用于需要定义明确的 API 和高性能远程调用的应用场景。
在这里插入图片描述

常用函数或类

服务器端

ServerBuilder:用于创建和启动grpc::Server实例的构建器类
GreeterServiceImpl:创建服务实现类的实例
AddListeningPort:指定我们要用于侦听客户端请求的地址和端口
BuildAndStart:调用BuildAndStart()构建器为服务创建和启动 gRPC 服务器
例如下面这个服务器的案例

/**/
GreeterServiceImpl service;
grpc::ServerBuilder builder;
builder.AddListeningPort("localhost:50051", grpc::InsecureServerCredentials());
builder.RegisterService(&service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
server->Wait();

  1. 创建 GreeterServiceImpl 类的实例 service。GreeterServiceImpl 是你为服务定义的类,这个类实现了 .proto 文件中定义的服务接口(例如 Greeter::Service)。该实例封装了所有服务器端的业务逻辑。当客户端调用 SayHello 或其他服务方法时,这个实例的方法会被调用来处理请求。
  2. 创建一个 grpc::ServerBuilder 对象 builder。ServerBuilder 是 gRPC 提供的用于配置和构建服务器的类。你可以通过它来设置服务器的各种属性,比如监听的端口、注册的服务、线程池大小等。
  3. 通过 builder 添加一个监听的端口和对应的安全凭证配置。
  4. 将之前创建的 GreeterServiceImpl 实例 service 注册到 ServerBuilder 中。这一步将 service 与服务器绑定,使得服务器能够处理客户端发送的与 Greeter 服务相关的请求。RegisterService 方法告诉服务器哪个服务方法应该处理特定的 RPC 调用。
  5. BuildAndStart() 是 ServerBuilder 类的一个方法,它会根据之前的配置创建一个新的 gRPC 服务器实例,并立即启动它。
  6. 使用 builder.BuildAndStart() 构建和启动 gRPC 服务器,并将其返回的指针保存到 server 中。
  7. 调用 server 的 Wait() 方法,这会使服务器进入等待状态,直到它被关闭。Wait() 方法会阻塞当前线程,使服务器保持运行状态,等待并处理来自客户端的请求。

整体流程
首先,创建并配置 GreeterServiceImpl 服务实例,绑定处理逻辑。
然后,创建一个 gRPC 服务器构建器 ServerBuilder,通过它来配置服务器的监听地址和注册服务。
BuildAndStart() 方法生成服务器实例并立即启动它。
最后,Wait() 使得服务器持续运行,等待客户端的连接和请求。
这段代码就是服务器端的启动代码,一旦执行,gRPC 服务器将开始监听客户端请求,并且能够处理请求中定义的服务方法。

使用protoc编译器生成grpc服务框架

在这里插入图片描述

1、消息定义:在 .proto 文件中使用 message 关键字定义请求和响应的消息类型。
2、服务定义:使用 service 关键字定义 gRPC 服务,并使用 rpc 关键字定义方法,方法的参数和返回类型为消息类型。
3、生成代码:使用 protoc 编译器生成服务端和客户端的代码框架。
详细步骤如下

  • 在.proto文件中定义消息类型和gprc服务:
    在 .proto 文件中定义要在 RPC 方法中使用的请求和响应消息。这些消息将作为 RPC 方法的参数和返回类型。
syntax = "proto3";

package example;

// 定义请求消息
message MyRequest {
  string name = 1;
  int32 id = 2;
}

// 定义响应消息
message MyResponse {
  string message = 1;
  bool success = 2;
}

// 定义 gRPC 服务
service MyService {
  // 定义一个简单的 RPC 方法
  rpc MyMethod (MyRequest) returns (MyResponse);
}

  • 生成代码:保存 .proto 文件后,你可以使用 protoc 编译器生成客户端和服务器的代码框架。
# 生成 gRPC 和 Protocol Buffers 代码
protoc --grpc_out=. --plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) your_service.proto//生成grpc.pb.h和grpc.pb.cc的
protoc --cpp_out=. your_service.proto	//生成pb.h和pb.cc的
//--grpc_out=.: 这个选项指定了 gRPC 服务代码的输出目录。. 表示当前目录。生成的文件通常会有 .grpc.pb.h 和 .grpc.pb.cc 后缀,包含 gRPC 的服务端和客户端的框架代码。
//--plugin=protoc-gen-grpc=$(which grpc_cpp_plugin): 这个选项指定使用哪个插件来生成 gRPC 代码。在这种情况下,grpc_cpp_plugin 是用来生成 C++ 的 gRPC 代码的插件。$(which grpc_cpp_plugin) 会找到 grpc_cpp_plugin 的路径,并将其传递给 protoc 编译器。
//your_service.proto: 这是你的 Protocol Buffers 文件,包含服务定义和消息类型定义。protoc 会根据这个文件生成相应的 gRPC 代码。

以上命令会生成与 .proto 文件中定义的 gRPC 服务和消息相对应的 C++ 代码,其他语言的代码生成命令类似,只是插件和输出选项会有所不同。
服务端:你将使用 .grpc.pb.h 和 .grpc.pb.cc 文件来实现服务端逻辑,使用 .pb.h 和 .pb.cc 文件来处理消息的序列化和反序列化。
客户端:你将使用 .grpc.pb.h 和 .grpc.pb.cc 文件来实现客户端的调用逻辑,使用 .pb.h 和 .pb.cc 文件来构造和解析消息。

protoc编译后生成了什么

通过在 .proto 文件中定义 gRPC 服务,并使用 protoc 编译器生成代码后,你将得到客户端和服务端的代码框架。这些生成的代码会包含在 .pb.h 和 .pb.cc(或者 .grpc.pb.h 和 .grpc.pb.cc)文件中。

  1. .pb.h 和 .pb.cc 文件:这些文件主要包含由 Protocol Buffers 生成的消息类的定义和实现。例如,MyRequest 和 MyResponse 消息类型会在这些文件中定义。它们允许你在代码中序列化和反序列化这些消息,以及访问消息的各个字段。
  2. .grpc.pb.h 和 .grpc.pb.cc 文件:这些文件主要包含 gRPC 服务的客户端和服务端代码框架。例如,服务端将有一个基类,你可以继承这个基类并实现实际的服务逻辑;客户端将有一个 Stub 类,允许你调用远程服务的方法。
    你需要在这些生成的代码框架基础上实现自己的服务逻辑和客户端调用逻辑。gRPC 自动生成的代码使得客户端和服务端的通信变得简单,开发者只需要专注于业务逻辑的实现。

channel概念

Channel 是 gRPC 中一个非常重要的概念,扮演着客户端和服务器端之间通信桥梁的角色。
端点连接: Channel 可以理解为一个连接对象,代表客户端到服务器的一个逻辑连接。客户端通过这个 Channel 向服务器发起 RPC(Remote Procedure Call,远程过程调用)请求。
抽象层: Channel 并不是一个实际的网络连接,而是 gRPC 内部维护的一个抽象层。它可能在多个底层网络连接上操作,并且可以根据负载均衡和配置自动选择不同的连接。

channel的核心作用

负载均衡: 在分布式系统中,通常服务器端有多个实例,Channel 可以帮助客户端实现负载均衡。gRPC 客户端可以通过 Channel 动态选择最适合的服务器实例来处理请求。
连接管理: Channel 管理底层连接池,并处理与服务器之间的连接重用和复用,确保客户端请求的高效传输。
RPC 请求的传输: 所有的 RPC 请求都通过 Channel 发送到服务器。Channel 负责封装请求,处理底层通信细节,并确保请求的可靠传输。

channel的配置

目标服务器地址: 创建 Channel 时,必须指定服务器的地址(例如 localhost:50051)。这个地址是客户端要连接的服务器的端点。
安全配置: Channel 可以配置为使用不同的安全选项,例如 TLS/SSL 加密通信。gRPC 提供了多种安全凭证(credentials)来配置 Channel。
负载均衡策略: Channel 可以配置为使用不同的负载均衡策略,例如轮询(round-robin)、随机选择等。gRPC 通过 Channel 内置了多种负载均衡机制来优化请求分配。

创建和使用Channel

在客户端代码中,通常会创建一个 Channel,然后通过这个 Channel 来创建一个存根(stub)对象。这个存根对象用于调用服务端的 RPC 方法。
示例:

// 创建一个到指定服务器地址的 Channel
auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());

// 通过 Channel 创建一个 gRPC 存根(Stub)
auto stub = Greeter::NewStub(channel);

// 使用存根调用服务端的方法
HelloRequest request;
request.set_name("World");
HelloReply reply;
grpc::ClientContext context;
grpc::Status status = stub->SayHello(&context, request, &reply);

Channel 是 gRPC 客户端和服务器通信的核心部分,它管理着底层的连接、负载均衡和请求的传输。
Channel 的设计使得客户端能够透明地处理服务器端实例的动态变化,并通过简单的接口与远程服务进行交互。

认证

gRPC 提供了多种身份验证机制来保护客户端和服务器之间的通信。以下是 gRPC 身份验证的概述,包括内置的身份验证机制和如何插入自定义的身份验证系统。

gRPC 内置身份验证机制

1、TLS/SSL(传输层安全)

TLS/SSL 是 gRPC 内置支持的最常见的身份验证机制,用于确保通信的加密和完整性。
双向认证: 支持单向和双向认证。单向认证是服务器向客户端证明身份,双向认证则同时验证客户端和服务器的身份。
在单向认证中,客户端验证服务器证书的真实性。
在双向认证中,客户端和服务器都需要验证对方的证书。
设置方式
服务器端:

grpc::SslServerCredentialsOptions::PemKeyCertPair keycert = {server_key, server_cert};
grpc::SslServerCredentialsOptions ssl_opts(GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY);
ssl_opts.pem_key_cert_pairs.push_back(keycert);
ssl_opts.pem_root_certs = root_certs;
auto server_creds = grpc::SslServerCredentials(ssl_opts);
server_builder.AddListeningPort("localhost:50051", server_creds);

客户端:

auto creds = grpc::SslCredentials(grpc::SslCredentialsOptions());
auto channel = grpc::CreateChannel("localhost:50051", creds);

2、gooole的JWT/OAuth2

Google IAM 和 Google 的身份验证系统: 如果您在 Google Cloud Platform 上构建应用程序,可以利用 Google 的 IAM 和 OAuth2 机制,通过 JWT(JSON Web Tokens)来进行身份验证。
gRPC 使用的 tokens:
JWT: gRPC 允许您将 JWT 令牌附加到 RPC 请求的元数据中,从而进行身份验证。
OAuth2: gRPC 支持 OAuth2 协议,允许您使用访问令牌来验证用户身份。
设置方式
客户端(使用 OAuth2 访问令牌):

auto creds = grpc::ServiceAccountJWTAccessCredentials(json_key);
auto channel = grpc::CreateChannel("localhost:50051", creds);

grpc::InsecureChannelCredentials() 是 gRPC 提供的一种方法,用于创建不使用任何加密或身份验证的通道凭据。具体来说,它创建了一个不安全的通道(insecure channel),即客户端和服务器之间的通信不进行任何加密,也没有进行身份验证。

grpc::ClientContext 是 gRPC 库中的一个类,用于客户端调用时的上下文管理。

它封装了 RPC 调用的元数据、超时设置、取消操作等各种控制选项。在 gRPC 客户端发起一个 RPC 请求时,ClientContext 是用于配置和管理该请求的重要对象。

主要作用:

1、设置和获取元数据:

ClientContext 允许在 RPC 请求中添加自定义的元数据(Metadata),这些元数据通常用于认证、追踪等目的。

grpc::ClientContext context;
context.AddMetadata("custom-key", "custom-value");

2、设置超时

可以在 ClientContext 中设置请求的超时时间,以确保客户端不会无限期地等待响应。

grpc::ClientContext context;
std::chrono::system_clock::time_point deadline = std::chrono::system_clock::now() + std::chrono::seconds(10);
context.set_deadline(deadline);

3、取消请求

在某些情况下,客户端可能需要取消一个正在进行的 RPC 调用。例如,当用户中断操作时,ClientContext 提供了取消功能。

context.TryCancel();

4、管理压缩和身份验证

ClientContext 还可以用于设置请求的压缩选项,或配置身份验证信息,虽然这些功能通常较少直接使用。

5、跟踪请求状态

ClientContext 也可以在调用返回后用于检查请求的状态,包括 RPC 是否成功,是否有错误发生,以及错误的详细信息。

典型使用流程

在 gRPC 中,当客户端想要发起一个 RPC 请求时,通常会创建一个 ClientContext 实例,并将其传递给存根(Stub)的方法。ClientContext 控制着这个请求的各种行为。

grpc::ClientContext context;
MyRequest request;
MyResponse response;

// 设置元数据、超时等选项
context.AddMetadata("token", "12345");
std::chrono::system_clock::time_point deadline = std::chrono::system_clock::now() + std::chrono::milliseconds(500);
context.set_deadline(deadline);

// 调用 RPC 方法
grpc::Status status = stub->MyRpcMethod(&context, request, &response);

// 检查调用是否成功
if (status.ok()) {
    // 处理响应
} else {
    // 处理错误
}

grpc::ClientContext 是管理客户端 RPC 调用行为的核心工具,提供了对元数据、超时、取消操作等的控制。在 gRPC 编程中,它是发起请求时的一个基本组成部分,能让开发者灵活配置和管理每一次 RPC 调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值