grpc介绍及使用

1.RPC(Remote Procedure Call)

RPC,即远程过程调用,允许位于不同地址空间的程序之间进行通信,就像调用本地服务一样简单。RPC的核心思想是通过网络将程序调用请求发送到远程服务器,服务器执行请求后返回结果。

1. rpc完整流程(函数映射/编码解码/网络传输)

RPC(远程过程调用)的完整流程可以分为以下几个关键步骤,这些步骤在客户端和服务端之间进行交互,以实现远程方法调用。以下是详细的流程描述:

1. 定义服务接口

  • 服务定义:在RPC框架中,首先需要定义服务接口,包括可以远程调用的方法及其参数和返回类型。这通常通过一个接口定义语言(IDL)来完成,例如gRPC使用.proto文件。

  • 示例

    service Greeter {
      rpc SayHello (HelloRequest) returns (HelloReply) {}
    }
    
    message HelloRequest {
      string name = 1;
    }
    
    message HelloReply {
      string message = 1;
    }

2. 生成代码

  • 代码生成:使用框架提供的工具(如protoc编译器)将服务定义文件转换为特定语言的代码。生成的代码包括客户端存根(stub)和服务端骨架(skeleton)。

  • 客户端存根:客户端存根提供了与服务端相同的方法接口,但实际调用时会通过网络发送请求。

  • 服务端骨架:服务端骨架定义了服务端需要实现的方法接口。

3. 服务端实现

  • 实现服务接口:服务端开发者根据生成的骨架代码实现服务接口,定义每个方法的具体逻辑。

  • 启动服务:服务端启动一个RPC服务器,监听客户端的连接请求。服务端需要注册服务接口,并绑定到特定的端口。

  • 示例(gRPC C++)

    class GreeterServiceImpl : public Greeter::Service {
      Status SayHello(ServerContext* context, const HelloRequest* request, HelloReply* reply) override {
        std::string prefix("Hello ");
        reply->set_message(prefix + request->name());
        return Status::OK;
      }
    };
    
    int main() {
      GreeterServiceImpl service;
      ServerBuilder builder;
      builder.AddListeningPort("localhost:50051", grpc::InsecureServerCredentials());
      builder.RegisterService(&service);
      std::unique_ptr<Server> server(builder.BuildAndStart());
      server->Wait();
      return 0;
    }

4. 客户端调用

  • 创建客户端存根:客户端使用生成的存根代码创建一个客户端存根实例,该存根用于调用远程服务。

  • 发起请求:客户端通过存根调用远程方法,将请求参数序列化后发送到服务端。

  • 示例(gRPC C++)

    int main() {
      auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
      auto stub = Greeter::NewStub(channel);
      HelloRequest request;
      request.set_name("world");
      HelloReply reply;
      grpc::ClientContext context;
      Status status = stub->SayHello(&context, request, &reply);
      if (status.ok()) {
        std::cout << "Greeter received: " << reply.message() << std::endl;
      } else {
        std::cerr << "RPC failed" << std::endl;
      }
      return 0;
    }

5. 网络传输

  • 请求传输:客户端将请求数据序列化后通过网络发送到服务端。

  • 响应传输:服务端处理请求后,将响应数据序列化并发送回客户端。

6. 服务端处理

  • 接收请求:服务端接收到客户端的请求后,反序列化请求数据。

  • 执行方法:服务端调用本地实现的方法,处理请求逻辑。

  • 返回响应:服务端将响应数据序列化后发送回客户端。

7. 客户端接收响应

  • 反序列化响应:客户端接收到响应数据后,反序列化响应内容。

  • 处理结果:客户端根据反序列化的响应数据处理结果。

8. 关闭连接

  • 客户端关闭:客户端完成调用后,关闭与服务端的连接。

  • 服务端关闭:服务端在完成所有请求处理后,可以选择关闭服务。

 2.rpc与http1.1区别

1. 数据格式

  • HTTP

    • HTTP 数据格式通常是文本格式,如 HTML、JSON、XML 等。

    • HTTP 头部和正文是明文的,易于理解和调试。

  • RPC

    • RPC 数据格式可以是二进制格式(如 Protocol Buffers、Thrift)或文本格式(如 JSON、XML)。

    • 二进制格式的 RPC 数据更紧凑,传输效率更高,但调试时不如文本格式直观。

2. 连接管理

  • HTTP

    • HTTP/1.1 支持持久连接(keep-alive),但每个请求仍然需要单独建立和关闭连接。

  • RPC

    • RPC 框架通常会优化连接管理,以减少连接开销。例如,gRPC 使用 HTTP/2 的多路复用特性,允许在同一个连接上并发处理多个 RPC 调用。

    • 一些 RPC 框架(如 Thrift)可以使用长连接(long-lived connections),减少连接建立和关闭的开销。

2.gRPC(Google Remote Procedure Call)

gRPC是由Google开发的高性能、开源、通用的RPC框架,基于HTTP/2协议标准设计开发,默认采用Protocol Buffers(ProtoBuf)作为数据序列化协议。gRPC允许客户端应用直接调用其他机器上的服务器应用中的方法,如同调用本地对象一样。

1.HTTP/2 主要特点

  1. 二进制帧结构:HTTP/2 将数据以二进制帧的形式传输,而不是 HTTP/1.x 的文本格式。这种二进制帧结构使得协议更高效,减少了数据传输量。

  2. 多路复用:HTTP/2 允许在一个 TCP 连接上同时发送多个请求和响应,避免了 HTTP/1.x 中的“队头阻塞”问题。这种多路复用机制大大提高了网络资源的利用率。

  3. 头部压缩:HTTP/2 使用 HPACK 算法对 HTTP 头部进行压缩,显著减少了头部数据的传输量。这不仅提高了传输效率,还减少了带宽占用。

  4. 服务器推送:HTTP/2 支持服务器主动向客户端推送资源,而无需客户端明确请求。这一特性可以优化页面加载速度,提升用户体验。

  5. 流控制:HTTP/2 引入了流的概念,允许对每个流进行优先级设置和流量控制。这使得资源可以根据重要性优先传输,进一步优化了性能。

3.grpc使用

 1.定义服务和消息

syntax = "proto3";

package helloworld;

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

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

service 是一个关键字,用于在 .proto 文件中定义一个服务接口。服务接口是一组远程过程调用(RPC)方法的集合,客户端可以通过这些方法与服务端进行通信。

1. rpc 关键字

rpc.proto 文件中用于定义远程过程调用的关键字。它表示这是一个可以被客户端调用的服务端方法。

2. 方法名:SayHello

SayHello 是这个 RPC 方法的名称。客户端可以通过这个名称调用服务端的对应方法。

3. 请求消息类型:HelloRequest

HelloRequest 是客户端发送给服务端的请求消息类型。它定义了客户端需要提供的数据结构。例如:

message HelloRequest {
  string name = 1;
}

在这个例子中,HelloRequest 包含一个 string 类型的字段 name,客户端需要在调用 SayHello 方法时提供这个字段的值。

4. 返回消息类型:HelloReply

HelloReply 是服务端返回给客户端的响应消息类型。它定义了服务端需要返回的数据结构。例如:

message HelloReply {
  string message = 1;
}

在这个例子中,HelloReply 包含一个 string 类型的字段 message,服务端会在处理完请求后将结果放在这个字段中返回给客户端。

5. 方法体:{}

.proto 文件中,方法体是空的 {}。实际的实现逻辑需要在生成的代码中完成 

2. 服务端骨架

服务端骨架是基于 .proto 文件生成的代码,用于定义服务接口和处理 RPC 请求。

2.1 服务端骨架代码结构

生成的 greeter.grpc.pb.h 文件中定义了服务接口 Greeter

namespace helloworld {
class Greeter {
 public:
  virtual ~Greeter() {}
  virtual ::grpc::Status SayHello(
      ::grpc::ServerContext* context,
      const ::helloworld::HelloRequest* request,
      ::helloworld::HelloReply* response) = 0;
};
}
1.Status 的作用

