队列持久化和消息持久化_评估持久的复制邮件队列

队列持久化和消息持久化

消息队列在许多情况下很有用。 例如,当我们要异步执行任务时,我们将其排队,然后某些执行程序最终将其完成。 根据使用情况,队列可以为消息的持久性和传递提供各种保证。 对于某些用例,有一个内存中的消息队列就足够了。 对于其他用户,我们希望确保消息发送完成后,即使节点或系统崩溃,消息也会持久排队并最终被传递。

为了确保消息不会丢失,我们将查看以下队列:

  • 将消息持久保存到磁盘
  • 在网络上复制消息

理想情况下,我们希望有3个相同的,包含消息队列的复制节点。

有许多可用的开源消息传递项目,但是只有少数几个同时支持持久性和复制。 我们将评估4个消息队列的性能和特征:

mqperf_logos

尽管SQS不是开源消息传递系统,但它可以满足需求,并且我最近对它进行基准测试 ,因此将自托管解决方案与即服务解决方案进行比较将很有趣。

MongoDB当然不是队列,而是基于文档的NoSQL数据库,但是使用其某些机制,很容易在其之上实现消息队列。

这绝不是要进行全面的概述,而只是对一些项目进行评估。 如果您知道其他任何提供持久复制队列的消息传递系统,请告诉我!

更新:正如许多读者所指出的(例如@tlockney@Evanfchan@conikeec等), Kafka丢失了! 尽管它的工作方式略有不同(消费者是有状态的–集群化的–他们保持偏移量),但Kafka支持点对点消息传递,其中每条消息都由一个消费者使用,因此请继续关注博客的更新版本。卡夫卡!

测试方法

测试的所有来源都可以在GitHub上找到 。 测试运行可变数量的节点(1-8); 每个节点使用可变数量的线程(1-25)发送或接收消息,具体取决于具体的测试设置。

每个发件人线程尝试以1到10条消息之间的随机大小批量尽快发送给定数量的消息。 对于某些队列,我们​​还将评估更大的批次,最多100或1000条消息。 发送完所有邮件后,发件人报告每秒发送的邮件数。

接收方尝试接收消息(也分批接收),并且在接收到消息后,确认消息已传递(这将导致消息从队列中删除)。 一分钟内未收到任何消息时,接收器线程将报告每秒接收到的消息数。

mqtestsetup

队列必须实现Mq接口。 该方法应具有以下特征:

  • send应该是同步的,也就是说,当它完成时,我们要确保( 确切意味着可能有所不同)发送消息
  • receive应从队列接收消息并将其阻止; 如果节点崩溃,则应将消息返回到队列并重新传递
  • ack应该确认消息的传递和处理。 确认可以是异步的,也就是说,我们不必确保消息确实被删除。

上面的模型描述了至少一次消息传递模型。 一些队列还提供了其他交付模型,但我们将重点放在该模型上,以比较可能相似的事物。

我们将研究使用单个2或3节点消息队列群集可以多快(就吞吐量而言)发送和接收消息。

蒙哥

Mongo具有两个主要功能,可以轻松在其上实现持久的复制消息队列:非常简单的复制设置(我们将使用3节点副本集),以及各种文档级原子操作,例如find-and-modify 。 该实现只是几行代码。 看看MongoMq

我们还能够通过在编写新消息时使用适当的写关注来控制send给我们的保证:

  • WriteConcern.ACKNOWLEDGED (以前为SAFE )确保发送完成后,消息已被写入磁盘(尽管不是100%的持久性保证,因为磁盘可能具有自己的写缓存)
  • WriteConcern.REPLICA_ACKNOWLEDGED确保将消息写入集群中的大多数节点

基于Mongo的队列的主要缺点是:

  • 无法批量接收邮件- find-and-modify操作一次只能在一个文档上进行
  • 当有很多连接试图接收消息时,该集合将遇到很多争用,并且所有操作都将被序列化。

结果表明:发送比接收快。 但是总体表现还是不错的!

单线程,单节点设置可实现发送7 900 msgs / s和接收1900 msgs / s 。 当使用“安全”写入方式时,我能够实现的具有多个线程/节点的最大发送吞吐量约为10 500 msgs / s ,而最大接收速率为3 200 msgs / s

线程数 节点数 发送信息/秒 接收消息/秒
1个 1个 7 968,60 1 914,05
5 1个 9 903,47 3 149,00
25 1个 10 903,00 3 266,83
1个 2 9 569,99 2 779,87
5 2 10 078,65 3 112,55
25 2 7 930,50 3 014,00


如果我们等待副本确认写入(而不是仅一个节点),则发送吞吐量将降至6 500 msgs / s ,接收吞吐量将降至2 900 msgs / s

线程数 节点数 发送信息/秒 接收消息/秒
1个 1个 1 489,21 1 483,69
1个 2 2 431,27 2 421,01
5 2 6 333,10 2 913,90
25 2 6 550,00 2 841,00


我认为,对于在Mongo之上非常简单的队列实现来说,还不错。

SQS

SQS在我以前的博客中已经介绍了很多,所以这里只是一个简短的回顾。

SQS保证,如果发送完成,则消息将被复制到多个节点。 它还提供至少一次的交货保证。

我们真的不知道SQS的实现方式,但是它很可能将负载分散到许多服务器上,因此在这里包括竞争有点不公平:其他系统使用单个复制群集,而SQS可以使用多个复制群集并路由/平衡它们之间的消息。 但是,由于我们有结果,因此让我们看一下如何比较。

单个节点上的单个线程可达到430 msgs / s的发送速度,并且接收到相同数量的msgs。

这些结果并不令人印象深刻,但是当增加线程数和节点数时,SQS可以很好地扩展。 在具有50个线程的单个节点上,我们可以发送高达14500 msgs / s的数据 ,并接收高达4200 msgs / s的数据

在8节点群集上,这些数字发送速度高达63 500 msgs / s ,接收的速度高达34 800 msgs / s

sqsperf31

兔子MQ

RabbitMQ是领先的开源消息传递系统之一。 它用Erlang编写,实现AMQP ,是涉及消息传递时非常受欢迎的选择。 它支持消息持久性和复制,并且在例如分区的情况下具有充分记录的行为。

我们将测试3节点Rabbit集群。 为确保发送成功完成,我们将使用发布者Confirms ,这是AMQP的Rabbit扩展。 确认是在整个集群范围内进行的,因此这为我们提供了有力的保证:消息将被写入磁盘并复制到集群(请参阅docs )。

如此有力的保证可能解释了性能不佳。 单线程,单节点为我们提供了310 msgs / s的发送和接收速度。 当我们添加节点时,这很好地扩展,最高可达1600 msgs / s

兔子1

Mq接口的RabbitMq实现同样非常简单。 我们正在使用上述发布者确认,并在接收时设置服务质量,以便最多发送10条未经确认的邮件。

有趣的是,增加节点上的线程数不会影响结果。 可能是因为我不正确地使用Rabbit API,或者这仅仅是Rabbit的工作方式。 在单个节点上有5个发送线程时,吞吐量仅增加到410 msgs / s

如果我们最多发送100或1000而不是10的消息,情况会有所改善。在两种情况下,我们都可以达到3 300 msgs / s的发送和接收,这似乎是Rabbit可以达到的最大值。 批处理结果(最多100个):

线程数 节点数 发送信息/秒 接收消息/秒
1个 1个 1 829,63 1 811,14
1个 2 2 626,16 2 625,85
1个 4 3 158,46 3 124,92
1个 8 3 261,36 3 226,40

对于1000以下的批次:

线程数 节点数 发送信息/秒 接收消息/秒
1个 1个 3 181,08 2 549,45
1个 2 3 307,10 3 278,29
1个 4 3 566,72 3 533,92
1个 8 3 406,72 3 377,68

大黄蜂

HornetQ由JBoss编写,并且是JBossAS(实现JMS)的一部分,是一个强有力的竞争者。 一段时间以来,它支持使用实时备份对进行网络复制。 我尝试设置3个节点的群集,但似乎数据仅复制到一个节点。 因此,这里我们将使用两个节点的集群。

这就提出了一个关于如何处理分区的问题。 默认情况下,备份服务器不会自动进行故障转移,操作员必须执行此操作(将备份服务器转换为活动服务器)。 这当然是处理分区的有效方法,但通常不是首选方法。 可以添加配置以自动检测到主服务器已死,但是随后我们可以轻松地获得两台活动服务器,这又引发了一个问题,即当重新建立连接时,两个主服务器上的数据将如何处理。 总体而言,复制支持和文档比Mongo和Rabbit差。

另外,据我了解的文档(但我认为在任何地方都没有明确指出),复制是异步的,这意味着即使我们在事务中发送消息,但一旦事务提交,我们就可以确保消息已写入仅在主节点的日志上。 这比Rabbit中的保证要弱,并且对应于Mongo的safe写入问题。

HornetMq实现使用核心Hornet API。 对于发送,我们使用事务,对于接收,我们依靠内部接收缓冲区并关闭阻塞确认(使它们异步)。 有趣的是,我们一次只能收到一条消息,然后再进行确认,否则我们将在服务器上收到异常消息。 但这似乎并不影响性能。

说到性能,那是非常好的! 单节点单线程设置达到1100 msgs / s 。 使用25个线程,我们的速度高达12800 msgs / s ! 最后,使用25个线程和4个节点,我们可以达到17000 msgs / s

线程数 节点数 发送信息/秒 接收消息/秒
1个 1个 1 108,38 1 106,68
5 1个 4 333,13 4 318,25
25 1个 12 791,83 12 802,42
1个 2 2 095,15 2 029,99
5 2 7 855,75 7 759,40
25 2 14 200,25 13 761,75
1个 4 3 768,28 3 627,02
5 4 11572,10 10708,70
25 4 17402,50 16 160,50


最后要注意的一点是:当尝试使用25个线程(最多1000个)批量发送消息时,我曾经遇到一种情况,即使备份正在工作,备份仍认为主要死机,而又一次由于“地址是阻塞”(换句话说,队列已满,无法容纳在内存中),即使接收器一直在工作。 也许是由于GC? 还是只是很高的负载?

综上所述

与往常一样,您选择哪种消息队列取决于特定的项目要求。 上述所有解决方案都有一些好的方面:

  • SQS是一项服务,因此,特别是如果您使用的是AWS云,这是一个简单的选择:良好的性能且无需设置
  • 如果您使用的是Mongo,则很容易在其上构建复制的消息队列,而无需创建和维护单独的消息传递群集
  • 如果您想获得高持久性保证,RabbitMQ可以确保跨集群复制以及在消息发送时在磁盘上进行复制。
  • 最后,HornetQ表现最佳

仅查看吞吐量时,HornetQ无疑是赢家(除非我们将SQS包含多个节点,但如上所述,那将是不公平的):

摘要1

当然,除了性能以外,还有许多其他方面,在选择消息队列时应考虑到这些方面,例如管理开销,分区容限,有关路由的功能集等。

您对持久的复制队列有任何经验吗? 也许您正在使用其他一些消息传递解决方案?

翻译自: https://www.javacodegeeks.com/2014/07/evaluating-persistent-replicated-message-queues.html

队列持久化和消息持久化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值