消息队列全面大指南 - sudhir

21-01-08 banq

消息队列基础概念的指南,以及它们如何应用于当今流行的排队系统。

在本指南中,我们将讨论:

  • 什么是消息队列及其历史记录。
  • 为什么它们有用,以及在推理时要使用哪些心理模型。
  • 交付保证了排队系统的语义(至少一次,最多一次和完全一次语义)。
  • 排序和FIFO保证以及它们如何影响排序,并行性和性能。
  • 扇出和扇入的模式:将一条消息传递到多个系统,或将消息从许多系统组合成一个。
  • 有关当今可用的许多流行系统的优缺点的说明。

 

什么是消息队列?

消息队列是在两个系统之间传输信息的一种方式。此信息(一条消息)可以是数据,元数据,信号或这三者的组合。发送和接收消息的系统可以是同一台计算机上的进程,同一应用程序的模块,可能在不同计算机或技术堆栈上运行的服务或完全不同种类的系统,例如将信息从软件传输到电子邮件或手机网络上的SMS。

每条消息的内容将完全由您的应用程序的体系结构及其用途来决定-因此,在本指南的其余部分中,我们无需关注消息中的内容-我们更关注消息的方式从其起源的系统(生产者,源,发布者或发送者)到达本应到达的系统(消费者,订户,目的地或接收器)。

 

为什么我们需要它们?

我们需要消息队列,因为没有系统存在或无法孤立地工作-所有系统都需要以它们都能理解的结构化方式与它们都可以处理的受控方式与其他系统通信。任何非平凡的流程都需要一种在流程的各个阶段之间移动信息的方法。任何工作流程都需要一种在该工作流程的各个阶段之间移动中间产品的方法。消息队列是处理此移动的好方法。有很多方法可以使用API​​调用,文件系统或许多其他对事物自然顺序的滥用来获取这些消息。但是所有这些都是消息队列的临时实现,有时我们拒绝承认我们需要这些实现。

消息队列的最简单思维模型是一个很长的管,您可以将它扔进去。您将您的消息写在一个球上,将其滚动到管子中,另一端收到某人或其他东西。此模型有很多有趣的好处,其中包括:

  • 我们不必担心接收消息的人或对象是什么–发件人不必担心要承担的责任。
  • 我们不必担心接收方何时接收消息。
  • 我们可以按照自己喜欢的速度将任意数量的消息放入管道(假设我们有一个无限长的管道)。
  • 接收者永远不会受到我们行动的影响-他们将以他们希望的任何速率拉出任意数量的消息。
  • 无论是发送还是接收方所关心的是如何的其他作品。
  • 发送方和接收方都不关心对方的容量或负载。
  • 两个系统都不关心另一个系统的位置–它们可能位于或不在同一台计算机,网络,大陆或同一星球上。

所有这些优点(甚至还不是一个详尽的清单)在软件开发中都具有非常重要的优点-它们的共同点是去耦。一个系统在职责,时间,带宽,内部工作方式,负载和地理位置方面与另一个系统是分离的。解耦是任何分布式或复杂系统中非常需要的部分-系统各部分之间的解耦越多,独立构建,测试,运行,维护和扩展它们就越容易。

大多数系统也可以与其他外部或第三方系统进行交互-如果我们构建购物网站,则可能与支付处理器进行交互,并且假设我们尝试在每次用户点击时直接与支付处理器进行通信。如果我们的系统承受重负载,那么我们还要使另一个系统承受相同的负载。反之亦然,如果我们的付款服务提供商需要向我们发送数百万条有关我们过去付款状态的信息,那么我们的系统就更好了。

现在将两个系统耦合。一个系统做出的决定和动作会对另一个系统产生重大影响,因此在做出每个决定时都必须考虑到两者的需求。将足够多的其他系统(例如物流或交付系统)添加到组合中,我们很快就会陷入瘫痪状态,从而很难做出任何决定。如果一个系统发生故障,则其他系统也有效地发生故障,而它们本身没有故障。

如果我们想将这些系统中的任何一个切换为另一个系统,例如新的付款处理器或交付系统,也会遇到麻烦。我们必须在应用程序的多个位置进行深刻的更改,构建代码以在多个提供程序之间拆分消息更加困难—我们可能需要使用比率来负载均衡它们或按地理位置拆分它们;或根据每个提供商的可用性或成本在它们之间动态切换。

消息队列提供了解决许多这些问题的解耦功能。如果我们在两个需要相互通信的系统之间建立一个队列,那么他们现在就可以继续工作而不必担心彼此—我们将针对任何系统的消息放入队列中,我们期望获得信息也可以通过另一个队列来访问我们。现在,我们有了明确的要点,可以在这些要点上添加规则或进行所需的更改,而无需系统知道或关心不同之处。

 

那么有什么收获呢?

消息队列是计算的圣杯吗?他们解决了世界上所有的问题吗?不,当然不。在很多情况下,我们可能不想使用它们。而且我们当然不希望仅仅因为我们有一个容易使用的队列就认为使用队列很有趣。有些系统真的很简单,只是不需要它—消息队列是降低通信系统复杂性的一种方法,但是两个通信系统总是比一个不必通信的系统更为复杂。如果您的系统非常简单,不需要与其他任何人进行通信,那么就没有任何理由排队。

也有一些系统可以互相通信,但是这些通信所增加的复杂性微不足道,因此不必担心。从某种意义上说,它们都需要协同工作才能正常运行,或者更经常地,系统已经耦合。一个真正常见的示例是应用服务器和数据服务(在OLTP系统中)。将它们与队列解耦没有多大意义,因为如果没有另一个的直接参与,这两个都无法做任何有用的事情。

然后,还要考虑性能-将两个系统在时间和负载方面分离开来,以便它们可以按照自己的进度处理信息-但我们当然不希望这种情况发生在对性能敏感的应用程序或时间系统。队列可以帮助我们同时处理更多的工作(接收者可能有许多并行处理您发送的消息的工作),但会消除我们对每项工作所需的确切时间的保证。如果可预测性比吞吐量更重要,那么最好不要排队。

使用队列可能会增加处理每个所花费的时间个人信息,但会允许你在处理跨不同的,同时还有更多的消息电脑,让你的每分钟或一小时,或处理的消息总数的吞吐量将增加。

如果确实有多个需要通信的系统,并且通信需要持久(如果将消息放入队列中,我们要确保消息传递系统不会“忘记”它)并且解耦后,消息队列必不可少。

 

消息传递语义学

建立消息队列的人将声称他们的系统提供了三种传递保证之一—您放入队列中的每条消息都将被传递:

  • 至少一次。
  • 最多一次。
  • 恰好一次。

我们正在使用的保证将对我们系统的设计和工作产生巨大影响,因此让我们将它们中的每一个拆开。

  • 至少一次

这是最常见的交付机制,也是最简单的推理和实施机制。如果我有一条要给您的消息,我会给您阅读,并一遍又一遍,直到您确认为止。而已。在至少一次运行的系统中,这意味着当您从队列中收到一条消息并且不删除/确认该消息时,以后您将再次收到它,并且将一直接收到该消息为止明确删除/确认它。

这是最常见的保证,其原因是它很简单,并且可以100%地完成工作-在任何情况下都不会丢失消息。即使接收器在确认消息之前崩溃了,它也只会再次接收相同的消息。不利的一面是,作为接收方的您需要计划多次接收同一条消息,即使您不一定遇到崩溃也是如此。这是因为至少提供一次是保护排队服务也不会丢失消息的最简单方法-如果您的确认没有通过网络到达排队系统,则消息将再次发送。如果继续确认存在问题,该消息将再次发送。如果排队系统在重新启动之前可以正确跟踪已发送给您的内容,该消息将再次发送。在任何方面出现任何问题的情况下,再次发送消息的简单补救措施就是使此保证如此可靠。

但是消息重复/重复是一个问题吗?这实际上取决于您和您的应用程序或用例。例如,如果该消息是时间戳和度量,则接收一百万个副本没有问题。但是,如果您根据消息来转移资金,那肯定是一个问题。在这种情况下,您将需要在接收端拥有一个事务(ACID)数据库,并可能将消息ID记录在唯一索引中,以使其无法重复。这称为使用幂等标记或逻辑删除-当您对消息进行操作时,您通常会在与执行操作本身相同的数据库事务中存储唯一的永久标记来跟踪您的操作。即使消息重复,也会阻止您再次重复该操作。

如果您处理重复,或者您的消息天生就可以抵抗重复,则可以说您的系统是幂等的。这意味着您可以安全地处理多次收到相同的消息,而不会破坏您的工作。这通常也意味着您可以容忍发件人多次发送同一条消息-请记住,发件人在发送消息时通常也将至少一次执行一次操作。如果发件人无法记录他们已发送特定消息的事实,则只需再次发送即可。然后,发件人有责任确保在重新发送消息时使用相同的幂等令牌。

  • 最多一次

这是一种非常罕见的语义,用于重复性极高的爆炸性消息(或消息如此根本不重要)以至于我们不希望根本不发送消息,而不是发送两次。最多一次表示排队系统将尝试一次将消息传递给您,但仅此而已。如果您收到并确认该消息一切正常,但是如果您不满意,或者出现任何错误,则该消息将永远丢失-这是因为排队系统在尝试将消息发送给您之前花了很大的功夫将其记录下来(如果该消息具有极高的爆炸性),或者根本没有费心去记录该消息,而只是像路由器传递UDP数据包一样传递消息。

这种语义通常在充当无状态信息路由器的消息传递系统中起作用。或在重复消息具有破坏性的情况下,如果出现任何故障,则必须进行调查或对帐。

  • 恰好一次

这是消息传递的圣杯,也是许多蛇油的源泉。这意味着保证每条消息都只发送一次,处理一次,最多也不会减少。每个构建或使用分布式系统的人在生活中都会想到“这有多难?”,然后他们要么(1)了解为什么这是不可能的,找出幂等性,然后至少一次使用,或者(2)他们尝试建立一个半确定的“完全一次”系统,并以高价将其出售给尚未弄清(1)的人。

不可能一次发送的原因有两个基本事实:

  1. 发件人和收件人不完善
  2. 网络不完善

如果您深入思考问题,那么很多事情都会出错:

  1. 发件人可能无法记录(他们忘记了)他们已经发送了消息
  2. 网络呼叫发送消息可能失败
  3. 消息系统的数据库可能无法记录消息
  4. 消息系统已记录消息的确认可能无法通过网络到达发件人
  5. 发件人可能无法记录消息传递系统已收到消息的确认

假设在发送消息时一切顺利-当消息传递系统尝试将消息传递给接收者时:

  1. 该消息可能无法通过网络到达接收者
  2. 接收者可能无法在其数据库中记录消息
  3. 来自接收者的确认可能无法通过网络到达消息系统
  4. 消息系统的数据库可能无法记录消息已传递

考虑到所有可能出问题的地方,任何消息传递系统都不可能保证一次发送。即使消息传递系统完美无瑕,大多数可能出问题的地方还是在消息传递系统之外或在互连网络中。某些系统确实尝试使用“仅一次”这个短语,通常是因为它们声称其实现绝不会遇到上述任何消息传递系统问题-但这并不意味着整个系统都拥有一次精确的语义魔术,即使声明确实是真实的。这通常意味着排队系统具有某种形式的排序,锁定,散列,计时器和幂等性令牌,这将确保它永远不会重新传递已经被删除/确认的消息,但是不会。

大多数优秀的消息传递系统工程师都理解这一点,并将向用户解释为什么这种语义不可行。处理消息的更简单,更可靠的方法是回到基础知识,并在发送,接收和排队过程的每个点上至少一次采用幂等度量:如果一开始您不成功,请重试,重试,重试...

 

有序与并行

在传递语义之后,人们心中的另一个常见问题是“为什么我们既不能同时处理消息,又要确保我们按顺序处理消息?”。不幸的是,这是逻辑专制强加给我们的另一个权衡。依次进行工作和同时进行多项工作总是相互冲突的。大多数消息队列系统都会要求您选择一个-AWS SQS是通过将并行性放在优先顺序之上来开始的;但最近又引入了一个单独的FIFO(先进先出)排队系统,该系统保持严格的顺序排序。在两者之间做出选择之前,让我们先探讨一下区别是什么以及为什么根本需要区别。

