消息队列

消息队列

一、概述

现在的后端系统几乎离不开3类基本中间件:数据库,nosql缓存,消息队列(MQ)。为什么需要MQ,主要有以下几个场景:异步处理、应用解耦、系统间通讯、消息队列驱动型架构、错峰与流控、日志收集、广播等待。

开源的消息队列有很多如rabbitmq,kafka,rocketmq,activemq,ZeroMQ …,针对不同的业务场景、使用不同的编程语言,可能有不同的选择。不同的队列都有优缺点,性能、稳定性、文档、客户端等方面各有优势。但不管选择哪一个,有些业务中,消息队列并不能解决全部问题,比如消息丢失、消息重复、消息顺序等问题,需要根据队列的特性在应用中自己设计处理。

二、消息队列的协议

不同的消息队列,可能会遵循不同的公共协议,也有可能自己制定的协议。比如OpenMQ支持JMS协议,rabbitmq支持AMQP协议,ActiveMQ支持比较多,而Kafka和rocketmq则有自己的一套玩法,现在ali在搞Open Messaging规范,希望大家都支持。下面简单介绍几种协议。

2.1 JMS协议

Java Message Service (JMS),即java标准化组织制定的一个消息通讯的标准。只要有点对点和发布订阅两种模式,点对点很好理解,发布订阅可理解为一对多。

2.2 AMQP 协议

AMQP,即高级消息队列协议。生产者(publishers),消费者(consumers)之间,还有一个代理(broker)。
代理中有队列,交换机和绑定等统称为AMQP实体。消息存在队列中,消费者消费,有两种途径达到消费的目的:push 和 pull。为了保证消费,需要有一个确认,所以AMQP模块包含了一个消息确认(message acknowledgements)的概念。

三、消息队列问题

使用消息队列,需要考虑一系列问题,作为选型和设计开发的判断依据。

3.1 可靠性

高可用和持久化策略,如何保证数据不丢。在消息投递和消费层面,大多数MQ都有自己的保障机制,对于持久的情况,只能在对集群做同步双写。rocketmq有同步双写,rabbitmq可以配置镜像(副本)。

3.2 消息重复

队列通过确认机制可能保证至少有一条消息,但可能无法保证只有一条,也就是保证可靠投递就无法保证只有一条,所以设计时需要考虑幂等性。我常考虑的方案如把消息Id缓存半小时,消费时通过id检查消息重复,当然,如果能保证消息处理的幂等性就更好。

3.3 消息顺序

可能队列能保证消息的顺序,但多个消费者因为网络或者处理时间的原因,可能导致先到达的消息后消费完。在有的业务场景需要保证顺序。
大多数的解决方案是保证生产者 - MQServer - 消费者是一对一对一的关系,但是引入的问题的单点无法保证可用性和吞吐量。rocketmq采用的方式是根据某个id,把它路由到同一个队列。简单说就是主动去分配队列,单个消费者实现。

不管什么方案,保证消息顺序是最复杂的问题,在设计考虑是最好能屏蔽掉需要依赖顺序的情况。

3.4 性能

对稳定性和吞吐量的需求考虑。比如实时日志处理,不太在乎重复或者丢失,但数据量大,需要吞吐量好的队列如kafka,rocketmq。

3.5 定时消息

有的场景需要消费者定时去消费,比如24小时候干嘛,5分钟后干嘛之列的业务场景。rockemq本身支持延时消息,也可以自己设计,比如通过定时器设置“环形队列”。

3.6 异常处理

除了中间件自己的可靠性,业务中使用客户端时如果出现异常( 发信息失败、消费方出现异常),需要考虑重试机制和消息落库反补策略。

3.7 消息堆积

业务消息有没有突然大量堆积的情况出现,如果堆满了,broker是一个什么样的处理策略。

总结

总之,消息中间件很多,各有优缺点,选型时需要考虑具体业务场景,并根据业务需求设计开发补充中间件所不能支持的场景。


参考文档:
* 1分钟实现“延迟消息”功能
* rocketmq最佳实践
* https://tech.meituan.com/mq-design.html

CONTENTS