我们需要什么样的消息队列?

市面上的消息队列有很多,每种都有自己的设计理念,那么我们应该选择哪一款消息队列,我们需要什么样的消息队列?

应用场景

一般使用消息队列,我们都是希望能够实现以下功能:

  • 削峰填谷:服务异步化,避开高峰流量,使服务负载更平稳
  • 解藕:生产者和消费者只需要约定Queue/Topic相关的定义即可
  • 可用性转移:服务之间不再关心双方的实例状态,可用性转移到消息服务上
  • 可伸缩性增强:消费者的增加和移除是平滑的,拓展性强的

功能性需求

针对上述应用场景,我们需要消息队列提供什么功能?

常用功能

  • 发布订阅:发布订阅是消息中间件的最基本模型和功能。
  • 持久化:提供持久化,持久化拓展,持久化成功通知等功能。
  • 高可用:服务应该是高可用的
  • 消息确认:基本功能,确认后的消息才视为消费成功
  • 消息重试:消息消费失败,提供重试功能
  • 死信队列:对于连续无法消费的消息,统一移至死信队列
  • 消息延迟:消息延迟一般用于消息重试,如果是业务上的延时最好使用其他方式。
  • 消息优先级:对消息设置优先级可以控制消息的推送优先级
  • 消息有序:保证整个队列是有序的
  • 消息只投递一次:保证消息不会重复投递
  • 消息支持事务:应对业务回滚,结果消息发送出去的场景
  • 提供控制台:支持Topic管理,队列状态监控等功能的图形界面
  • 提供消息回溯:已确认的消息还能查看
  • 提供消息追踪:消息发送和消费加入调用链
  • 处理消息积压:能够存储大量消息

功能探讨

发布订阅

发布订阅相比于点对点模型在解藕上更好,属于系统的基本设计

消息确认

基本功能,用以保证消息不会丢失

持久化

对于日志型的消息系统本身就是自带持久化的,一些传统需要提供基本的持久化功能。普通消息持久化只需要顺序追加写即可。有些消息系统会提供持久化的接口,支持对接MySQL,InfluxDB等存储。消息发送到消息系统并不会瞬间持久化到磁盘,如果同步等待持久化完成再返回消息发送成功的结果会影响消息的吞吐量,所以可以提供一个消息持久化成功后的通知功能。

死信队列

连续无法消费成功的消息会影响队列的正常消费,设计死信队列将这部分消费移除原队列不是一件很复杂的功能,甚至在业务方都能实现。

高可用

高可用属于系统的目标,是整个消息系统重要的考核目标。

控制台

控制台需要消息系统收集一些数据,会影响一些性能,统计数据同步,上传应该是异步的。同时提供丰富的可视化图形界面也有一些工作量。

消息延迟

对于有序队列消息延迟应该是整体延迟,否则不定长的延迟时间就会破坏有序。

延迟队列在内存中使用小顶堆很容易实现,但是如果考虑到持久化这个问题就会复杂很多。

简单解决方案就是延迟的消息全放到内存中,持久化继续使用顺序追加,这种设计无法应对大量的延迟消息。

作为优化可以把延迟时间较久的消息持久化到专门的分区里,可以按照延迟时间分段存储。

消息优先级

消息优先级同消息延迟一样,在内存中使用小顶堆很容易实现,但是如果考虑到持久化这个问题就会复杂很多。

消息有序

消息有序分为有序发送和有序消费。

保障有序发送首先需要关闭自动重试和异步发送,对于超时的情况必须同步重试,对发送的性能影响很大。

有序消费是建立在单个分区只有一个消费者的情况下保证分区内有序,因为就算有多个消费者也不能同时消费。

消息超时,消费者宕机等原因会影响消息顺序。

消息只投递一次

由于超时,重试等问题,很难保证消息只投递一次,消费者方需要自己做好幂等。

消息支持事务

生产者将消息发送给消息系统,消息系统等待二次确认之后再进行投递。事务消息是否需要单独存储,或者整个Topic都使用事务,不存在部分消息开启事务的情况?对于已经消费过的消息产生的影响如何回滚?

提供消息追溯

基于日志系统的消息队列可以定义保留多久的消息

提供消息追踪

需要额外的存储空间,会带来部分性能损耗,存储是否单纯使用key value即可,使用LSM-Tree,参考LevelDB

处理消息积压

基于日志系统的消息队列可以轻易处理消息积压,设计上当作管道的消息队列不太能够处理该问题