回到我们较早的隐喻队列(长的管子,我们将写在球上的消息滚动到其中),我们可能想象管子比单个球要宽一点。实际上,球在管内根本不可能超车或彼此通过,因此接收器发出这些消息的唯一方法是按照它们被放入的顺序一个接一个地发送。这保证了严格的排序,但是放置牢固对我们接收器的限制。有可能只有一个 代理在正在处理每条消息的接收方,如果多于一条,则不能保证消息是按顺序处理的。因为每个新代理都可以独立处理每个消息,所以它们可以随时完成并从下一条消息开始。如果是两个代理,A和B,并且代理A接收到第一个消息,代理B接收第二个消息;甚至在代理A完成处理第一条消息之前,代理B即可完成第二条消息的处理并从第三条消息开始。尽管严格按照放入顺序从队列中接收消息,但是如果存在多个接收代理,则无法说出消息将按照该顺序进行处理。

代理可以使用某种分布式 相互协调,但这基本上与只有一个代理相同-该锁将只允许一个代理在任何给定时间工作。这也意味着一个代理崩溃将导致死锁,而无需完成任何工作。

消息传递系统保证命令的一种方式是,试管拒绝发出下一个球,直到并且除非接收到的最后一个球被破坏(最后一条消息已被删除/确认)为止。这通常是FIFO队列会执行的操作-仅在确认或删除最后一个消息后才提供下一条消息-但这意味着即使只有N个代理在等待,一次也只能工作一个代理从队列中接收消息。

有时,这正是我们想要的。当我们只需要与单个代理人打交道时,某些操作就更容易有效地控制,例如对金融交易执行规则;遵守限速; 或通常假设将始终按顺序处理其格式已设计好的消息。但是,这些“好处”中的许多并非真正来自决定使用FIFO排序的情况-在任何情况下,如果我们有N个接收器,它们必须以某种方式彼此协调工作,则将受益于N = 1的特殊情况。关键要点是要求有保证的顺序意味着我们必须一次只在一个接收器上顺序处理消息。

这种限制也给排队系统带来了巨大压力,因此您会发现FIFO队列通常比并行队列更昂贵且容量更少。这是因为相同的逻辑限制也适用于排队系统的内部实现-大多数工作需要限制在单个代理或服务器上,并且该系统必须保持可靠。任何增加冗余的工作都需要在主服务器和备用服务之间进行同步协调,以维持订购保证。在AWS SQS中,FIFO队列的成本比并行队列高2倍左右,并且在需要严格的FIFO排序时,每秒被约束到几百条消息。

因此,推进FIFO消息队列的唯一方法是接受整个消息处理体系结构将具有固有的速度限制。许多系统将在队列内支持组标题,以表示我们要严格排序的消息-我们可能会说,“付款”标题下的所有消息都必须是FIFO,而“顺序”标题下的所有消息都必须是FIFO,但是它们彼此之间不需要是FIFO。这允许在队列内进行一些并行化(例如,使用两个FIFO管而不是一个),但是我们需要记住,每个组标题内的消息带宽仍将受到限制。

平行!=随机

这是否意味着并行队列中的排序是完全随机的?有时候是的,但通常不是。在SQS中,比喻更多的是,没有从发送者到接收者的一根管子,而是多根管子。他们也可能在此过程中相互分支或加入。这并不意味着您以任何方式有意地随机分配了要滚动的消息的顺序-在大量消息中,您仍然希望通常较早的消息早于较晚的消息被接收。这是最大的努力排序,需要付出一定的努力来保持排序的完整性,但是由于在逻辑上已经不可能了,因此对于系统而言,这并不是一个重要的优先事项。这也允许像SQS这样的消息传递系统扩展到几乎无限的容量-因为如果要滚动很多消息,则排队系统可以简单地添加更多的管道。可以想象,这将同时支持任意数量的接收者,也支持任意数量的发送者。这种简单性使SQS可以扩展到令人难以置信的数字,包括一个队列中有超过2500亿条消息等待消费的情况,而接收器每秒读取和确认一百万条消息。而这仅仅是一个客户操作的一个队列。

看起来他们对FIFO有严格要求的大多数问题通常可以使自己具有并行性和无序交付,但需要一点点创造力。发件人在消息中添加时间戳是一种帮助解决此问题的方法,例如,在消息为度量标准的情况下,只有最后一个很重要。在更具事务性的系统中,发件人通常可以在消息中添加单调递增的计数器

