常用高可靠消息队列(MQ)系统选型

MQ可靠消息传递背景知识

消息队列是大多数分布式系统的核心,给服务/微服务的异步处理提供支持。

  • 当异步执行任务时,可以将任务写入队列,其他组件(线程、进程)读取到该任务后运行。
  • 消息送达处理任务队列时,我们希望它能够被快速读取,异步处理。
  • 在提高可靠消息传递前提下(我们希望确保消息发送完成后,即使节点或系统崩溃,该消息也将永久排队并最终将被传递)测试如下几种常见MQ系统:

  • Mongo DB
  • RabbitMq
  • ActiveMQ
  • Kafka
  • 系统通过以下方式确保消息高可靠性:

  • 将消息持久保存到磁盘
  • 通过网络复制消息
  • MQ满足条件

    1. 作为发送方,我们希望一旦消息发送成功,消息就会得到处理。当然,不可能达到100%成功,将消息丢失可能性降到最低:消息在服务器重启不丢失,消息应保留在持久性硬盘中;服务器故障消息不丢失,消息应复制到其他服务器后才发送。
    2. 作为接收方,接收一条消息,确认该消息已成功处理。消息接收不应立刻将消息从队列中删除,因为接收方可能随时崩溃(包括接收后,处理之前)。可能导致消息被处理两次(例如,接收方在处理之后 ,确认之前崩溃)。
    3. 确认一条消息成功处理。对于同一次消息传递,消息处理应该是幂等的,即两次处理消息应该不会造成任何问题(业务保证重新发送了某个消息,不会造成任何损害)。

    测试及结果

    测试维度:

    • 消息吞吐量/秒:队列平均速度,即每秒可以发送多少条消息,每秒可以接收和确认多少条消息
    • 处理延迟95th percentile (1分钟窗口内):消息发送和消息接收之间经过的时间(以毫秒为单位)。也就是说,代理将消息从发送方传递到接收方的速度有多快
    • 发送延迟95th percentile (1分钟窗口内):完成一条消息发送需要多长时间,才确保消息安全地保留在群集中。类似响应客户的http请求“已收到邮件”。(95th percentile=X,表示95%的数值都比X低或高)

    测试过程:

    测试队列部署在3个相同集群节点上,集群自动处理故障。测试使用了1到8个客户端节点(客户端和接收方节点的数量始终相同),每个节点运行1到25个线程,每个线程尝试以每一批为1到10条(消息大小随机)成批发送定量消息(每一批最多100或1000条消息)。接收节点接收消息后,确认消息(队列删除该消息)。当一分钟内未收到任何消息时,测试结束。

    • sender 应该是同步的,也就是说,send完成时,我们要确保消息发送成功。
    • receiver应该从队列中接收消息,如果receive节点崩溃,则应将消息返回到队列并重新发送。
    • ACK确认消息的传递和处理。ACK可以是异步的,不必确保ACK成功。
    常用高可靠消息队列(MQ)系统选型

    测试硬件:

    测试服务器才有AWS m4.2xlarge EC2 实例;每个这样的实例8个虚拟CPU,32GiB的RAM和SSD存储。所有实例均在同一可用区(eu-west-1)中启动。

    Mongo

    常用高可靠消息队列(MQ)系统选型

    Mongo有两个重要功能,可以轻松在其上实现持久化

    队列之间消息复制:非常简单的复制设置(使用3节点副本)

    文档原子性操作:例如find-and-modify,该实现只是几行代码(MongoMq项目)。

    • WriteConcern.W1确保发送完成后,消息已被写入单个节点上的磁盘(但是缓冲区可能尚未刷新,因此不是100%成功),异步复制。
    • WriteConcern.W2确保将消息写入集群中的至少2个节点(共有3个节点),同步复制。

    Mongo的队列的主要缺点是:

    • 无法批量接收消息,find-and-modify操作一次只能处理一个文档。
    • 当有很多连接尝试接收消息时,并且所有接受操作都将顺序执行。

    异步复制,测试结果:

    • 发送比接收快。但是总体表现还是不错的!
    • 单线程,单节点同步复制可实现1346 msgs / s发送和接收速度。
    • 多线程/节点的最大发送吞吐量约为25038 msgs / s(25个线程,2个节点),最大接收速率为8273 msgs / s(25个线程,1个节点)
    常用高可靠消息队列(MQ)系统选型

    异步复制,测试结果:

    发送速度高达57555 msg / s,接收速度高达9937 msgs / s。发送和接收性能之间的差异更大。需要注意的一件有趣的事是,接收吞吐量迅速达到了最大值,而增加更多线程(客户端)只会降低性能。并发性越高,总体吞吐量就越低。

    常用高可靠消息队列(MQ)系统选型

    延迟测试结果:

    • 发送延迟,在同步和异步测试中,约为48毫秒,并且增加并发客户端不会恶化。
    • 处理延迟,只有在接收速率与发送速率相同时,测量才有意义。如果客户端无法像发送时一样快地接收消息,则处理时间会任意增加。

    在2个节点各自运行5个线程的情况下,Mongo实现了7357 msgs / s的吞吐量和48 ms的处理延迟。该测试结果仪表盘:

    常用高可靠消息队列(MQ)系统选型

    注意:接收速率有些不均匀-两个接收器节点都收到不同数量的消息。使用同步复制时的详细结果:

    常用高可靠消息队列(MQ)系统选型

    总的来说,基于Mongo 实现队列MQ,效果还不错。

    RabbitMQ

    常用高可靠消息队列(MQ)系统选型

    RabbitMQ是领先的开源消息系统。它用Erlang编写,实现AMQP协议。使用RabbitMQ,可以定义非常复杂的消息传递拓扑。它支持消息持久性和复制。

    我们测试3节点Rabbit集群。为确保发送成功完成,我们将使用发送Confirms,即AMQP的Rabbit扩展,而不是交易。

    测试结果:

    单线程单节点使我们能够发送和接收680 msgs / s的数据,处理延迟为184 ms,发送延迟为48 ms

    常用高可靠消息队列(MQ)系统选型

    常用高可靠消息队列(MQ)系统选型

    当我们增加线程/节点时,使用4个发送/接收节点(每个线程运行25个线程),最高达到到4330 msgs / s,这似乎是Rabbit可以达到的最大值。

    看测试仪表板,接收速率在40005000 msgs / s之间相当稳定。整个测试过程中的处理延迟都基本相同,第95个百分位数为

    179 ms。但是,发送延迟在短时间内上升到504ms。因此,在某些情况下,甚至在发送完成之前,消息就已经被处理了!但是,在大多数情况下,发送延迟在150-250毫秒之间。

    常用高可靠消息队列(MQ)系统选型

    消息始终以相同的速率发送和接收,这表明消息发送是吞吐量的限制因素。Rabbit的性能不足是它提供的某些功能的结果。

    有趣的是,以更大的批发送消息不会对总体吞吐量产生太大影响,当使用100消息每批时为

    4550msgs/s,当我们批处理1000消息每批次时为5004msgs/s

    重要的是:RabbitMQ具有一个基于Web的出色控制台,几乎无需设置即可使用,它提供了有关队列执行情况的一些很好的见解。

    ActiveMQ

    常用高可靠消息队列(MQ)系统选型

    ActiveMQ是最受欢迎的消息代理之一。但是,它只是最近才支持复制功能。在版本5.9.0之前,使用共享文件系统(例如SAN)或共享数据库进行主从设置。这些解决方案需要专门的硬件,或者受关系数据库的约束。

    但是,现在可以使用Replicated LevelDB存储,该存储使用Zookeeper进行集群协调(例如Kafka)。

    复制可以是同步的,也可以是异步的。实际上,它具有很大的灵活性。通过设置sync存储的配置,我们可以控制完成请求之前必须接收多少个节点以及是否应该将其写入磁盘:

    • quorum_mem 对应于同步复制,大多数服务器必须接收一条消息并将其存储在内存中。
    • quorum_disk 更强大,需要将消息写入磁盘。
    • local_mem是异步复制,其中消息必须仅存储在内存中。即使刷新了磁盘缓冲区,这也不能保证在服务器重新启动的情况下传递消息。
    • local_disk 是异步复制,其中必须将消息写入一台服务器上的磁盘。

    在测试中,我们使用quorum_mem操作群集3节点,发送时,我们创建一个生产者,将交付方式设置为PERSISTENT;接收时,我们创建一个消费者CLIENT_ACKNOWLEDGE,并通过手动确认消息。

    在性能方面,ActiveMQ的性能比RabbitMQ差一点,通过同步复制最多可达到3857 msgs / s

    。这似乎是最大的,并通过1个节点和25个线程来实现:

    常用高可靠消息队列(MQ)系统选型

    在最高吞吐量下,接收速率在37004100 msg / s之间相当稳定。处理等待时间也较高,最多284 ms

    常用高可靠消息队列(MQ)系统选型

    添加更多节点并不能改善结果,实际上,它们会稍差一些。有趣的是,使用更强的quorum_disk保证对性能没有太大影响:

    常用高可靠消息队列(MQ)系统选型

    常用高可靠消息队列(MQ)系统选型

    与RabbitMQ相同,在所有测试中,发送和接收均以相同的速率进行,因此,吞吐量方面的瓶颈是发送方。

    Kafka

    常用高可靠消息队列(MQ)系统选型

    Kafka采用了不同的消息传递方法。服务器本身是一个流式发布-订阅系统,或者在更基本的层次上,是一个分布式日志。每个Kafka主题可以有多个分区。通过使用更多分区,可以拓展消息的使用者(和吞吐量)并增加处理的并发性。

    在具有分区的发布-订阅之上,通过将大量逻辑控制使用者中来构建点对点消息传递系统。

    消费者组中的每个消费者都从多个独立分区中读取消息;因此,增加更多的消费者线程而不增加分区是没有意义的。

    消息不会在服务器上确认,而是由消费者管理消息偏移量,并将其写回到特殊的Kafka存储中,这些存储可以在后台自动进行,或手动。

    这样的设计有两个后果:

    • 来自相同分区的消息按顺序处理。可以自定义分区路由策略
    • 所有消费者都应以相同的速度消费消息。来自慢消费者的消息不会被快速消费者处理
    • 消息不能被选择性地确认。
    • 没有可用的“高级”传递选项,例如路由或延迟消息传递。

    为了实现有保证的发送和最少一次的传递,我们使用了以下配置(请参阅KafkaMq类):

    • topic 的 replication-factor 设置 3
    • 对于发送者,request.required.acks选项设置为1(异步复制-发送请求将被阻止,直到被接受为止)或-1(同步复制;与min.insync.replicas主题配置一起设置为2 发送请求将被阻止,直到至少被2 个节点接受为止)。
    • 消费者补偿是每10秒手动提交一次;在此期间,消息接收被阻止(使用读写锁来确保)。这样,我们可以实现至少一次传递。

    测试结果:Kafka的表现很棒。使用同步复制,单节点单线程可达到约2391 msgs / s,最好的结果是54494 msgs / s,具有25个发送和接收线程以及6个客户端发送方/接收方节点。

    常用高可靠消息队列(MQ)系统选型

    接收速率非常稳定。处理延迟的第95个百分点也是47 ms的稳定值。发送延迟也约为48ms。结果的摘要:

    常用高可靠消息队列(MQ)系统选型

    常用高可靠消息队列(MQ)系统选型

    在6个以上的节点上添加更多的客户端线程并不会提高性能-这可能是我们可以从3节点的Kafka集群中获得的最大收益。但是,可以添加节点并增加分区数量,Kafka具有很大的可扩展性。

    扩大批次:通过使用多达100个的批次,我们可以通过4个客户端节点达到102170 msgs / s,而使用多达1000个消息的批次,则可以达到141250 msgs / s。但是,处理延迟然后增加到443ms。

    总结

    选择哪个消息队列取决于特定的项目要求,总结如下:

    • 如果您已经在使用Mongo,则很容易在其上构建复制的消息队列,而无需创建和维护单独的消息传递集群。
    • 如果您想获得高持久性保证,RabbitMQ可以确保跨集群复制以及在消息发送时在磁盘上进行复制。这是在许多项目中使用的非常受欢迎的选择,具有完整的AMQP支持。
    • Kafka以功能集为代价提供最佳性能和可扩展性,仅查看吞吐量时,Kafka无疑是赢家。
    • 发送延迟的第95个百分位数始终很低(大约50毫秒),只有较高负载的Rabbit例外。
    • 处理延迟方面,ActiveMQ在高负载下的表现较差。

    当然,除了性能以外,还有许多其他方面,在选择消息队列时还应考虑这些方面:例如管理开销,分区容限,路由功能。希望能够为您的选择提供帮助!


    分享到:


    相關文章: