导读:Disque 是 Redis 之父 Salvatore Sanfilippo 新开源的一个分布式内存消息队列。本文是作者编写的该软件设计和使用的说明。 Disque 是一个正在进行的实验性的,分布式内存消息代理。 它的目标是捕获“ Redis 作为消息队列”的用例,通常使用阻塞列表操作来实现。将其移植到 adhoc,自包含的,可扩展的,容错的设计,但在简单性,性能和实现方面仍然类似于 Redis, 并使用 C 语言实现非阻塞服务器。 目前( 2016 年 1 月 2 日)该项目处于 release candidate 状态。 我们现在鼓励人们开始评估该版本,报告 bug 和使用经验。 提醒:目前是测试版代码,可能不适合生产环境使用。 尽管 API 已经稳定,但是在下一个 Release candidate 会有细节上的改动。这是全新代码,所以请小心使用!(高可用架构小编:但如果想学习及研究消息队列这是一个好去处) 什么是消息队列(高可用架构小编提醒:熟悉消息队列的老司机请跳过此部分) 你知道人们如何使用短信来沟通,我可以给我妻子发信息“班顺路买一斤包子带回来,如果看到卖西瓜的,买一个”,她也许会回答“好,消息收到,我会回家买1个”。 消息队列与短信类似,但是作用于计算机程序。例如,当用户订阅消息时, Web 应用可以发送消息给处理发送电子邮件的应用,“请发送确认电子邮件到 [email protected]”。 诸如 Disque 的消息系统允许进程通过不同消息队列进行通信。因此,进程可以向具有给定名称的队列发送消息,并且只有从该队列获取消息的进程才会响应这些消息。此外,多个进程可以侦听同一队列中的消息,并且多个进程可以向同一队列发送消息。 消息队列的重点是能够保证到达,使得即使在失败时也能最终传递消息。 因此,即使在理论上实现消息队列是很容易的,实际写一个非常鲁棒和可扩展的消息队列也很困难。 功能细节介绍Disque 是一个分布式容错消息代理,可以作为想要交换消息的进程之间的中间层。 消息生产者投放消息。由于消息队列通常用于处理延迟的任务, Disque 经常在 API 和文档中使用术语“任务”,然而任务实际上只是以字符串形式的消息,因此 Disque 可以用于其他用例。在本文档中,“任务”和“消息”以可互换的方式使用。 具有生产者 - 消费者模型的队列在业界相当普遍的,所以细节很重要。 Disque 的一些细节如下: Disque 是同步复制的任务队列。默认情况下,添加新任务时,会在客户端收到确认之前将其复制到 W 个节点。 W-1 个节点可能会失败,但仍会传递消息。 Disque 同时支持至少一次投递语义和最多一次投递语义。至少一次投递语义在设计和实现中最为费事,而最多一次投递的语义是设置消息重试时间为 0(这意味着不再将消息重新排队),并且消息的复制因子为 1(不是严格需要的,但是如果它将被最多传送一次,则具有多个消息副本是没有意义的),因此实现相对容易。你可以在同一队列和节点中使用至少一次和最多一次任务,因为这是每个消息上设置的属性。 至少一次投递被设计为尽量接近单次投递,即使在某些类型的故障期间也是如此。这意味着,尽管 Disque 只能保证等于或大于 1 的消息投递,但是尽可能避免多次投递。 Disque 是一个分布式系统,其中所有节点都具有相同的角色(又称为多主节点)。生产者和消费者可以使用他们喜欢的任何节点,并且不需要同一队列的生产者和消费者连接到同一节点。节点将根据负载和客户端请求自动交换消息。 Disque 是高可用的(它是一个最终一致的 AP 系统):哪怕一个单节点可用,生产者和消费者就可以交换消息。 Disque 支持异步命令,异步命令对客户端来说是低延迟操作,然而提供较低的投递保证。例如,生产者可以将复制因子为 3 的任务添加到队列中,但是可能想要复制完成之前结束调用。节点将尽可能在后台复制消息。 在过了消息的重试时间之后, Disque 会自动将未确认的消息由消费者重新排队。如果消息未被处理,则不需要重新排队消息。 Disque 使用显式确认,以便消费者以信号形式通知消息已经投递(使用其他的术语来说就是通知任务已经处理)。 Disque 队列仅尽力保证投递顺序。每个队列基于任务创建时间对任务进行排序,任务创建时间是使用创建消息节点的时钟(加上在同一毫秒中创建的消息的增量计数器)获得的,因此在同一节点中创建的消息通常以创建顺序交付。然而这不是因果顺序,因为在一些情况下不保证正确顺序:当消息因为没有被投递而重新发布时,节点本地时钟漂移,并且消息被移动到其他节点以用于负载平衡和联合(在这种情况下您以具有不同节点的任务发起的队列结束与不同的壁钟)时无法保证顺序。然而,所有这也意味着通常消息有序传递的,而是按时间顺序。 注意,由于 Disque 不提供严格的 FIFO 语义,从技术上讲,它不应该被称为消息队列,而应该被称为消息代理。 然而,我 认为在 IT 行业里,消息队列通常用于可能或不能在所有情况下保证消息投递的通用代理。 鉴于我们非常清楚这一语义,我认为 Disque 可以叫做消息队列。 Disque 为用户提供了使用三个时间相关参数和一个复制参数来对每个任务做细粒度控制。 对于每个任务,用户可以控制:
最后,Disque 支持磁盘持久化,默认情况下不启用,但在单个数据中心初始化和重新启动期间可以使用。
队列的 ACK 和重试实现 Disque 的至少一次投递(at-least-once)语义是为了避免在某些故障期间的多次投递。它不能保证不会发生多次投递。 然而,存在许多至少一次投递的情况下,其中重复投递是可接受的(或可以明确处理的),但消费者不希望如此。一个简单的例子是向用户发送重复电子邮件(用户收到重复的电子邮件不是很大的问题,但应该尽可能避免)相比执行代价很高的幂等操作更容易接受。
例如,如果在任务被消费者确认的时间内,具有任务副本的节点被分区,则很可能当它返回时(在合理的时间量中,即在达到重试时间之前) ),它将收到ACK 并且将避免对消息重新排队。类似地,任务在分区期间内被确认为仅仅单个可用节点,并且当分区修复ACK时,传播到可能具有消息副本的其他节点。 快速确认Disque 支持通过 FASTACK 命令更快地确认处理的消息。从节点之间交换的消息的角度来看,确认消息是非常昂贵的,以下是在正常确认期间流程:
注意:实际的垃圾收集在失败的情况下更复杂,将在稍后的状态机中解释。上面是 99% 的情况下发生了什么的流程。
如果在快速确认期间,具有消息副本的节点不可达,则节点将再次投递消息,这是因为该节点具有消息的未确认副本,并且当分区愈合时没有人能够通知它消息已被确认。 死消息队列许多消息队列实现称为死消息队列的功能。它是一个特殊的队列,用于累积由于某种原因而无法处理的消息。常见原因可能是:
这种想法用于系统管理员检查(通常通过自动系统)在死信队列中是否存在某些东西,以便了解是否存在某些软件错误或其他类型的错误从而导致消息没有被处理。
基本上,本功能的使用取决于应用程序。注意,计数器在面对故障或网络分区时不需要不需要保证一致性:如果最终消息有问题,计数器将增加到足够的值从而达到警告阈值(由应用程序设定)。 Disque 的磁盘持久化Disque 只能在内存中操作,使用同步复制作为可靠性保证,或者可以使用AOF(Append Only File)方式,将任务创建和废弃在磁盘上进行记录(使用可配置的 fsync 策略),并在重新启动时重新加载。
此时,我们在磁盘上新生成AOF,并且服务器配置为仅在下次重新启动时加载完整状态(aof-enqueue-jobs-once在重新启动后自动关闭)。 任务 ID任务 ID(如下所示)是其唯一标识: D-dcb833cf-8YL1NT17e9 wsA/09NqxscQI-05a1 任务 ID 由 40 个字符组成,并以前缀 D- 开头。我们可以将 ID 拆分为多个部分: D- | dcb833cf | 8YL1NT17e9 wsA/09NqxscQI | 05a1
当成功创建任务时,ADDJOB 返回 ID,它们是 GETJOB 输出的一部分,用于确认任务由 worker 正确处理。 P(100,2^32) = .000001164 在发生碰撞的情况下,worker 可能只是做出一个无效的选择。144 位随机部分中发生冲突是不可能的,因为它是如下计算的。
所以有 22300745198530623141535718272648361505980416 可能的 ID 以供选择。 虽然冲突的概率在数学上是存在的,但工程上每个 ID 可以被认为是唯一的。 本文由高可用架构志愿者翻译及首发,英文来源: https://github.com/antirez/disque 关注高可用架构公众号,了解后续 disque 更多介绍。 欢迎通过公众号菜单「联系我们」进行投稿,也欢迎最新优秀技术文章的译稿。转载请注明来自高可用架构「ArchNotes」微信公众号及包含以下二维码。 高可用架构 改变互联网的构建方式
高可用架构主办 GIAC 全球互联网架构大会,李智慧、左耳朵耗子、来炜等技术专家已经加入出品人,本周购买双日套票仅需 900 元起,点击阅读原文了解详细议程。
|
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|