MQ概念
MQ消息队列,是基础数据中一种先进先出的数据结构。一般用来解决应用解耦,异步消息,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构。提供强大、安全、稳定的消息数据传递。
MQ的作用
- 解耦:当业务涉及到多个模块或者一条消息需要多个系统处理,只需要主业务完成以后,发送消息到MQ,其余模块去消费MQ消息,即可实现业务,降低耦合。
- 异步:主业务完成后,从属业务再去响应处理,异步执行,减少业务响应时间。
- 削峰:在高并发的情况下,业务异步处理,消息暂存,避免了系统瘫痪。
MQ需要考虑的问题
系统可用性降低,且复杂度提高,需考虑MQ瘫痪以及消息丢失、重复消费、消息传递的顺序以及业务一致性等问题。
MQ主要的产品
RocketMQ、rabbitMQ、ActiveMQ、Kafuka等,各产品间的特性比较。
RocketMQ
消息队列RocketMQ版是阿里云基于Apache RocketMQ构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。消息队列RocketMQ版既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试,多协议接入,支持TCP跟HTTP协议等特性。
核心概念
-
Topic:消息主题,一级消息类型,生产者向其发送消息。
-
生产者:也称为消息发布者,负责生产并发送消息至Topic。
-
消费者:也称为消息订阅者,负责从Topic接收并消费消息。
-
消息:生产者向Topic发送并最终传送给消费者的数据和(可选)属性的组合。
-
消息属性:生产者可以为消息定义的属性,包含Message Key和Tag。
- Message Key:消息的业务标识,唯一标识某个业务逻辑。可根据设置的Message Key对消息进行查询。
- Tag:消息标签,二级消息类型,用来进一步区分某个Topic下的消息分类。消费者可通过Tag对消息进行过滤。消息过滤的更多信息,请参见消息过滤。
-
Group:一类生产者或消费者,这类生产者或消费者通常生产或消费同一类消息,且消息发布或订阅的逻辑一致。
消息模型
生产者/消费者集群,包含多个实例,可以是多个机器,或者是一个机器的多个进程,或者是一个进程的多个对象。一个消费者集群对应一个GroupId,一个GroupId可以订阅多个Topic。一个消费者集群下的消费者以均摊方式消费消息,如果是广播者模式,则每个实例都消费全量数据。
应用场景与功能特性
- 异步解耦 异步解耦的主要目的是减少响应时间和应用解耦。适用于比较耗时且不需要同步返回结果的操作,将操作作为消息放入消息队列。只要保证消息格式不变,消息发送方和接收方不需要 彼此联系,互不影响。
- 分布式事务的数据一致性
RocketMQ提供分布式事务功能,来达到分布式事务的最终一致性,交互流程如下:
-
生产者将半事务消息发送至消息队列RocketMQ版服务端。
-
消息队列RocketMQ版服务端将消息持久化成功之后,向生产者返回Ack确认消息已经发送成功,此时消息为半事务消息。
-
生产者开始执行本地事务逻辑。
-
生产者根据本地事务执行结果向服务端提交二次确认结果(Commit或是Rollback),服务端收到确认结果后处理逻辑如下:
- 二次确认结果为Commit:服务端将半事务消息标记为可投递,并投递给消费者。
- 二次确认结果为Rollback:服务端将回滚事务,不会将半事务消息投递给消费者。
-
在断网或者是生产者应用重启的特殊情况下,若服务端未收到发送者提交的二次确认结果,或服务端收到的二次确认结果为Unknown未知状态,经过固定时间后,服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。
事务消息回查步骤如下:
- 生产者收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
- 生产者根据检查得到的本地事务的最终状态再次提交二次确认,服务端仍按照步骤4对半事务消息进行处理。
-
消息顺序发出 对于一个指定的topic,消息严格按照先进先出的原则进行发布和消费,先发布先消费。顺序消息分为分区顺序消息和全局顺序消息。分区顺序消息适用于性能要求高的场景,所有消息根据Sharding Key进行区块分区,同一个分区内的消息按照严格的先进先出(FIFO)原则进行发布和消费。
-
削峰填谷 削峰填谷一般用于秒杀或者团购活动,若秒杀系统处理大量的访问请求后,下游系统无法承载海量数据访问,则可能发生系统崩溃,可在两个系统中间加入MQ队列,将请求先发送至MQ,下游系统订阅消息进行处理。
-
消息同步 MQ提供了一种广播机制,一条消息本来只可以被集群的一台机器消费,如果使用消息队列RocketMQ版的广播消费模式,那么这条消息会被所有节点消费一次,相当于把价格信息同步到需要的每台机器上,取代缓存的作用、
-
消息重试 消息重试机制:消息队列在消费过程中失败,在重试间隔时间后,重新投递给消费者,如果达到最大次数还没有成功,则会被移到死信队列,消息重试只对集群消费模式有效,广播模式不提供重试特性
-
消息过滤 消费者订阅某个topic后,消息队列会将这个topic的所有消息都投递给消费者,可以通过设置过滤条件对消息进行过滤,方式如下:
-
SQL属性过滤 ,SQL属性过滤是在消息发送时设置消息的自定义属性,消费者订阅时使用SQL语法设置过滤表达式,根据自定义属性过滤消息,消息队列RocketMQ版根据表达式的逻辑进行计算,将符合条件的消息投递到消费端。
-
Tag,即消息标签,用于对某个Topic下的消息进行分类。消息队列RocketMQ版的生产者在发送消息时,指定消息的Tag,消费者需根据已经指定的Tag来进行订阅
-
定时消息和延时消息 通过msg.setStartDeliverTime设置定时/延时时间,StartDeliverTime时服务端开始向消费端投递的时间,如果消息有堆积,则发送时间可能延迟。
-
集群消费和广播消费
集群消费:当使用集群消费模式时,消息队列RocketMQ版认为任意一条消息只需要被集群内的任意一个消费者处理即可。
广播消费:当使用广播消费模式时,消息队列RocketMQ版会将每条消息推送给集群内所有的消费者,保证消息至少被每个消费者消费一次。
设置消息的消费方式,需要在订阅消息SDK中设置以下参数: properties.put(PropertyKeyConst.MessageModel, 消费方式); 消费方式:PropertyValueConst.CLUSTERING(集群消费)/PropertyValueConst.BROADCASTING(广播消费)
MQ问题整理
1.异步线程池也能实现,为什么用MQ而不是用线程来做?
线程池是解决单个机器上如何并发执行的问题,mq解决的是服务间如何通讯的问题。
2.MQ如何保证高可用?
不同的MQ有不同的处理机制,一般是通过主从,集群来保证消息高可用
3.如何保证消息不被重复消费
一般有两种方法,一种是保证消息消费逻辑的幂等性,一种是需要维护一个消费记录,用于消息是否被消费过。
4.如何保证消息消费的时候是幂等的
- 并发不高的时候,可以先查再保存,每个消息都会有唯一ID,查询数据如果存在就不保存
- 业务表或者建立消息表添加唯一约束
- 或者使用redis来对消息ID进行加锁处理
5.如何保证消息的可靠传输,数据丢失了怎么处理
数据可能在生产者端丢失,也可能MQ端丢失,或者消费者端丢失,一般采取消息确认回复来判断下一环节是否正确收到消息,MQ端还能开启持久化,保证数据丢失恢复。
6.如何保证消息消费的顺序性
在发送端,把同一个业务ID的消息发送到同一队列,在消费的过程中保证不被并发处理, consumer使用messageListenerOrderly的时候才能达到部分有序。