如果这无法实现,那么我们也许可以根据消息的内容进行处理:例如,如果要发送消息的文件所占的百分比,则看到41%,42%和43%始终表示当前值为43%-即使我们认为它们分别是41%,43%和42%,也是如此。

虽然更改系统以适应我们使用的工具通常是一个坏主意,但是设计消息以允许无序传递和幂等使系统总体上更具弹性,同时还允许使用更多的并行消息传递系统,通常可以节省成本时间,金钱和大量运营工作。

 

Fan Out 扇出/ 扇入In

在构建分布式系统时,通常需要将同一条消息发送给多个接收者,除了通常的消息接收者之外,我们还经常希望将同一条消息发送到其他地方,例如存档,审核日志(为了遵守法规和安全检查)或我们仪表板的分析器。如果您使用的是具有许多服务的事件驱动的体系结构,则可能需要在应用程序中使用单个事件总线,其中发布到该事件总线中的所有消息都会自动发送到所有服务。这称为扇出问题,其中一个生产者的消息需要传达给许多消费者。

相反的问题是,单个接收者负责读取发布到多个队列中的消息的情况也很常见-在我们上面所考虑的示例中,正在归档所有消息或创建审核日志的接收器可能会接收到在一个队列中生成的所有消息。组织,每一个队列。在服务体系结构中,通常还具有像通知那样单独处理的功能-因此,通知系统可能需要接收有关新确认的订单,付款失败,运输成功等消息。这是一个扇入式问题,其中来自许多生产者的消息需要到达同一消费者。

如果所有生产者都将其消息直接放入队列中,这将是一个非常难以解决的问题-我们必须以某种方式拦截我们的队列,并将消息可靠地复制到多个队列中。构建,配置和维护该总机根本不值得花费时间或精力,尤其是当我们只可以使用Topic主题时。

想象主题topic的一种方法是,它们类似于您在学校或办公室的公告板上看到的标题。生产者在板上的特定主题下发布消息,对此主题感兴趣的每个人都将看到该消息。消息传递系统向感兴趣的接收者发送消息的最常见方式是HTTP(S)请求,有时也称为Webhook。在基于推送的系统(如HTTP请求)中,无论消息是否就绪,都会将其推送到接收器中。这重新介绍了我们之前要避免的耦合,我们不希望接收器在短时间内跨越数十/数百/数千/百万个webhooks的巨大负载而崩溃的情况。同样,这里的答案是仅使用消息队列以无论主题生成的速率吸收主题中的消息。然后,接收者可以按照自己的速度处理它们。

将消息从一个主题自动复制到一个或多个队列中并不是严格意义上的消息队列功能,而是一项补充功能-大多数功能齐全的消息传递系统都将提供一种实现此目的的方法。生产者仍将像往常一样继续将消息放在一个地方,但这将是一个主题,在内部消息将被复制到多个队列,每个队列将由各自的接收者读取。

如果您使用的是Apache Kafka之类的其他系统,那么您也会在其中看到类似的概念-您将发布消息的主题称为主题,并且任何数量的使用者都可以阅读该主题中的所有消息。Google的发布/订阅系统还集成了主题和队列。

这些场景的这种结合非常普遍,以至于有一个简单的,完善的模式可以处理它:

  • 将每个消息发布到一个适当的主题。
  • 为每个接收者创建一个队列。
  • 将每个接收者的队列链接到接收者感兴趣的主题。

由于通常可以为任意数量的主题预订队列,因此在接收方不需要额外的管道即可处理来自多个主题的消息。当然,可以有任意数量的消息队列订阅单个主题。这种设置既支持扇出也支持扇入,并且使您的体系结构在将来可以扩展和更改。

 

毒药和死信

听起来很病态,当设置系统与多个其他系统对话时,肯定会发生错误。常见的问题是,订阅者被挂接到接收来自其不了解的主题的消息的消息,而该消息格式是其无法理解的。怎么了?订户是否忽略该消息?还是确认/删除它?忽略它不会错,因为消息只会在一次至少一次的系统中不断反复出现?但是,删除/确认我们未处理的消息是否更糟?在我们找到用树木倒下的树木制成的哲学书籍之前,我们可能需要配置一个死信队列在我们的队列中。这是许多队列系统提供给我们的功能,如果系统看到一条消息被发送出去以进行重复处理(每次失败),它将被移到一个特殊的队列中,称为死信队列。我们希望将此队列挂接到某种警报上,因此我们将很快知道发生了什么奇怪的事情。

