1、为什么要用MQ?
关键词:解耦、异步、削峰
-
解耦:比如A要发数据给BCD,通过接口调用发送。此时新增E,或C现在不需要这个数据了,此时就很难受了。如果使用MQ,A发送消息到MQ,谁需要就就去订阅。A完全不需要考虑给谁发送数据,不需要维护这个代码,也不需要考虑别人是否调用成功、失败超时等情况。
-
异步
-
削峰:减少高峰时刻对服务器的压力。
2、MQ的优缺点
优点上面说了,解耦、异步、削峰
缺点:
-
系统可用性降低。万一MQ挂了,整套系统就崩溃了。
-
系统负责度提高。新增一套MQ系统,引入了很多问题,比如消息重复消费、消息丢失等等。
-
一致性问题。A发送数据给BCD,万一BD成功了,C失败了,就会导致数据不一致。
3、Kafka、ActiveMQ、RabbitMQ、RocketMQ都有什么区别?
-
吞吐量:kafka和RocketMQ支持高吞吐,amq和rmq低一个数量级,但是rmq延迟最低。
-
持久化消息:amq和rmq都支持。在不可抗力因素下挂掉了,消息不会丢失。
-
高并发:rmq最高
-
关注度:rmq比kfk成熟,可用性、稳定性、可靠性都高于kfk
4、如何保证高可用性?
以rmq为例,有3种模式:单机模式、普通集群模式、镜像集群模式
单机模式:生产一般不使用
普通集群模式:多台机器启动多个rmq实例。创建的queue,只会放在一个rmq实例上,但是每个实例都同步queue的元数据(通过元数据,可以找到queue所在实例)。当消费的时候,实际上当前机器上实例会从queue实际所在的实例上去拉取数据。此方案主要是提高吞吐量,让集群中多个节点来服务某个queue的读写操作。
镜像集群模式:每个创建的queue,无论是元数据还是消息体都会存在于多个实例上,也就是说,每个rmq节点都有queue的一个完整镜像。
kafka:由多个broker组成,每个broker是一个节点;当创建一个topic后,这个topic可以划分为多个partition,每个partition可以存在不同的broker上,每个partition就存放一部分数据。这就是一个天然的分布式消息队列,就是说一个topic的数据,分散保存再不同服务器上。
kafka8.0之后,提供了HA机制,就是replica副本机制。每个partition的数据库都会同步到其他机器上,并选举一个leader,生产者和消费者都与leader打交道,然后其他的副本就是follower。leader挂掉的时候,会从follower中重新选举一个新的leader。
5、如何保证消息的可靠传输,消息丢了怎么办?
数据的丢失分为3个方面:生产者、消费者、MQ。
-
生产者丢失:
生产者发送消息到rmq时,数据在半路就丢失了(有可能是网络问题)。可以选择事务功能,但是这样吞吐量会下来。一般来说,在生产者这边设置开启confirm模式,每次写的消息都会分配为一个id,rmq收到消息回传一个ack消息,如果rmq没能处理这个消息,会回调一个nack接口。结合这个机制,可以加入超时重发。
事务机制和confirm机制的最大不同是:事务是同步的,confirm机制是异步的。
-
MQ丢失:
rmq自己弄丢了数据。这个必须开启rmq的持久化,就是消息写入之后会持久化到磁盘。哪怕挂了,恢复之后会自动读取之前存储的数据。
设置持久化的步骤:
1、创建queue时设置为持久化,保证持久化queue的元数据。
2、发送消息的时候设置deliveryMode=2,保证持久化消息数据。配合confirm支持后,只有消息被持久化后,才会通知生产者ack了。
-
消费者丢失:消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,那么rmq认为你消费了。这个时候得用rmq提供ack机制,通过api调用关闭rmq的自动ack机制,消费者需要在每次消费完成后手动ack,就可以保证消费者已经消费完成。
6、如何解决消息队列的延时以及过期失效问题?队列满了以后怎么处理?有几百万消息持续积压几小时,该怎么解决?
消息积压处理办法:
kafka:临时紧急扩容,将现有consumer停掉,新建一个topic,partition是原来的10倍,临时建立好原先10倍的queue数量。然后写一个临时分外数据的consumer,部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍queue中。接着临时征用10倍的机器来部署consumer,相当于把queue资源和consumer资源扩大10倍,以正常的10倍速度消费数据。等消费完积压数据后,恢复原先部署的架构,重新用原来的consumer机器来消费消息。
rmq:设置过期时间ttl。但是这样会导致大量的数据丢失,采取另外一种方案,就是批量重导。直接丢弃大量积压的数据,等到低峰时间段,写程度把丢失的那批数据一点点查出来,重新灌入mq,把数据补回来。假设10k个订单积压在mq里面没有处理,其中1k个订单都丢了,那么只能写程序把那1k个订单给查出来,手动发到mq再补一次。
7、给你机会设计一个消息队列,怎么设计?
几个角度:
-
支持可伸缩性,需要的时候快速扩容,就可以增加吞吐量和容量。分布式参考kafka的设计理念,broker->topic-partition,每个partition放一个机器,存一部分数据。当资源不够的时候,给topic增加partition,然后数据迁移,增加机器,就可以存放更多数据了。
-
存储方式。存到磁盘,顺序写,可以就没有磁盘随机读写的寻址开销了。
-
高可用性。参考kafka,多副本->leader&follower->leader挂了之后重新选举leader对外服务。
-
0丢失。confirm机制,持久化,手动ack;