用于昂贵请求的请求聚合器/中间层设计模式
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 个变量,成本都是一样的。
所以,既然如此,我正在寻找一种架构:
- 在需要数据时在每个线程上创建一个请求
- 有一个中间层 "aggregator" 来获取所有请求
- 一旦聚合了 X 个请求(或达到了时间限制),中间层将执行 Web 服务的单个请求
- 中间层接收来自网络服务的回复
- 中间层将信息路由回等待对象
目前,我的想法是使用像 NetMQ 这样的库,中间层作为服务器,每个线程作为轮询器,但我陷入了实际的实现,在开始之前离兔子洞太远了,我希望已经有一个设计模式/库可以比我想象的更有效地做到这一点。
请理解我是菜鸟,因此,非常感谢任何帮助/指导!
谢谢!!!
我觉得你使用队列的想法不错。
这是您问题的一种可能解决方案,我相信还有无数其他解决方案可以满足您的需求。
- 有一个 "publish queue" (PQ) 和一个 "consume queue" (CQ)
- 客户端订阅 CQ,MT 订阅 PQ
- 客户端将请求发布到 PQ
- MT 监听 PQ,聚合请求并分派到一个线程中的农场
- 一旦结果返回,此线程将结果分成 req/res 对
- 然后将 req/res 对发布到 CQ
- 每个客户端选择正确的消息并处理它
长(呃)版本:
让你的 "middle tier" 监听队列(客户端向其发布消息)并聚合请求,直到 N 个请求通过或 X 时间过去。
准备就绪后,将聚合请求卸载到线程以调用您的服务器场并获取结果。当您需要将此反馈给客户时,很可能会出现更大的问题。
为此,您可能需要所有客户都订阅的另一个队列,一旦您的结果批次从服务器场准备好(例如 XML 中的 20 个响应),调用服务器场的线程将分离XML 结果进入他们对应的 request/response 对并发布到此队列。每个客户端都需要从队列中选择正确的 request/response 对并进行处理。
这不是传统意义上的 Web 服务,因为等待时间可能非常长,而且您不想保持连接,这就是我建议排队的原因。
您还可以让您的消费者队列基于主题,这意味着您只将 req/res 对发布给请求它的消费者而不广播它(因此客户端不必"pick the correct req/res"。会根据题目名称处理)。几乎所有的队列都支持这个。
概览
从架构的角度来看,您刚刚勾画出一个解决问题的好方法:
- 在请求应用程序和远程 Web 服务之间插入代理
- 在代理中,将请求放入请求队列,直到至少发生以下事件之一
- 请求队列达到给定长度
- 请求队列中最早的请求达到一定年龄
- 将请求队列中的所有请求集中在一个请求中,删除重复的对象或属性
- 将此请求发送到远程网络服务
- 将请求移入(等待)响应队列
- 等待响应直到出现以下情况之一
- 响应队列中最早的请求达到一定年龄(超时)
- 收到回复
- 获取响应(如果适用)并将其映射到响应队列中的相应请求
- 回答响应队列中所有有答案的请求
- 为早于超时限制的所有请求发送超时错误
- 从响应队列中删除所有已回答的请求
技术
您可能找不到完全符合您要求的现成产品或框架。但是您可以使用多种框架/架构模式来构建解决方案。
C#:RX 和 LINQ
当你想使用 C# 时,你可以使用 reactive extensions 来获得正确的时间和分组。
然后您可以使用 LINQ 来 select 请求中的属性来构建响应,并 select 响应队列中与特定部分匹配的请求响应或超时。
Scala/Java:阿卡
您可以使用多个参与者将解决方案建模为参与者系统:
- 作为请求网关的参与者
- 持有请求队列的演员
- 参与者向远程 Web 服务发送请求并取回响应
- 持有响应队列的参与者
- 演员发出响应或超时
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 作为传输系统。
我正在开发一个程序,该程序将具有多个线程,需要来自可以处理请求的 Web 服务的信息,例如:
"Give me [Var1, Var2, Var3]
for [Object1, Object2, ... Object20]
"
并且得到的回复将给我一个,在这种情况下,20 节点 XML(每个对象一个),每个节点有 3 个子节点(每个变量一个)。
我面临的挑战是,对这个 Web 服务发出的每个请求都会花费组织的钱,无论是 1 个对象的 1 个变量还是 20 个对象的 20 个变量,成本都是一样的。
所以,既然如此,我正在寻找一种架构:
- 在需要数据时在每个线程上创建一个请求
- 有一个中间层 "aggregator" 来获取所有请求
- 一旦聚合了 X 个请求(或达到了时间限制),中间层将执行 Web 服务的单个请求
- 中间层接收来自网络服务的回复
- 中间层将信息路由回等待对象
目前,我的想法是使用像 NetMQ 这样的库,中间层作为服务器,每个线程作为轮询器,但我陷入了实际的实现,在开始之前离兔子洞太远了,我希望已经有一个设计模式/库可以比我想象的更有效地做到这一点。
请理解我是菜鸟,因此,非常感谢任何帮助/指导!
谢谢!!!
我觉得你使用队列的想法不错。
这是您问题的一种可能解决方案,我相信还有无数其他解决方案可以满足您的需求。
- 有一个 "publish queue" (PQ) 和一个 "consume queue" (CQ)
- 客户端订阅 CQ,MT 订阅 PQ
- 客户端将请求发布到 PQ
- MT 监听 PQ,聚合请求并分派到一个线程中的农场
- 一旦结果返回,此线程将结果分成 req/res 对
- 然后将 req/res 对发布到 CQ
- 每个客户端选择正确的消息并处理它
长(呃)版本:
让你的 "middle tier" 监听队列(客户端向其发布消息)并聚合请求,直到 N 个请求通过或 X 时间过去。
准备就绪后,将聚合请求卸载到线程以调用您的服务器场并获取结果。当您需要将此反馈给客户时,很可能会出现更大的问题。
为此,您可能需要所有客户都订阅的另一个队列,一旦您的结果批次从服务器场准备好(例如 XML 中的 20 个响应),调用服务器场的线程将分离XML 结果进入他们对应的 request/response 对并发布到此队列。每个客户端都需要从队列中选择正确的 request/response 对并进行处理。
这不是传统意义上的 Web 服务,因为等待时间可能非常长,而且您不想保持连接,这就是我建议排队的原因。
您还可以让您的消费者队列基于主题,这意味着您只将 req/res 对发布给请求它的消费者而不广播它(因此客户端不必"pick the correct req/res"。会根据题目名称处理)。几乎所有的队列都支持这个。
概览
从架构的角度来看,您刚刚勾画出一个解决问题的好方法:
- 在请求应用程序和远程 Web 服务之间插入代理
- 在代理中,将请求放入请求队列,直到至少发生以下事件之一
- 请求队列达到给定长度
- 请求队列中最早的请求达到一定年龄
- 将请求队列中的所有请求集中在一个请求中,删除重复的对象或属性
- 将此请求发送到远程网络服务
- 将请求移入(等待)响应队列
- 等待响应直到出现以下情况之一
- 响应队列中最早的请求达到一定年龄(超时)
- 收到回复
- 获取响应(如果适用)并将其映射到响应队列中的相应请求
- 回答响应队列中所有有答案的请求
- 为早于超时限制的所有请求发送超时错误
- 从响应队列中删除所有已回答的请求
技术
您可能找不到完全符合您要求的现成产品或框架。但是您可以使用多种框架/架构模式来构建解决方案。
C#:RX 和 LINQ
当你想使用 C# 时,你可以使用 reactive extensions 来获得正确的时间和分组。
然后您可以使用 LINQ 来 select 请求中的属性来构建响应,并 select 响应队列中与特定部分匹配的请求响应或超时。
Scala/Java:阿卡
您可以使用多个参与者将解决方案建模为参与者系统:
- 作为请求网关的参与者
- 持有请求队列的演员
- 参与者向远程 Web 服务发送请求并取回响应
- 持有响应队列的参与者
- 演员发出响应或超时
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 作为传输系统。