C++ grpc使用示例

核心工作

  • 在一个 .proto 文件内定义服务.
  • 用 protocol buffer 编译器生成服务器和客户端代码.
  • 使用 gRPC 的 C++ API 为你的服务实现一个简单的客户端和服务器.
服务定义

route_guide.proto 定义服务的proto文件:

service RouteGuide {
  rpc GetFeature(Point) returns (Feature) {}
  rpc ListFeatures(Rectangle) returns (stream Feature) {}
  rpc RecordRoute(stream Point) returns (RouteSummary) {}
  rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
}

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}
生成客户端和服务器端代码

通过protocol buffer的编译器protoc以及一个特殊的gRPC C++插件来完成。运行对应的命令后在当前目录中生成如下文件:
route_guide.pb.h, 声明生成的消息类的头文件
route_guide.pb.cc, 包含消息类的实现
route_guide.grpc.pb.h, 声明你生成的服务类的头文件
route_guide.grpc.pb.cc, 包含服务类的实现

这些包括:
所有的填充,序列化和获取我们请求和响应消息类型的 protocol buffer 代码
名为 RouteGuide 的类,包含

  1. 为了客户端去调用定义在 RouteGuide 服务的远程接口类型(或者 存根 )
  2. 让服务器去实现的两个抽象接口,同时包括定义在 RouteGuide 中的方法。
创建服务器

想要让RouteGuide服务工作有两个部分

  1. 实现我们服务定义的生成的服务接口:做我们的服务的实际工作
  2. 运行一个gRPC服务器,监听来自客户端的请求并返回服务的响应。
实现RouteGuide

我们可以看出,服务器有一个实现了生成的 RouteGuide::Service 接口的 RouteGuideImpl 类:

class RouteGuideImpl final : public RouteGuide::Service {
  Status GetFeature(ServerContext* context, const Point* point,Feature* feature) override {
    feature->set_name(GetFeatureName(*point, feature_list_));
    feature->mutable_location()——>CopyFrom(*point);
    return Status::OK;
  }
}

这个方法为 RPC 传递了一个上下文对象,包含了客户端的 Point protocol buffer 请求以及一个填充响应信息的Feature protocol buffer。在这个方法中,我们用适当的信息填充 Feature,然后返回OK的状态,告诉 gRPC 我们已经处理完 RPC,并且 Feature 可以返回给客户端。

启动服务器

一旦我们实现了所有的方法,我们还需要启动一个gRPC服务器,这样客户端才可以使用服务。下面这段代码展示了在我们RouteGuide服务中实现的过程:

void RunServer(const std::string& db_path) {
  std::string server_address("0.0.0.0:50051");
  RouteGuideImpl service(db_path);

  ServerBuilder builder;
  builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
  builder.RegisterService(&service);
  std::unique_ptr server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
}

如你所见,我们通过使用ServerBuilder去构建和启动服务器。为了做到这点,我们需要:

创建我们的服务实现类 RouteGuideImpl 的一个实例。
创建工厂类 ServerBuilder 的一个实例。
在生成器的 AddListeningPort() 方法中指定客户端请求时监听的地址和端口。
用生成器注册我们的服务实现。
调用生成器的 BuildAndStart() 方法为我们的服务创建和启动一个RPC服务器。
调用服务器的 Wait() 方法实现阻塞等待,直到进程被杀死或者 Shutdown() 被调用。

创建客户端

examples/cpp/route_guide/route_guide_client.cc看到我们完整的客户端例子代码.

创建一个存梗

为了能调用服务的方法,我们先创建一个存根。
首先需要为我们的存根创建一个gRPC channel,指定我们想联机的服务器的地址和端口,以及channel相关的参数

grpc::CreateChannel("localhost:50051", grpc::InsecureCredentials(), ChannelArguments());

现在我们可以利用channel,使用从.proto中生成的RouteGuide类提供的NewStub方法去创建存根。

public:
  RouteGuideClient(std::shared_ptr channel,
                   const std::string& db)
      : stub_(RouteGuide::NewStub(channel)) {
    ...
  }
调用服务的方法

现在我们来看看如何调用服务的方法。注意,在本教程中调用的方法,都是 阻塞/同步 的版本:这意味着 RPC 调用会等待服务器响应,要么返回响应,要么引起一个异常。
简单RPC
调用简单 RPC GetFeature 几乎是和调用一个本地方法一样直观。

Point point;
  Feature feature;
  point = MakePoint(409146138, -746188906);
  GetOneFeature(point, &feature);

...

  bool GetOneFeature(const Point& point, Feature* feature) {
    ClientContext context;
    Status status = stub_->GetFeature(&context, point, feature);
    ...
  }

如你所见,我们创建并且填充了一个请求的 protocol buffer 对象(例子中为 Point),同时为了服务器填写创建了一个响应 protocol buffer 对象。为了调用我们还创建了一个 ClientContext 对象——你可以随意的设置该对象上的配置的值,比如期限,虽然现在我们会使用缺省的设置。注意,你不能在不同的调用间重复使用这个对象。最后,我们在存根上调用这个方法,将其传给上下文,请求以及响应。如果方法的返回是OK,那么我们就可以从服务器从我们的响应对象中读取响应信息。

std::cout << "Found feature called " << feature->name()  << " at "
                << feature->location().latitude()/kCoordFactor_ << ", "
                << feature->location().longitude()/kCoordFactor_ << std::endl;

你可能感兴趣的:(C++ grpc使用示例)