::grpc::Status 是 gRPC 的一个类,用于表示 RPC 调用的结果状态。它包含以下信息:

  1. 状态码(code:表示 RPC 调用是否成功,常见的状态码有:

    • OK(0):表示调用成功。

    • CANCELLED(1):表示调用被取消。

    • UNKNOWN(2):表示未知错误。

    • INVALID_ARGUMENT(3):表示请求参数无效。

    • DEADLINE_EXCEEDED(4):表示超时。

    • 等等,更多状态码可以参考 gRPC Status Codes

  2. 错误信息(error_message:如果调用失败,可以提供更详细的错误信息。

  3. 错误详情(error_details:可以提供更复杂的错误详情,通常是一个序列化的 google.protobuf.Any 消息。

2.::grpc::ServerContext* context:提供 RPC 调用的上下文信息,如超时、取消、元数据等。 
  1. 身份验证和授权ServerContext 可以用于处理与身份验证和授权相关的操作,例如获取客户端的身份信息。

  2. 超时和取消:它允许设置超时时间,或者在 RPC 调用过程中取消操作。

  3. 元数据:可以用于处理请求和响应的元数据(如 HTTP 头信息),例如在跨域请求中传递认证信息

2.2 实现服务端逻辑

继承 Greeter::Service 类并实现 SayHello 方法:

#include "greeter.grpc.pb.h"

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

final是一个类修饰符,用于表示一个类不能被继承,不能再被派生出子类。 

2.3 启动 gRPC 服务器

创建并启动服务器:

#include <grpcpp/grpcpp.h>
#include "greeter.grpc.pb.h"

void RunServer() {
  std::string server_address("0.0.0.0:50051");
  GreeterServiceImpl service;

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

  std::unique_ptr<grpc::Server> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
}

int main(int argc, char** argv) {
  RunServer();
  return 0;
}

gRPC 的 C++ 实现中,grpc::ServerBuilder 是一个用于构建和配置 gRPC 服务器的工具类。它提供了一系列方法来设置服务器的各种参数,包括监听端口、注册服务、设置证书等。

builder.AddListeningPort 方法用于添加服务器监听的端口。

grpc::ServerBuilder& AddListeningPort(
    const std::string& addr,
    std::shared_ptr<grpc::ServerCredentials> creds,
    int* selected_port = nullptr);

builder.RegisterService用于注册一个服务。它的原型如下:

grpc::ServerBuilder& RegisterService(grpc::Service* service);
std::unique_ptr<grpc::Server> server(builder.BuildAndStart());

建并启动一个 gRPC 服务器实例,并将其存储在一个 std::unique_ptr

server->Wait():阻塞主线程,直到服务器停止。这通常是一个无限循环,直到服务器被显式停止(例如,通过信号处理或其他机制)

3. 客户端存根

客户端存根是基于 .proto 文件生成的代码,用于简化客户端对服务端的 RPC 调用。

3.1 客户端存根代码结构

生成的 greeter.grpc.pb.h 文件中定义了客户端存根类 Greeter::Stub

namespace helloworld {
class Greeter::Stub {
 public:
  explicit Stub(grpc::ChannelInterface* channel);//构造函数声明,初始化一个客户端存根

  grpc::Status SayHello(
      grpc::ClientContext* context,
      const HelloRequest& request,
      HelloReply* response);
};
}
3.2 使用客户端存根调用服务

创建客户端并调用服务端的方法:

#include <grpcpp/grpcpp.h>
#include "greeter.grpc.pb.h"

class GreeterClient {
 public:
    //构造函数的作用是初始化 GreeterClient 类的成员变量 stub_,并将其绑定到一个 gRPC 通道(channel)上
  GreeterClient(std::shared_ptr<grpc::Channel> channel)
      : stub_(helloworld::Greeter::NewStub(channel)) {}

  std::string SayHello(const std::string& user) {
    helloworld::HelloRequest request;
    request.set_name(user);

    helloworld::HelloReply reply;
    grpc::ClientContext context;

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

    if (status.ok()) {
      return reply.message();
    } else {
      return "RPC failed";
    }
  }

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

int main(int argc, char** argv) {
  GreeterClient client(grpc::CreateChannel(
      "localhost:50051", grpc::InsecureChannelCredentials()));
  std::string user("world");
  std::string reply = client.SayHello(user);

  std::cout << "Greeter received: " << reply << std::endl;

  return 0;
}
auto channel = grpc::CreateChannel("192.168.1.100:50051", grpc::InsecureChannelCredentials());
1.GreeterClient 与 Stub 的关系
  • 封装关系GreeterClient 封装了 Stub,使得客户端代码更加简洁和易于使用。

  • 调用关系GreeterClient 通过 Stub 来发起 RPC 调用。例如,在 SayHello 方法中,GreeterClient 使用 stub_ 调用了 SayHello 方法。

  • 客户端类(如 GreeterClient)封装了 Stub:在初始化时,客户端类通过 Stub 的工厂方法创建了一个 Stub 对象,并将其存储在成员变量中。

  • 客户端类通过 Stub 发起 RPC 调用:客户端类的方法(如 SayHello)通过 Stub 的方法向服务端发起 RPC 请求。

 存根(Stub) 是一个客户端代理对象,用于代理对服务端的远程调用。通过在客户端类中初始化存根,你可以方便地调用存根中的方法,从而实现与服务端的通信。

2.helloworld::Greeter::NewStub:

这是一个静态方法,用于创建一个helloworld::Greeter::Stub 对象。它接受一个 std::shared_ptr<grpc::Channel> 对象作为参数,并返回一个std::unique_ptr<helloworld::Greeter::Stub> 对象。

RpcClient::RpcClient(const std::string& server_address) {
  auto channel =
      grpc::CreateChannel(server_address, grpc::InsecureChannelCredentials());
  stub_ptr_ = monitor::proto::GrpcManager::NewStub(channel);
}

1. CreateChannel 的作用

CreateChannel 用于创建一个到 gRPC 服务器的连接通道。这个通道是客户端与服务器之间通信的基础,通过它,客户端可以发送请求并接收响应。

2. CreateChannel 的参数

通常,CreateChannel 需要以下参数:

  • 服务器地址:指定 gRPC 服务器的地址,通常是 host:port 格式。

  • 通道选项(可选):可以设置一些通道的配置,例如超时时间、证书等。

3.ClientContext 的作用

  1. 设置超时时间:防止 RPC 调用无限等待。

  2. 添加元数据:可以传递额外的信息,比如身份认证、跟踪 ID。

  3. 管理流控制:在流式 RPC 中,可以取消请求或调整流行为。

  4. 获取 RPC 状态:检查调用是否成功或失败。

4. 编译和运行

4.1 编写 CMakeLists.txt
find_package(protobuf CONFIG REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(Threads REQUIRED)
find_package(c-ares CONFIG)
find_package(Threads)

set(PROTO_FILES
    monitor_info.proto
    cpu_load.proto
    cpu_softirq.proto
    cpu_stat.proto
    mem_info.proto
    net_info.proto
)

add_library(monitor_proto ${PROTO_FILES})

target_link_libraries(monitor_proto
    PUBLIC
        protobuf::libprotobuf
        gRPC::grpc
        gRPC::grpc++
)

target_include_directories(monitor_proto PUBLIC
${PROTOBUF_INCLUDE_DIRS} 
${CMAKE_CURRENT_BINARY_DIR})


get_target_property(grpc_cpp_plugin_location gRPC::grpc_cpp_plugin LOCATION)
protobuf_generate(TARGET monitor_proto LANGUAGE cpp)
protobuf_generate(TARGET monitor_proto LANGUAGE grpc GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc PLUGIN "protoc-gen-grpc=${grpc_cpp_plugin_location}")
find_package(Protobuf REQUIRED)
find_package(gRPC CONFIG REQUIRED)
find_package(Threads REQUIRED)
find_package(c-ares CONFIG)

set(
    SOURCES
    main.cpp 
    rpc_server.cpp
)
add_executable(server ${SOURCES})

set(CMAKE_BUILD_TYPE Debug)

target_include_directories(server PUBLIC "/home/book/Desktop/proj/work/build/proto")
target_link_directories(server PUBLIC "/home/book/Desktop/proj/work/build/proto")
target_link_libraries(server
    PUBLIC 
    monitor_proto
    protobuf::libprotobuf
    gRPC::grpc
    gRPC::grpc++
    )

4.2 编译和运行
mkdir build
cd build
cmake ..
make

# 启动服务端
./greeter_server

# 启动客户端
./greeter_client

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值