“重新sorting”消息在无序处理之后

我正在研究基本上是高度可用的分布式消息传递系统。 系统通过HTTP或TCP接收来自某个地方的消息,对其执行各种转换,然后将其发送到一个或多个目的地(也使用TCP / HTTP)。

系统要求发送到给定目的地的所有消息都是有序的,因为一些消息build立在以前的内容上。 这限制了我们按顺序处理消息,每条消息大约需要750ms。 所以如果有人发送给我们,例如,每250ms一条消息,我们不得不将消息排在对方之后。 这最终会在高负载下的消息处理中引入无法忍受的延迟,因为每个消息在轮到它之前可能不得不等待数百个其他消息被处理。

为了解决这个问题,我希望能够并行化我们的消息处理,而不会破坏我们发送它们的顺序的要求。

我们可以轻松地横向扩展处理。 丢失的部分是一种确保即使消息被乱序处理也将被“重新sorting”并按照接收顺序发送到目的地的方法。 我试图find实现这一目标的最佳方法。

Apache Camel有一个叫Resequencer的东西 ,它包含了一个很好的图(我没有足够的代表直接embedded)。 这正是我想要的:把乱序信息放在一边的东西。

但是,我不希望它是用Java编写的,我需要的解决scheme是高度可用的(即抵制典型的系统故障,如崩溃或系统重启),我不认为Apache Camel提供。

我们的应用程序是用Node.js编写的,使用Redis和Postgresql来进行数据持久化。 我们使用Kue库来处理消息队列。 尽pipeKue提供了优先级排队function,但function集对于上述用例来说太有限了,所以我认为我们需要一种替代技术与Kue一起重新sorting我们的消息。

我试图在网上研究这个话题,而且我也找不到像我预期的那么多的信息。 看起来像分布式架构模式的types将有文章和实现丰富,但我没有看到这么多。 search诸如“消息重新sorting”,“乱序处理”,“并行消息处理”等等的东西,大多只是放松了基于分区或主题或者什么的“有序”需求的解决scheme。 或者,他们谈论在一台机器上的并行化。 我需要一个解决scheme:

  • 可以按任意顺序同时处理多个消息。
  • 无论按照什么顺序处理,总会按照到达系统的顺序发送消息。
  • 可以从Node.js使用
  • 可以在高可用性环境下运行(即在同一消息队列上运行的多个实例不会出现不一致)。

我们目前的计划对我来说是有意义的,但在网上任何地方我都找不到。我们现在的计划是使用Redis来维护一套正在进行的和准备发送的消息,按照到达时间进行sorting。 粗略地说,它的工作原理是这样的:

  1. 当收到消息时,该消息被放置在正在进行的设置中。
  2. 当消息处理完成时,该消息被置于准备发送集合上。
  3. 只要在进行中和准备发送的集合的前面有相同的消息,就可以发送该消息并且按顺序发送。

我将编写一个小型的Node库,使用primefacesRedis事务,使用优先级队列式API实现此行为。 但是这只是我自己想出来的,所以我想知道:是否有其他技术(理想情况下使用Node / Redis堆栈,我们已经在这里)解决重新sorting无序消息的问题? 还是有其他一些术语可以用作研究的关键字吗? 谢谢你的帮助!

这是一个常见的问题 ,所以肯定有很多解决scheme可用。 这也是一个相当简单的问题,也是分布式系统领域的一个很好的学习机会。 我会build议写你自己的。

你将会遇到一些问题,比如说

2:一次交付
1:消息的保证顺序
2:一次交付

你已经find了数字1,你正在通过在redis中重新sorting来解决这个问题,这是一个很好的解决scheme。 另一个则没有解决。

看起来你的架构并不是面向容错的,所以目前,如果服务器出现故障,你需要重新启动它并继续你的生活。 当顺序处理所有的请求时,这个工作正常,因为那么你就知道什么时候崩溃了,基于最后一次成功完成的请求是什么。

你所需要的是要么找出你实际完成了什么要求,哪些失败的策略,要么就是写出一封写得很好的道歉信,在事情崩溃时发送给你的客户。

如果Redis不分片,则强烈一致。 如果单个节点崩溃,它将会失败并可能丢失所有数据,但是对于乱序数据或数据popup和移出的情况,您不会有任何问题。 因此,一个Redis节点可以保证,如果一个消息被插入到进程集中,然后被插入到完成集中,那么没有节点将在完成集中看到消息,stream程设置。

我该怎么做

使用redis似乎太多模糊了,假设消息不是很大,如果一个进程崩溃,丢失它们是可以的,并且运行它们不止一次,或者甚至同时多个单个请求的副本不是一个问题。

我build议设置一个监督服务器,接收传入的请求,将其分发给一个随机select的从服务器,存储这些响应,并在发送之前再次将它们放回原处。 你说你预计处理需要750毫秒。 如果一个从站在2秒钟内没有响应,则在0-1秒钟之内将其重新分配给另一个节点。 第一个回应是我们要使用的。 谨防重复的回应。

如果重试请求也失败,请将最大等待时间加倍。 经过5次左右的失败后,每次等待两次(或者多于一次),我们可能会有一个永久的错误,所以我们应该要求人为的干预。 这种algorithm被称为指数退避(exponential backoff),并防止请求中的突然尖峰从整个集群中取出。 不使用随机间隔,并在n秒后重试可能会导致DOS攻击每n秒,直到群集死亡,如果它得到一个足够大的负载峰值。

有很多方法可能会失败,所以确保这个系统不是唯一存储数据的地方。 然而,这可能会工作99 +%的时间,它可能至less和你现在的系统一样好,你可以在几百行代码中实现它。 只要确保您的主pipe正在使用asynchronous请求,以便您可以处理重试和超时。 JavaScript本质上是单线程的,所以这比正常情况稍微棘手,但我相信你可以做到这一点。