更糟糕的情况是,该消息以某种方式具有爆炸性-可能是以XML格式而不是JSON格式,或者包含用户生成的内容,其中包含格式错误的输入攻击,这会导致解析代码崩溃……您的订阅者吞下了一个毒丸。这种药丸到达用户时会发生什么情况,很大程度上取决于您的技术堆栈,因此不用说,您要仔细考虑用户代码中的错误处理和异常。好消息是,如果您配置了一个死信队列,则仅静默失败可能是一个不错的选择。毒药最终将出现在死信队列中并可以进行检查。即使有害消息使您的订户崩溃,使用进程管理器运行自动重启通常也足以重试该消息多次,以至于将其移至死信队列。但是,您确实需要确保没有安全隐患,并且请记住,这是一种简单的DoS攻击

请记住,无论是根据消息的结构是否符合预期的方式以及是否是预期的收件人,都要始终验证传入的消息。

 

Q-List队列产品列表

这是一些当前流行的消息排队系统的列表,并列出了到目前为止我们所看到的概念如何应用于它们中的每个。

  • AWS SNS和SQS

AWS运行两项服务,它们相互集成以提供完整的消息排队功能。SQS服务是一个纯粹的消息队列,它允许你创建一个队列,发送一条消息,并收到一条消息。而已。ReceiveMessageSQS队列上的API是仅拉式的,因此,只要接收者准备好处理消息,就需要调用它。有一个WaitTimeSeconds选项可以阻止呼叫等待消息的时间长达20秒,因此一种有效的模式是ReceiveMessage在等待20秒的情况下以无限循环轮询API。

SNS的集成附带了主题和扇出/扇入功能,该功能可用于构建主题。这允许将消息发布到主题(而不是队列)中。然后,您可以将任意数量的SQS队列预订到一个主题中,因此发布到该主题的消息可以快速复制到所有预订的队列中,而无需支付额外费用。您将需要打开raw message选项,该选项使将消息发布到SNS主题中实际上等同于将消息发布到SQS队列中-不会对消息进行任何转换或打包。

SQS和SNS都是完全托管的服务,因此无需维护服务器或安装软件。根据您发送和接收的消息数向您收费,并且AWS会处理扩展到任何负载的情况。

FIFO选项在SNSSQS上可用,具有不同的定价和容量保证。AWS使用术语消息组ID来表示所有消息都是FIFO的组标题。通过在删除前一条消息之前不给出下一条消息,来按顺序传递组标题中的消息。

  • Google Pub / Sub

Google提供发布/订阅服务作为其云平台的一部分,以处理集成服务中的消息队列和主题。主题的概念如您所愿地存在,而队列称为subscription。如预期的那样,将多个订阅与一个主题相关联会将消息复制到所有相关联的订阅中。除了允许订阅者轮询或拉出订阅中的消息外,Pub / Sub还可以将消息的Webhook样式POST到您的服务器,让您通过成功返回状态代码对其进行确认。

这也是一个完全托管的系统,例如AWS。您会根据发送的邮件数向您收费,Google会根据需要扩展系统规模。它还具有SNS + SQS组合中没有的一些功能,例如允许您使用时间戳和重播消息来查看历史记录。

FIFO功能存在于订购密钥的上下文中,该功能使您可以通过在确认前一条消息之前不给您下一条消息,从而确保按顺序处理订购密钥中的消息。

  • AWS EventBridge

来自AWS的新产品EventBridge提供了完全托管的事件总线—这是队列和主题概念的一种变体,其中所有消息都发布到一条总线中,没有可见的主题分离。相反,每条消息都需要根据一种标准格式进行结构化,该标准格式中应包含有关消息主题的信息。然后,总线将读取该消息,并将其路由到表示有兴趣接收有关该主题的消息的任何订户。从总线到订户的实际传递机制可以是SQS队列,webhooks或许多其他特定于平台的选项。这使得可以轻松地将事件总线作为可单独配置的基于规则的总机进行管理,同时还允许使用简单的插件进行存档,审计,监视,警报,重播等。

  • Redis流

