【发布时间】:2015-03-16 12:06:49
【问题描述】:
我有一个系统设计挑战,我想获得一些社区反馈。
基本系统结构:
[客户端] ---HTTP-POST--> [REST 服务] ---> [队列] ---> [处理器]
- [客户端] 将 json 发布到 [REST 服务] 进行处理。
- 根据请求,[Rest Services] 将数据发送到各种队列,以供以各种语言编写并在不同进程中运行的各种处理器拾取。
- 工作在每个处理器中并行处理,但仍可能需要长达 30 秒的时间来处理。处理时间是数据复杂性的函数,无法加快。
- 结果在完成后无法流式传输回客户端,因为有一个最后的后处理步骤,只有在完成所有子步骤后才能完成。
关键挑战:后处理完成后,客户要么需要:
- 在客户端等待后发送结果
- 异步通知作业已完成并传递一个 id 以请求最终结果
设计要求
我不想阻止 [REST 服务]。它需要接受传入的请求,将数据路由到适当的队列以供其他进程处理,然后立即可用于下一个传入请求。
通常我会使用演员和/或期货/承诺,因此在等待后台工作人员完成时不会阻止 [REST 服务]。这里的挑战是从事后台工作的工作人员在不同的进程/虚拟机中运行,并以各种技术堆栈编写。为了在异构系统之间传递这些消息并确保请求生命周期的完整性,正在使用持久队列(不是在内存消息传递或 RPC 中)。
最后一点考虑,为了扩展,在各个池中有一组负载平衡的 [REST 服务] 和 [处理器]。因此,由于从 [REST Service] 到 [Processor] 的消息需要通过队列异步发送(并且所有正在运行的都是单独的进程),因此无法将后台 [Processor] 中完成的工作关联回到它的原始调用 [REST Service] 实例,以便在 Promise 或 Actor 消息中返回最终处理的数据,并最终将响应传递回原始客户端。
那么,问题是,如何建立这种关联?完成所有后台处理后,我需要通过长时间等待的响应或通知将结果返回给客户端(我不想使用 UrbanAirship 之类的东西,因为大多数客户端都是浏览器或其他服务。
我希望这很清楚,如果没有,请要求澄清。
编辑:可能的解决方案 - 想法?
我想我将一个喷雾 RequestContext 传递给任何可以响应客户端的参与者(不必是接收 HTTP 请求的原始参与者)。如果是这样,我是否可以缓存 RequestContext,然后在处理完成时使用它,使用缓存的 RequestContext 将响应异步发送到适当的客户端?
【问题讨论】:
-
嗯...为您发送到处理器的所有内容生成一个 UUID,并且每当处理器向结果队列生成某些内容时,它们都会指定此 UUID。
-
@SarveshKumarSingh 是的,我们在创建请求时生成一个“作业 ID”(uuid),它包含在所有消息中,因此可以关联最终的结果数据。在立即响应中将此 id 发送回客户端会起作用,但我不希望在结果准备好之前让客户端轮询。当所有后台处理完成时,我希望原始 [REST Service] 实例上的原始调用上下文(以非阻塞方式)响应收到的 promise 或 akka 消息的结果。核心问题是如何进行这种关联?
-
您不是在寻找相关性。您正在寻找一种回应客户的方式。您可以选择诸如套接字之类的东西,也可以将最终输出发布到结果队列并让客户端从那里选择结果。
-
@SarveshKumarSingh 我们已经将最终结果发布到队列中。同样,问题是:我不希望客户必须轮询工作 ID。我想在计算最终结果时(以非阻塞方式)回复客户端。这将要求我使用 akka 消息进行响应或承诺返回 [REST 服务] 的实例,以便它可以将响应发送回调用客户端。这就是我想做的 - 我认为这需要我匹配(或关联)已完成的工作,将最终计算结果返回给调用客户端。
-
由于您要使用工人服务,我想您的工作应该需要很多时间,或者有些工作将等待工人接手。这意味着您不想以服务器接收请求并以结果响应的通常方式响应您的客户端。所以......你想要一种与客户端通信的方式,而不需要客户端向你的第一个发送请求(轮询通过继续发送请求来工作)。实现这一点的最流行的方法是 - 套接字或消息队列。所以告诉你的客户端连接到消息队列并发布到相同的队列。
标签: scala asynchronous reactive-programming playframework-2.3 spray