关于如何实现多个异步请求,是否有更好的示例。
function commonCharacterCount(s1: string, s2: string): number {
let count = 0;
let s_1 = s1.split("");
let s_2 = s2.split("");
for (let i = 0; i < s_1.length; i++) {
for (let j = 0; j < s_2.length; j++) {
if (s_1[i] == s_2[j]) {
count++;
s_2.splice(j, 1);
break;
}
}
}
return(count);
}
我正在使用下面的异步模式来处理 // The greeting service definition.
service Greeter {
// Sends a greeting
rpc ListSayHello1 (HelloRequest1) returns (stream HelloReply1) {}
rpc ListSayHello2 (HelloRequest2) returns (stream HelloReply2) {}
}
// The request message containing the user's name.
message HelloRequest1 {
string name = 1;
}
message HelloRequest2 {
string name = 1;
}
// The response message containing the greetings
message HelloReply1 {
string message = 1;
}
message HelloReply2 {
string message = 1;
}
,但是现在我想添加对SayHello1
的支持。应该采取什么方法?
SayHello2
在 new CallData(&service_, cq_.get(), *this);
void* tag; // uniquely identifies a request.
bool ok;
while (true) {
// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
中,我这样做
CallData
答案 0 :(得分:1)
没有必要创建额外的完成队列。我们只需要知道如何处理完成队列返回的内容。我们可以通过创建一个公共基类来解决这个问题,它能够完成必要的功能:
class CallDataBase
{
protected:
virtual void WaitForRequest() = 0;
virtual void HandleRequest() = 0;
public:
virtual void Proceed() = 0;
CallDataBase() {}
};
CallDataBase 的每个专业都知道如何 Proceed、WaitForRequest 和 HandleRequest。其中一些对于所有请求都是通用的,因此使用模板化类很方便:
template < class RequestType, class ReplyType>
class CallDataT : CallDataBase
{
protected:
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_;
Greeter::AsyncService* service_;
ServerCompletionQueue* completionQueue_;
RequestType request_;
ReplyType reply_;
ServerAsyncResponseWriter<ReplyType> responder_;
ServerContext serverContext_;
// When we handle a request of this type, we need to tell
// the completion queue to wait for new requests of the same type.
virtual void AddNextToCompletionQueue() = 0;
public:
CallDataT(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) :
status_(CREATE),
service_(service),
completionQueue_(completionQueue),
responder_(&serverContext_)
{
}
public:
virtual void Proceed() override
{
if (status_ == CREATE)
{
status_ = PROCESS;
WaitForRequest();
}
else if (status_ == PROCESS)
{
AddNextToCompletionQueue();
HandleRequest();
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
}
else
{
// We're done! Self-destruct!
if (status_ != FINISH)
{
// Log some error message
}
delete this;
}
}
};
最后,消息类型的实际实现:
class CallDataHello : CallDataT<HelloRequest, HelloReply>
{
public:
CallDataHello(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) : CallDataT(service, completionQueue)
{
Proceed();
}
protected:
virtual void AddNextToCompletionQueue() override
{
new CallDataHello(service_, completionQueue_);
}
virtual void WaitForRequest() override
{
service_->RequestSayHello(&serverContext_, &request_, &responder_, completionQueue_, completionQueue_, this);
}
virtual void HandleRequest() override
{
reply_.set_message(std::string("Hello ") + request_.name());
}
};
class CallDataHelloAgain : CallDataT<HelloAgainRequest, HelloAgainReply>
{
public:
CallDataHelloAgain(Greeter::AsyncService* service, ServerCompletionQueue* completionQueue) : CallDataT(service, completionQueue)
{
Proceed();
}
protected:
virtual void AddNextToCompletionQueue() override
{
new CallDataHelloAgain(service_, completionQueue_);
}
virtual void WaitForRequest() override
{
service_->RequestSayHelloAgain(&serverContext_, &request_, &responder_, completionQueue_, completionQueue_, this);
}
virtual void HandleRequest() override
{
reply_.set_message(std::string("Hello again ") + request_.name());
}
};
最后,在 GRPC 服务器实现中,我们可以统一处理不同的请求:
void HandleRpcs()
{
new CallDataHello(&service_, completionQueue.get());
new CallDataHelloAgain(engine_, &service_, completionQueue.get());
void* tag;
bool ok;
while (true) {
bool ret = completionQueue->Next(&tag, &ok);
if (ok == false || ret == false)
{
return;
}
static_cast<CallDataBase*>(tag)->Proceed();
}
}
这是有效的,因为我们添加到 completionQueue 的所有 CallData 都是基类 CallDataBase,所以我们可以安全地对它们调用 Proceed()。这种方法使得添加新请求相对容易。所有请求都由同一个完成队列处理。然而,所有的请求都会被串行处理,所以如果你要进行更多的并行处理,我认为你必须制作一个以上的完成队列。
答案 1 :(得分:0)
我能够弄清楚这一点。 对于每个protobuf函数,我必须创建单独的完成队列。