用于昂贵请求的请求聚合器/中间层设计模式

Request Aggregator / Middle-tier design pattern for costly requests

我正在开发一个程序,该程序将具有多个线程,需要来自可以处理请求的 Web 服务的信息,例如: "Give me [Var1, Var2, Var3] for [Object1, Object2, ... Object20]"

并且得到的回复将给我一个,在这种情况下,20 节点 XML(每个对象一个),每个节点有 3 个子节点(每个变量一个)。

我面临的挑战是,对这个 Web 服务发出的每个请求都会花费组织的钱,无论是 1 个对象的 1 个变量还是 20 个对象的 20 个变量,成本都是一样的。

所以,既然如此,我正在寻找一种架构:

  1. 在需要数据时在每个线程上创建一个请求
  2. 有一个中间层 "aggregator" 来获取所有请求
  3. 一旦聚合了 X 个请求(或达到了时间限制),中间层将执行 Web 服务的单个请求
  4. 中间层接收来自网络服务的回复
  5. 中间层将信息路由回等待对象

目前,我的想法是使用像 NetMQ 这样的库,中间层作为服务器,每个线程作为轮询器,但我陷入了实际的实现,在开始之前离兔子洞太远了,我希望已经有一个设计模式/库可以比我想象的更有效地做到这一点。

请理解我是菜鸟,因此,非常感谢任何帮助/指导!

谢谢!!!

我觉得你使用队列的想法不错。

这是您问题的一种可能解决方案,我相信还有无数其他解决方案可以满足您的需求。

  1. 有一个 "publish queue" (PQ) 和一个 "consume queue" (CQ)
  2. 客户端订阅 CQ,MT 订阅 PQ
  3. 客户端将请求发布到 PQ
  4. MT 监听 PQ,聚合请求并分派到一个线程中的农场
  5. 一旦结果返回,此线程将结果分成 req/res 对
  6. 然后将 req/res 对发布到 CQ
  7. 每个客户端选择正确的消息并处理它

长(呃)版本:

让你的 "middle tier" 监听队列(客户端向其发布消息)并聚合请求,直到 N 个请求通过或 X 时间过去。

准备就绪后,将聚合请求卸载到线程以调用您的服务器场并获取结果。当您需要将此反馈给客户时,很可能会出现更大的问题。

为此,您可能需要所有客户都订阅的另一个队列,一旦您的结果批次从服务器场准备好(例如 XML 中的 20 个响应),调用服务器场的线程将分离XML 结果进入他们对应的 request/response 对并发布到此队列。每个客户端都需要从队列中选择正确的 request/response 对并进行处理。

这不是传统意义上的 Web 服务,因为等待时间可能非常长,而且您不想保持连接,这就是我建议排队的原因。

您还可以让您的消费者队列基于主题,这意味着您只将 req/res 对发布给请求它的消费者而不广播它(因此客户端不必"pick the correct req/res"。会根据题目名称处理)。几乎所有的队列都支持这个。

概览

从架构的角度来看,您刚刚勾画出一个解决问题的好方法:

  1. 在请求应用程序和远程 Web 服务之间插入代理
  2. 在代理中,将请求放入请求队列,直到至少发生以下事件之一
    1. 请求队列达到给定长度
    2. 请求队列中最早的请求达到一定年龄
  3. 将请求队列中的所有请求集中在一个请求中,删除重复的对象或属性
  4. 将此请求发送到远程网络服务
  5. 将请求移入(等待)响应队列
  6. 等待响应直到出现以下情况之一
    1. 响应队列中最早的请求达到一定年龄(超时)
    2. 收到回复
  7. 获取响应(如果适用)并将其映射到响应队列中的相应请求
  8. 回答响应队列中所有有答案的请求
  9. 为早于超时限制的所有请求发送超时错误
  10. 从响应队列中删除所有已回答的请求

技术

您可能找不到完全符合您要求的现成产品或框架。但是您可以使用多种框架/架构模式来构建解决方案。

C#:RX 和 LINQ

当你想使用 C# 时,你可以使用 reactive extensions 来获得正确的时间和分组。

然后您可以使用 LINQ 来 select 请求中的属性来构建响应,并 select 响应队列中与特定部分匹配的请求响应或超时。

Scala/Java:阿卡

您可以使用多个参与者将解决方案建模为参与者系统:

  1. 作为请求网关的参与者
  2. 持有请求队列的演员
  3. 参与者向远程 Web 服务发送请求并取回响应
  4. 持有响应队列的参与者
  5. 演员发出响应或超时

Actor 系统可以轻松处理并发并以可测试的方式分离关注点。

使用 Scala 时,您可以使用它的 "monadic" 集合 API (filter, map, flatMap) 来做与C# 方法中的 LINQ。

当您想测试单个元素时,actor 方法非常有用。这非常简单 to test each actor individually,无需模拟整个工作流程。

Erlang/Elixir:演员系统

这类似于 Akka 方法,只是使用了不同的(函数式!)语言。 Erlang / Elixir 对分布式 actor 系统有很多支持,所以当你需要一个超稳定或可扩展的解决方案时,你应该研究一下这个。

NetMQ/ZeroMQ

这可能级别太低,只涉及很少的基础设施。当你使用 actor 系统时,你可以尝试引入 NetMQ / ZeroMQ 作为传输系统。