是什么?
1.什么是队列?
队列是一种先进先出的数据结构。
数据结构
线性数据结构:
常用的:线性表、栈、队列、串等
非线性数据结构:
常用的:数组、广义表、树结构、图结构、堆
消息队列就是基础数据结构中的“先进先出”的一种数据结构。生活中的排队,先排的人先得到,就是典型的“先进先出”。
有什么用?
1.解藕
以我们的药房系统为例,有商品系统、订单系统、物流系统、支付系统等,用户下单后如果耦合的去调用各个系统,那么如果一个系统出现问题,下单的操作就无法完成。
当用消息队列的方式后,如果物流系统出现问题,需要时间去修复,但是我的下单操作是不受影响的,用户依然可以下单,当物流问题修复后,可以继续物流的操作。
2.异步
A调用B,B处理需要较长的时间,所以A不能一直等待B,但是又需要B的处理结果。之前的做法一般是给B一个callback。B处理完后回调A。
这时候用消息队列的话,A调用B,B处理完后,发消息给mq,mq再将消息转给A。
3.削峰/限流
假设每秒有3000个请求同步消息,只有两台机器,每台每秒只能处理1000个请求,那多出来的请求就会把系统弄崩溃。
这时候用消息队列的方式,请求都打到消息队列里,两台机器根据自己的能够处理的请求数去消息队列中拿数据,这样即便有每秒有8000个请求,那只是把请求放在消息队列中,去拿消息队列的消息由系统自己去控制,这样就不会把整个系统给搞崩。
消费者怎么得到消息队列的数据?
生产者将数据放到消息队列中,消息队列有数据了,主动叫消费者去拿(俗称push)
消费者不断去轮训消息队列,看看有没有新的数据,如果有就消费(俗称pull)
有啥问题?
1.高可用
无论是我们使用消息队列来做解耦、异步还是削峰,消息队列肯定不能是单机的,所以都得是集群/分布式的。
在集群模式的部署方式中,Master与Slave配对是通过指定相同的brokerName参数来配对,Master的BrokerId必须是0,Slave的BrokerId必须是大于0的数。一个Master下面可以挂载多个Slave,同一个Master下的多个Slave通过指定不同的BrokerId来区分。有4种部署方式:
部署方式 | 优点 | 缺点 | 备注 |
---|---|---|---|
单个Master模式 | 一旦Broker重启或者宕机时,会导致整个服务不可用,不建议线上环境使用; | - | - |
多个Master模式 | 配置简单,单个Master宕机或重启维护对应用无影响,在磁盘配置为RAID10时,即使机器宕机不可恢复情况下,由于RAID10磁盘非常可靠,消息也不会丢(异步刷盘丢失少量消息,同步刷盘一条不丢),性能最高。 | 单台机器宕机期间,这台机器上未被消费的消息在机器恢复之前不可订阅,消息实时性会收到影响。 | 当使用多master无slave的集群搭建方式时,master的brokerRole配置必须为ASYNC_MASTER。如果配置为SYNC_MASTER,则producer发送消息时,返回值的SendStatus会一直是SLAVE_NOT_AVAILABLE。 |
多Master多Slave模式——异步复制 | 即使磁盘损坏,消息丢失的非常少,但消息实时性不会受影响,因为Master宕机后,消费者仍然可以从Slave消费,此过程对应用透明,不需要人工干预,性能同多Master模式几乎一样。 | Master宕机,磁盘损坏情况,会丢失少量信息。 | - |
多Master多Slave模式——同步双写 | 数据与服务都无单点,Master宕机情况下,消息无延迟,服务可用性与数据可用性都非常高; | 性能比异步复制模式稍低,大约低10%左右,发送单个消息的RT会稍高,目前主宕机后,备机不能自动切换为主机,后续会支持自动切换功能。 | - |
在集群模式下,为了保证高可用,必须要保证备用Broker与主用Broker信息是一致的,在备用Broker初始化时设置的了定时任务,每个60秒调用SlaveSynchronize.syncAll()方法发起向主用Broker进行一次config类文件的同步,而消息数据的同步由主备Broker通过心跳检测的方式完成,每隔5秒进行一次心跳。 主用Broker提供读写服务,而备用Broker只提供读服务。
2.数据一致性,消息幂等、数据重复消费问题
A系统发送完消息,不管其他系统是否成功,是不对的。
两个方案:
A系统接收其它系统处理结果的mq消息回执
其它系统提供一个接口,用于A系统主动去查其处理结果。
消息幂等
对于确保消息在生产者和消费者之间进行传输而言一般有三种传输保障(delivery guarantee):At most once,至多一次,消息可能丢失,但绝不会重复传输;At least once,至少一次,消息绝不会丢,但是可能会重复;Exactly once,精确一次,每条消息肯定会被传输一次且仅一次。对于大多数消息中间件而言,一般只提供 At most once 和 At least once 两种传输保障,对于第三种一般很难做到,由此消息幂等性也很难保证
全局幂等:
如以订单号为唯一标识,下游服务设置一个去重。
解决方案:分布式事务
3.数据丢失问题
将数据写到消息队列上,系统B和C还没来得及取消息队列的数据,服务就挂了。如果没有做任何的措施,我们的数据就丢了。
所以需要数据持久化。
同步刷盘:在消息到达MQ后,RocketMQ需要将数据持久化,同步刷盘是指数据到达内存之后,必须刷到commitlog日志之后才算成功,然后返回producer数据已经发送成功。
异步刷盘:是指数据到达内存之后,返回producer说数据已经发送成功。,然后再写入commitlog日志。
commitlog:
commitlog就是来存储所有的元信息,包含消息体,类似于Mysql、Oracle的redolog,所以主要有CommitLog在,Consume Queue即使数据丢失,仍然可以恢复出来。
consumequeue:记录数据的位置,以便Consume快速通过consumequeue找到commitlog中的数据
5.如何保证绝对的顺序
简单的例子,有一条数据,先执行了修改,后执行了删除,并且都发送到了你自己的那个消息队列中,如果不按照顺序读,先读取了删除,再读取修改就会出现问题。
怎么做技术选型
RocketMQ是一款分布式、队列模型的消息中间件,具有以下特点:
1、支持严格的消息顺序;
2、支持Topic与Queue两种模式;
3、亿级消息堆积能力;
4、比较友好的分布式特性;
5、同时支持Push与Pull方式消费消息