【发布时间】:2018-08-25 09:52:22
【问题描述】:
我的团队正在设计一个具有微服务架构的可扩展解决方案,并计划使用 gRPC 作为层之间的传输通信。我们决定使用异步 grpc 模型。如果我扩展 RPC 方法的数量,example(greeter_async_server.cc) 提供的设计似乎不可行,因为我必须为每个 RPC 方法创建一个新类,并像这样在HandleRpcs() 中创建它们的对象.
Pastebin(简短示例代码)。
void HandleRpcs() {
new CallDataForRPC1(&service_, cq_.get());
new CallDataForRPC2(&service_, cq_.get());
new CallDataForRPC3(&service, cq_.get());
// so on...
}
它将被硬编码,所有的灵活性都将丢失。
我有大约 300-400 个 RPC 方法来实现,而拥有 300-400 个类会很麻烦当我必须处理超过 100K 的 RPC 请求/秒时,这个解决方案是非常糟糕的设计。我无法承受在每个请求上以这种方式创建对象的开销。有人可以为我提供一个解决方法吗? async grpc c++ 不能像它的同步伴侣那样简单吗?
编辑:为了让情况更清晰,对于那些可能难以掌握这个异步示例流程的人,我写的是我目前所理解的内容,请如果某处有错,请纠正我。
在异步 grpc 中,每次我们必须将唯一标记与完成队列绑定,这样当我们轮询时,服务器可以在特定的 RPC 将被客户端命中时将其返回给我们,我们推断来自返回的关于调用类型的唯一标签。
service_->RequestRPC2(&ctx_, &request_, &responder_, cq_, cq_,this); 这里我们使用当前对象的地址作为唯一标签。这就像在完成队列上注册我们的 RPC 调用。然后我们在HandleRPCs() 中向下轮询以查看客户端是否命中了 RPC,如果是,则cq_->Next(&tag, &OK) 将填充标签。轮询代码sn -p:
while (true) {
GPR_ASSERT(cq_->Next(&tag, &ok));
GPR_ASSERT(ok);
static_cast<CallData*>(tag)->Proceed();
}
因为,我们注册到队列中的唯一标签是 CallData 对象的地址,所以我们可以调用Proceed()。这对于一个逻辑在Proceed() 中的 RPC 来说很好。但是每次都有更多的 RPC,我们会将它们全部放在 CallData 中,然后在轮询时,我们将调用唯一的一个 Proceed(),它将包含(比如)RPC1(postgres 调用)、RPC2(mongodb 调用)的逻辑), .. 很快。这就像在一个函数中编写我的所有程序一样。因此,为了避免这种情况,我使用了 GenericCallData 类和 virtual void Proceed() 并从中创建了派生类,每个 RPC 一个类,在它们自己的 Proceed() 中具有自己的逻辑。这是一个可行的解决方案,但我想避免编写很多类。
我尝试的另一个解决方案是将所有 RPC-function-logics 排除在 proceed() 之外并放入它们自己的函数中,并维护一个全局 std::map<long, std::function</*some params*/>> 。因此,每当我将带有唯一标签的 RPC 注册到队列中时,我都会存储其相应的逻辑函数(我肯定会将其硬编码到语句中并绑定所有需要的参数),然后将唯一标签作为键。在轮询时,当我得到&tag 时,我会在地图中查找此键并调用相应的保存函数。现在,还有一个障碍,我必须在函数逻辑中执行此操作:
// pseudo code
void function(reply, responder, context, service)
{
// register this RPC with another unique tag so to serve new incoming request of the same type on the completion queue
service_->RequestRPC1(/*params*/, new_unique_id);
// now again save this new_unique_id and current function into the map, so when tag will be returned we can do lookup
map.emplace(new_unique_id, function);
// now you're free to do your logic
// do your logic
}
您看到了,代码现在已经传播到另一个模块中,并且它是基于 RPC 的。 希望它能清除情况。 我想如果有人能以更简单的方式实现这种类型的服务器。
【问题讨论】:
-
您总是可以使用函数指针作为模板参数(或只是构造函数参数)创建一个模板类,并将它们全部存储在一个向量中。然后添加新方法只是实现一个方法和
methods.add(new Template<newMethod>()); -
@KrzysztofSkowronek,我已经用更多解释编辑了这个问题。您能否为您提出的解决方案提供一个代码 sn-p?
-
如果我理解正确,主要的麻烦是所有那些 RPC1、RPC2、.. 等类都是完全相同的,唯一的区别是 id
// do your logic行。如果是这样,则使用函数指针作为构造函数参数创建类 RPC,并在// do your logic行中调用该函数。如果我的理解是错误的,我想我不能帮助你:( -
这是一个有效的建议,看起来比我做的麻烦更干净。
标签: c++ c++11 asynchronous microservices grpc