【发布时间】:2021-02-14 17:22:08
【问题描述】:
我正在尝试学习如何在 C++ 中异步使用 gRPC。在https://github.com/grpc/grpc/blob/v1.33.1/examples/cpp/helloworld/greeter_async_client.cc查看客户端示例
除非我有误解,否则我看不到任何异步的演示。 RPC 调用只有一个,它在主线程上阻塞,直到服务器处理它并将结果发回。
我需要做的是创建一个可以进行一次 RPC 调用的客户端,然后在等待第一个结果从服务器返回时启动另一个。
我不知道该怎么做。
有没有人有一个可行的例子,或者任何人都可以描述如何实际使用 gRPC 异步?
他们的示例代码:
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#include <iostream>
#include <memory>
#include <string>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Channel;
using grpc::ClientAsyncResponseReader;
using grpc::ClientContext;
using grpc::CompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class GreeterClient {
public:
explicit GreeterClient(std::shared_ptr<Channel> channel)
: stub_(Greeter::NewStub(channel)) {}
// Assembles the client's payload, sends it and presents the response back
// from the server.
std::string SayHello(const std::string& user) {
// Data we are sending to the server.
HelloRequest request;
request.set_name(user);
// Container for the data we expect from the server.
HelloReply reply;
// Context for the client. It could be used to convey extra information to
// the server and/or tweak certain RPC behaviors.
ClientContext context;
// The producer-consumer queue we use to communicate asynchronously with the
// gRPC runtime.
CompletionQueue cq;
// Storage for the status of the RPC upon completion.
Status status;
// stub_->PrepareAsyncSayHello() creates an RPC object, returning
// an instance to store in "call" but does not actually start the RPC
// Because we are using the asynchronous API, we need to hold on to
// the "call" instance in order to get updates on the ongoing RPC.
std::unique_ptr<ClientAsyncResponseReader<HelloReply> > rpc(
stub_->PrepareAsyncSayHello(&context, request, &cq));
// StartCall initiates the RPC call
rpc->StartCall();
// Request that, upon completion of the RPC, "reply" be updated with the
// server's response; "status" with the indication of whether the operation
// was successful. Tag the request with the integer 1.
rpc->Finish(&reply, &status, (void*)1);
void* got_tag;
bool ok = false;
// Block until the next result is available in the completion queue "cq".
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or the cq_ is shutting down.
GPR_ASSERT(cq.Next(&got_tag, &ok));
// Verify that the result from "cq" corresponds, by its tag, our previous
// request.
GPR_ASSERT(got_tag == (void*)1);
// ... and that the request was completed successfully. Note that "ok"
// corresponds solely to the request for updates introduced by Finish().
GPR_ASSERT(ok);
// Act upon the status of the actual RPC.
if (status.ok()) {
return reply.message();
} else {
return "RPC failed";
}
}
private:
// Out of the passed in Channel comes the stub, stored here, our view of the
// server's exposed services.
std::unique_ptr<Greeter::Stub> stub_;
};
int main(int argc, char** argv) {
// Instantiate the client. It requires a channel, out of which the actual RPCs
// are created. This channel models a connection to an endpoint (in this case,
// localhost at port 50051). We indicate that the channel isn't authenticated
// (use of InsecureChannelCredentials()).
GreeterClient greeter(grpc::CreateChannel(
"localhost:50051", grpc::InsecureChannelCredentials()));
std::string user("world");
std::string reply = greeter.SayHello(user); // The actual RPC call!
std::cout << "Greeter received: " << reply << std::endl;
return 0;
}
【问题讨论】:
-
这只是一个例子(也许不是一个很好的例子)。你看到它在哪里调用
StartCall,然后在下一行它调用Finish?在那里,您可以在服务器运行期间随心所欲地做任何事情。最终你调用Finish,如果 RPC 完成,你会立即返回,但如果 RPC 没有完成,你会在那儿阻塞直到它完成。还有一些方法可以在不阻塞的情况下进行轮询,或者获取回调,但是这个例子没有展示它们。事实上,gRPC 异步的例子很少——你真的需要看看——试试 gRPC 测试源。 -
Here's an example of a test that does nonblocking async client calls 和 here's another。两者都不是一个理想的例子——也没有任何评论告诉你发生了什么! - 但即便如此,这可能会帮助您解释 API 文档(当然,这些都不在教程中)。
-
P.S.这只是我的解释,但我认为他们不太关心异步 clients。异步服务器,是的,但是文档几乎没有更好,您的设计选择更加多样化 - 并且更难以理解权衡 - 当然,对于所有这些,边缘的发现和调试案件由你决定......
-
您好,如果我的回答帮助并回答了问题,您能否将其标记为正确?