Redis具有相对较新的Streams功能,非常适合消息队列。它通过动态创建主题并使用XADD命令向其中添加消息来工作。使用可以直接从主题中读取消息XREAD,因此每个订阅者可以维护自己的状态(最后读取的偏移量)以通读消息。为了避免每个订户必须保持其当前状态,使用等效于队列来创建使用者组更为有意义XGROUP CREATE。发送到主题的每条消息在每个消费者组中都变得独立可用,然后可以通过进行订阅XREADGROUP。可以使用确认消息XACK。

Redis流使用可以自动生成或手动设置的时间戳自动进行FIFO排序。这也意味着一次只能由一个消费者代理处理消息。为了解决此限制并与许多消费者代理并行工作,文档中描述了一个单独的基于非流的模式,RPOPLPUSH主要用于将LPUSH消息放入主题中,然后将RPOPLPUSH其放入其他列表中,每个列表代表一个队列,或更准确地说它的工作正在进行中。LREM用于删除/确认消息。

Redis是一个开源系统,您可以自行安装和维护或查找托管主机。根据您所需要的系统的持久性,您可能希望找出要使用的最佳持久性机制。

  • apache kafka

Kakfa是一种流行的消息代理,致力于生产者将消息(称为事件)发布到主题的概念。使用主题内的分区键将主题中的事件划分为多个分区,并在每个分区内维护FIFO顺序。事件可以通过套接字流传输给使用者,也可以由使用者查询以实现更分离的方法。对于不想维护状态的消费者,适用消费者组的概念,与Redis Streams相同。一个消费群实际上是一个队列,其中每发布一个主题事件是适用于所有相关加工消费群。

Kafka是开源的,但是安装和维护很复杂,因此适合大型项目和团队。它根据您将事件分割成多个分区的程度进行扩展-分区越多,Kafka可以分配的工作就越多,并且每个分区的容量仅与负责管理它的服务器一样大。托管托管选项是可用的,但与托管服务(如SNS + SQS,Pub / Sub或RabbitMQ)相比,它们往往具有较高的基本成本。

  • RabbitMQ

RabbitMQ是流行的开源消息代理,它支持多种协议,并直接支持主题和队列的概念。RabbitMQ在最多一次和最少一次两种模式下运行,最多一次是基于快速内存的模式,该模式在必要时偶尔将消息写入磁盘(可以在持久队列或临时队列之间进行选择)。如果您想要一个更可靠但更慢,至少一次的系统,则可以使用可靠性指南中描述的操作在发布消息时要求确认,并且需要强制确认阅读它们时。默认情况下,队列是FIFO,可以选择使用确认来强制执行顺序处理。

  • NSQ

NSQ在此列表中是第一个真正的分布式消息队列。它没有连接到发布或订阅消息的单一点:每个NSQ节点实际上都是一台完整的服务器,并与其他每个节点进行通信。节点使您可以将消息发布到主题,并且每个主题都可以链接到一个或多个通道(相当于队列)。发布到某个主题的每条消息都可以在其所有链接的渠道中找到。

NSQ默认为非持久性,至少一次,无顺序的消息传递,但是有一些配置选项可以进行调整。特别值得考虑的是,如果您的服务器之间相互高度联网,并且您想要一个没有单点故障的系统。

  • NATS

NATS是一种高性能的分布式消息传递系统,用于快速的内存中消息传递。它支持基于主题的广播(主题称为主题),其中发送给主题的所有消息都发送给所有订户代理。以及分布式队列,其中队列中的每个消息都发送到任何一个订户代理。没有将主题链接到队列的内置方法,但是应该可以以编程方式进行。

NATS通过提供流系统实验性持久性系统,最多支持一次和至少一次交付。它还支持根据主题名称模式订阅多个主题,这使得扇入和多租户操作更加容易。

当您需要高吞吐量的分布式系统时,NATS可以很好地工作-它也很容易运行,并且支持复杂的网络拓扑,例如使区域群集之间具有连接。

 

1
猜你喜欢