gRPC:基础教程

Basics tutorial

基础教程

A basic tutorial introduction to gRPC in C++.

C++中gRPC的基本教程介绍。

This tutorial provides a basic C++ programmer’s introduction to working with gRPC.

本教程提供了C++程序员使用gRPC的基本介绍。

By walking through this example you’ll learn how to:

通过浏览此示例,将学会如何:

  • Define a service in a .proto file.
  • 在.proto文件中定义服务。
  • Generate server and client code using the protocol buffer compiler.
  • 使用协议缓冲区编译器生成服务器和客户端代码。
  • Use the C++ gRPC API to write a simple client and server for your service.
  • 使用C++gRPC API为您的服务编写一个简单的客户端和服务器。

It assumes that you have read the Introduction to gRPC and are familiar with protocol buffers. Note that the example in this tutorial uses the proto3 version of the protocol buffers language: you can find out more in the proto3 language guide and C++ generated code guide.

​它假设已经阅读了gRPC简介并熟悉协议缓冲区。请注意,本教程中的示例使用proto3版本的协议缓冲区语言:可以在proto3语言指南和C++生成的代码指南中找到更多信息。

Why use gRPC?

为什么要使用gRPC?

Our example is a simple route mapping application that lets clients get information about features on their route, create a summary of their route, and exchange route information such as traffic updates with the server and other clients.

我们的示例是一个简单的路线映射应用程序,它允许客户端获取有关其路线上的功能的信息,创建路线摘要,并与服务器和其他客户端交换路线信息,如流量更新。

With gRPC we can define our service once in a .proto file and generate clients and servers in any of gRPC’s supported languages, which in turn can be run in environments ranging from servers inside a large data center to your own tablet — all the complexity of communication between different languages and environments is handled for you by gRPC. We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, and easy interface updating.

有了gRPC,我们可以在.proto文件中定义一次服务,并用gRPC支持的任何语言生成客户端和服务器,这些语言和服务器可以在从大型数据中心内的服务器到您自己的平板电脑的各种环境中运行-不同语言和环境之间通信的所有复杂性都由gRPC处理。我们还获得了使用协议缓冲区的所有优点,包括高效的序列化、简单的IDL和易于更新的接口。

Example code and setup

示例代码和设置

The example code is part of the grpc repo under examples/cpp/route_guide. Get the example code and build gRPC:

​示例代码是examples/cpp/route_guide下grpc repo的一部分。获取示例代码并构建gRPC:

1.Follow the Quick start instructions to build and locally install gRPC from source.

​1.按照快速入门说明从源代码构建并本地安装gRPC。

2.From the repo folder, change to the route guide example directory:

2.从repo文件夹更改为路线指南示例目录:

$ cd examples/cpp/route_guide

3.Run cmake

3.运行cmake

$ mkdir -p cmake/build
$ cd cmake/build
$ cmake -DCMAKE_PREFIX_PATH=$MY_INSTALL_DIR ../..

Defining the service

定义服务

Our first step (as you’ll know from the Introduction to gRPC) is to define the gRPC service and the method request and response types using protocol buffers. You can see the complete .proto file in examples/protos/route_guide.proto.

​我们的第一步(将从gRPC简介中了解到)是使用协议缓冲区定义gRPC服务以及方法请求和响应类型。可以在examples/protos/route_guide.proto中看到完整的.proto文件。

To define a service, you specify a named service in your .proto file:

要定义服务,请在.proto文件中指定一个命名服务:

service RouteGuide {
   ...
}

Then you define rpc methods inside your service definition, specifying their request and response types. gRPC lets you define four kinds of service method, all of which are used in the RouteGuide service:

然后在服务定义中定义rpc方法,指定它们的请求和响应类型。gRPC允许定义四种服务方法,所有这些方法都在RouteGuide服务中使用:

  • simple RPC where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call.

  • 一个简单的RPC,客户端使用存根向服务器发送请求,并等待响应返回,就像普通的函数调用一样。

    // Obtains the feature at a given position.
    rpc GetFeature(Point) returns (Feature) {}
    
  • server-side streaming RPC where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in our example, you specify a server-side streaming method by placing the stream keyword before the response type.

  • 服务器端流式RPC,客户端向服务器发送请求并获取流以读取一系列消息。客户端从返回的流中读取,直到不再有消息为止。正如在我们的示例中看到的,可以通过将stream关键字放在响应类型之前来指定服务器端流方法。

    // Obtains the Features available within the given Rectangle.  Results are
    // streamed rather than returned at once (e.g. in a response message with a
    // repeated field), as the rectangle may cover a large area and contain a
    // huge number of features.
    rpc ListFeatures(Rectangle) returns (stream Feature) {}
    
  • client-side streaming RPC where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a client-side streaming method by placing the stream keyword before the request type.

  • 客户端流式RPC,客户端再次使用提供的流写入一系列消息并将其发送到服务器。一旦客户端完成了消息的编写,它就会等待服务器读取所有消息并返回响应。可以通过将stream关键字放在请求类型之前来指定客户端流方法。

    // Accepts a stream of Points on a route being traversed, returning a
    // RouteSummary when traversal is completed.
    rpc RecordRoute(stream Point) returns (RouteSummary) {}
    
  • bidirectional streaming RPC where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the stream keyword before both the request and the response.

  • 双向流RPC,其中双方使用读写流发送一系列消息。这两个流独立运行,因此客户端和服务器可以按照他们喜欢的任何顺序进行读写:例如,服务器可以等待接收到所有客户端消息后再写响应,或者可以交替地读一条消息然后写一条消息,或者其他读写组合。每个流中消息的顺序都会保留下来。可以通过在请求和响应之前放置stream关键字来指定这种类型的方法。

    // Accepts a stream of RouteNotes sent while a route is being traversed,
    // while receiving other RouteNotes (e.g. from other users).
    rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
    

Our .proto file also contains protocol buffer message type definitions for all the request and response types used in our service methods - for example, here’s the Point message type:

proto文件还包含我们的服务方法中使用的所有请求和响应类型的协议缓冲区消息类型定义,例如,以下是Point消息类型:

// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

Generating client and server code

正在生成客户端和服务器代码

Next we need to generate the gRPC client and server interfaces from our .proto service definition. We do this using the protocol buffer compiler protoc with a special gRPC C++ plugin.

接下来,我们需要从.proto服务定义中生成gRPC客户端和服务器接口。我们使用协议缓冲区编译器协议和一个特殊的gRPC C++插件来实现这一点。

For simplicity, we’ve provided a CMakeLists.txt that runs protoc for you with the appropriate plugin, input, and output (if you want to run this yourself, make sure you’ve installed protoc and followed the gRPC code installation instructions first):

​为了简单起见,我们提供了一个CMakeLists.txt,它通过适当的插件、输入和输出为运行protoc(如果您想自己运行,请确保已经安装了protoc并首先遵循gRPC代码安装说明):

$ make route_guide.grpc.pb.o

which actually runs:

它实际上运行:

$ protoc -I ../../protos --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` ../../protos/route_guide.proto
$ protoc -I ../../protos --cpp_out=. ../../protos/route_guide.proto

Running this command generates the following files in your current directory:

运行此命令会在当前目录中生成以下文件:

  • route_guide.pb.h, the header which declares your generated message classes
  • route_guide.pb.h,声明生成的消息类的头
  • route_guide.pb.cc, which contains the implementation of your message classes
  • route_guide.pb.cc,其中包含消息类的实现
  • route_guide.grpc.pb.h, the header which declares your generated service classes
  • route_guide.grpc.pb.h,声明生成的服务类的头
  • route_guide.grpc.pb.cc, which contains the implementation of your service classes
  • route_guide.grpc.pb.cc,其中包含服务类的实现

These contain:

其中包括:

  • All the protocol buffer code to populate, serialize, and retrieve our request and response message types

  • 用于填充、序列化和检索请求和响应消息类型的所有协议缓冲区代码

  • A class called RouteGuide that contains

  • 一个名为RouteGuide的类,包含
    • a remote interface type (or stub) for clients to call with the methods defined in the RouteGuide service.
    • 客户端使用RouteGuide服务中定义的方法调用的远程接口类型(或存根)。
    • two abstract interfaces for servers to implement, also with the methods defined in the RouteGuide service.
    • 两个供服务器实现的抽象接口,也使用RouteGuide服务中定义的方法。

Creating the server

正在创建服务器

First let’s look at how we create a RouteGuide server. If you’re only interested in creating gRPC clients, you can skip this section and go straight to Creating the client (though you might find it interesting anyway!).

​首先,我们来看看如何创建RouteGuide服务器。如果只对创建gRPC客户端感兴趣,可以跳过这一节,直接进入创建客户端(尽管可能会觉得很有趣!)。

There are two parts to making our RouteGuide service do its job:

让我们的RouteGuide服务发挥作用有两个部分:

  • Implementing the service interface generated from our service definition: doing the actual “work” of our service.
  • 实现由我们的服务定义生成的服务接口:完成我们服务的实际“工作”。
  • Running a gRPC server to listen for requests from clients and return the service responses.
  • 运行gRPC服务器以侦听来自客户端的请求并返回服务响应。

You can find our example RouteGuide server in examples/cpp/route_guide/route_guide_server.cc. Let’s take a closer look at how it works.

​可以在examples/cpp/route_guide/route_guide_server.cc中找到我们的示例RouteGuide服务器。让我们仔细看看它是如何工作的。

Implementing RouteGuide
实现RouteGuide

As you can see, our server has a RouteGuideImpl class that implements the generated RouteGuide::Service interface:

正如所看到的,我们的服务器有一个RouteGuideImpl类,它实现了生成的RouteGuide::Service接口:

class RouteGuideImpl final : public RouteGuide::Service {
...
}

In this case we’re implementing the synchronous version of RouteGuide, which provides our default gRPC server behaviour. It’s also possible to implement an asynchronous interface, RouteGuide::AsyncService, which allows you to further customize your server’s threading behaviour, though we won’t look at this in this tutorial.

在本例中,我们实现了RouteGuide的同步版本,它提供了我们默认的gRPC服务器行为。还可以实现异步接口RouteGuide::AsyncService,它允许进一步自定义服务器的线程行为,尽管我们在本教程中不会讨论这一点。

RouteGuideImpl implements all our service methods. Let’s look at the simplest type first, GetFeature, which just gets a Point from the client and returns the corresponding feature information from its database in a Feature.

RouteGuideImpl实现了我们所有的服务方法。让我们先来看最简单的类型GetFeature,它只从客户端获取一个Point,并从feature中的数据库中返回相应的feature信息。

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;
}

The method is passed a context object for the RPC, the client’s Point protocol buffer request, and a Feature protocol buffer to fill in with the response information. In the method we populate the Feature with the appropriate information, and then return with an OK status to tell gRPC that we’ve finished dealing with the RPC and that the Feature can be returned to the client.

向该方法传递RPC的上下文对象、客户端的Point协议缓冲区请求和Feature协议缓冲区,以填充响应信息。在该方法中,我们用适当的信息填充Feature,然后返回OK状态,告诉gRPC我们已经处理完RPC,可以将Feature返回给客户端。

Note that all service methods can (and will!) be called from multiple threads at the same time. You have to make sure that your method implementations are thread safe. In our example, feature_list_ is never changed after construction, so it is safe by design. But if feature_list_ would change during the lifetime of the service, we would need to synchronize access to this member.

请注意,所有服务方法都可以(并且将!)同时从多个线程调用。必须确保方法实现是线程安全的。在我们的示例中,feature_list_在构建后从未更改,因此在设计上是安全的。但是,如果feature_list_在服务的生命周期内发生更改,我们将需要同步对该成员的访问。

Now let’s look at something a bit more complicated - a streaming RPC. ListFeatures is a server-side streaming RPC, so we need to send back multiple Features to our client.

现在让我们来看一个更复杂的东西-流式RPC。ListFeatures是一个服务器端流式RPC,所以我们需要向客户端发回多个Features。

Status ListFeatures(ServerContext* context, const Rectangle* rectangle,
                    ServerWriter<Feature>* writer) override {
  auto lo = rectangle->lo();
  auto hi = rectangle->hi();
  long left = std::min(lo.longitude(), hi.longitude());
  long right = std::max(lo.longitude(), hi.longitude());
  long top = std::max(lo.latitude(), hi.latitude());
  long bottom = std::min(lo.latitude(), hi.latitude());
  for (const Feature& f : feature_list_) {
    if (f.location().longitude() >= left &&
        f.location().longitude() <= right &&
        f.location().latitude() >= bottom &&
        f.location().latitude() <= top) {
      writer->Write(f);
    }
  }
  return Status::OK;
}

As you can see, instead of getting simple request and response objects in our method parameters, this time we get a request object (the Rectangle in which our client wants to find Features) and a special ServerWriter object. In the method, we populate as many Feature objects as we need to return, writing them to the ServerWriter using its Write() method. Finally, as in our simple RPC, we return Status::OK to tell gRPC that we’ve finished writing responses.

正如所看到的,这次我们没有在方法参数中获得简单的请求和响应对象,而是获得了一个请求对象(我们的客户端希望在其中查找功能的矩形)和一个特殊的ServerWriter对象。在该方法中,我们填充需要返回的任意数量的Feature对象,并使用其Write()方法将它们写入ServerWriter。最后,与我们的简单RPC一样,我们返回Status::OK来告诉gRPC我们已经完成了响应的编写。

If you look at the client-side streaming method RecordRoute you’ll see it’s quite similar, except this time we get a ServerReader instead of a request object and a single response. We use the ServerReaderRead() method to repeatedly read in our client’s requests to a request object (in this case a Point) until there are no more messages: the server needs to check the return value of Read() after each call. If true, the stream is still good and it can continue reading; if false the message stream has ended.

如果看看客户端的流方法RecordRoute,会发现它非常相似,只是这次我们得到的是ServerReader,而不是请求对象和单个响应。我们使用ServerReaders Read()方法重复读取客户端对请求对象(在本例中为Point)的请求,直到不再有消息为止:服务器需要在每次调用后检查Read()的返回值。如果是真的,流仍然是好的,它可以继续阅读;如果为false,则消息流已结束。

while (stream->Read(&point)) {
  ...//process client input
}

Finally, let’s look at our bidirectional streaming RPC RouteChat().

最后,让我们看看我们的双向流RPC RouteChat()。

Status RouteChat(ServerContext* context,
                  ServerReaderWriter<RouteNote, RouteNote>* stream) override {
  RouteNote note;
  while (stream->Read(&note)) {
    std::unique_lock<std::mutex> lock(mu_);
    for (const RouteNote& n : received_notes_) {
      if (n.location().latitude() == note.location().latitude() &&
          n.location().longitude() == note.location().longitude()) {
        stream->Write(n);
      }
    }
    received_notes_.push_back(note);
  }

  return Status::OK;
}

This time we get a ServerReaderWriter that can be used to read and write messages. The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other’s messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.

这一次我们得到了一个ServerReaderWriter,它可以用于读取和写入消息。这里的读写语法与我们的客户端流和服务器流方法完全相同。尽管双方总是按照消息的编写顺序获得对方的消息,但客户端和服务器都可以按照任何顺序读取和写入消息——流完全独立地运行。

Note that since received_notes_ is an instance variable and can be accessed by multiple threads, we use a mutex lock here to guarantee exclusive access.

注意,由于received_notes_是一个实例变量,可以由多个线程访问,因此我们在这里使用互斥锁来保证独占访问。

Starting the server
启动服务器

Once we’ve implemented all our methods, we also need to start up a gRPC server so that clients can actually use our service. The following snippet shows how we do this for our RouteGuide service:

一旦我们实现了所有的方法,我们还需要启动一个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> server(builder.BuildAndStart());
  std::cout << "Server listening on " << server_address << std::endl;
  server->Wait();
}

As you can see, we build and start our server using a ServerBuilder. To do this, we:

正如所看到的,我们使用ServerBuilder构建并启动服务器。为此,我们:

1.Create an instance of our service implementation class RouteGuideImpl.

1.创建我们的服务实现类RouteGuideImpl的实例。

2.Create an instance of the factory ServerBuilder class.

2.创建一个工厂ServerBuilder类的实例。

3.Specify the address and port we want to use to listen for client requests using the builder’s AddListeningPort() method.

3.使用构建器的AddListeningPort()方法指定要用于侦听客户端请求的地址和端口。

4.Register our service implementation with the builder.

4.向构建器注册我们的服务实现。

5.Call BuildAndStart() on the builder to create and start an RPC server for our service.

5.在构建器上调用BuildAndStart(),为我们的服务创建并启动RPC服务器。

6.Call Wait() on the server to do a blocking wait until process is killed or Shutdown() is called.

6.在服务器上调用Wait()进行阻塞等待,直到进程被终止或调用Shutdown()。

Creating the client

创建客户端

In this section, we’ll look at creating a C++ client for our RouteGuide service. You can see our complete example client code in examples/cpp/route_guide/route_guide_client.cc.

​在本节中,我们将研究为RouteGuide服务创建一个C++客户端。可以在examples/cpp/route_guide/route_guide_client.cc中看到我们完整的示例客户端代码。

Creating a stub
创建存根

To call service methods, we first need to create a stub.

要调用服务方法,我们首先需要创建一个存根。

First we need to create a gRPC channel for our stub, specifying the server address and port we want to connect to - in our case we’ll use no SSL:

首先,我们需要为存根创建一个gRPC通道,指定我们想要连接的服务器地址和端口——在我们的情况下,我们将不使用SSL:

grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
Note

In order to set additional options for the channel, use the grpc::CreateCustomChannel() api with any special channel arguments - grpc::ChannelArguments.

为了设置通道的其他选项,请将grpc::CreateCustomChannel()api与任何特殊通道参数一起使用-grpc::ChannelArguments。

Now we can use the channel to create our stub using the NewStub method provided in the RouteGuide class we generated from our .proto.

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

public:
 RouteGuideClient(std::shared_ptr<ChannelInterface> channel,
                  const std::string& db)
     : stub_(RouteGuide::NewStub(channel)) {
   ...
 }
Calling service methods
调用服务方法

Now let’s look at how we call our service methods. Note that in this tutorial we’re calling the blocking/synchronous versions of each method: this means that the RPC call waits for the server to respond, and will either return a response or raise an exception.

现在让我们来看看我们如何调用我们的服务方法。请注意,在本教程中,我们将调用每个方法的阻塞/同步版本:这意味着RPC调用将等待服务器响应,并将返回响应或引发异常。

Simple RPC
简单RPC

Calling the simple RPC GetFeature is nearly as straightforward as calling a local method.

调用简单的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);
  ...
}

As you can see, we create and populate a request protocol buffer object (in our case Point), and create a response protocol buffer object for the server to fill in. We also create a ClientContext object for our call - you can optionally set RPC configuration values on this object, such as deadlines, though for now we’ll use the default settings. Note that you cannot reuse this object between calls. Finally, we call the method on the stub, passing it the context, request, and response. If the method returns OK, then we can read the response information from the server from our response object.

正如你所看到的,我们创建并填充了一个请求协议缓冲区对象(在我们的例子中是Point),并创建了一个响应协议缓冲区,供服务器填写。我们还为我们的调用创建了ClientContext对象-你可以选择在这个对象上设置RPC配置值,例如截止日期,但目前我们将使用默认设置。请注意,不能在调用之间重用此对象。最后,我们调用存根上的方法,将上下文、请求和响应传递给它。如果方法返回OK,那么我们可以从响应对象中读取服务器的响应信息。

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

Now let’s look at our streaming methods. If you’ve already read Creating the server some of this may look very familiar - streaming RPCs are implemented in a similar way on both sides. Here’s where we call the server-side streaming method ListFeatures, which returns a stream of geographical Features:

​现在让我们来看看我们的流方法。如果已经阅读过创建服务器,其中一些内容可能看起来非常熟悉——流式RPC在双方都以类似的方式实现。以下是我们调用服务器端流媒体方法ListFeatures的地方,该方法返回地理特征流:

std::unique_ptr<ClientReader<Feature> > reader(
    stub_->ListFeatures(&context, rect));
while (reader->Read(&feature)) {
  std::cout << "Found feature called "
            << feature.name() << " at "
            << feature.location().latitude()/kCoordFactor_ << ", "
            << feature.location().longitude()/kCoordFactor_ << std::endl;
}
Status status = reader->Finish();

Instead of passing the method a context, request, and response, we pass it a context and request and get a ClientReader object back. The client can use the ClientReader to read the server’s responses. We use the ClientReaderRead() method to repeatedly read in the server’s responses to a response protocol buffer object (in this case a Feature) until there are no more messages: the client needs to check the return value of Read() after each call. If true, the stream is still good and it can continue reading; if false the message stream has ended. Finally, we call Finish() on the stream to complete the call and get our RPC status.

我们没有向方法传递上下文、请求和响应,而是向它传递上下文和请求,并返回ClientReader对象。客户端可以使用ClientReader读取服务器的响应。我们使用ClientReaders Read()方法重复读取服务器对响应协议缓冲区对象(在本例中为Feature)的响应,直到不再有消息为止:客户端需要在每次调用后检查Read()的返回值。如果是真的,流仍然是好的,它可以继续阅读;如果为false,则消息流已结束。最后,我们在流上调用Finish()来完成调用并获取RPC状态。

The client-side streaming method RecordRoute is similar, except there we pass the method a context and response object and get back a ClientWriter.

客户端流式传输方法RecordRoute类似,只是我们向该方法传递一个上下文和响应对象,并返回一个ClientWriter。

std::unique_ptr<ClientWriter<Point> > writer(
    stub_->RecordRoute(&context, &stats));
for (int i = 0; i < kPoints; i++) {
  const Feature& f = feature_list_[feature_distribution(generator)];
  std::cout << "Visiting point "
            << f.location().latitude()/kCoordFactor_ << ", "
            << f.location().longitude()/kCoordFactor_ << std::endl;
  if (!writer->Write(f.location())) {
    // Broken stream.
    break;
  }
  std::this_thread::sleep_for(std::chrono::milliseconds(
      delay_distribution(generator)));
}
writer->WritesDone();
Status status = writer->Finish();
if (status.IsOk()) {
  std::cout << "Finished trip with " << stats.point_count() << " points\n"
            << "Passed " << stats.feature_count() << " features\n"
            << "Travelled " << stats.distance() << " meters\n"
            << "It took " << stats.elapsed_time() << " seconds"
            << std::endl;
} else {
  std::cout << "RecordRoute rpc failed." << std::endl;
}

Once we’ve finished writing our client’s requests to the stream using Write(), we need to call WritesDone() on the stream to let gRPC know that we’ve finished writing, then Finish() to complete the call and get our RPC status. If the status is OK, our response object that we initially passed to RecordRoute() will be populated with the server’s response.

使用Write()将客户端的请求写入流后,我们需要对流调用WritesDone(),让gRPC知道我们已经完成了写入,然后使用Finish()完成调用并获取RPC状态。如果状态为OK,我们最初传递给RecordRoute()的响应对象将填充服务器的响应。

Finally, let’s look at our bidirectional streaming RPC RouteChat(). In this case, we just pass a context to the method and get back a ClientReaderWriter, which we can use to both write and read messages.

最后,让我们看看我们的双向流RPC RouteChat()。在这种情况下,我们只需向方法传递一个上下文,然后返回一个ClientReaderWriter,我们可以使用它来写入和读取消息。

std::shared_ptr<ClientReaderWriter<RouteNote, RouteNote> > stream(
    stub_->RouteChat(&context));

The syntax for reading and writing here is exactly the same as for our client-streaming and server-streaming methods. Although each side will always get the other’s messages in the order they were written, both the client and server can read and write in any order — the streams operate completely independently.

这里的读写语法与我们的客户端流和服务器流方法完全相同。尽管双方总是按照消息的编写顺序获得对方的消息,但客户端和服务器都可以按照任何顺序读取和写入消息——流完全独立地运行。

Try it out!

试试看!

Build the client and server:

构建客户端和服务器:

$ make

Run the server:

运行服务器:

$ ./route_guide_server --db_path=path/to/route_guide_db.json

From a different terminal, run the client:

从另一个终端运行客户端:

$ ./route_guide_client --db_path=path/to/route_guide_db.json

Last modified April 26, 2023: Update basics.md (#1104) (2ce4f4d)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值