参考资料:https://www.cnblogs.com/williamjie/p/9481780.html
https://blog.csdn.net/maihilton/article/details/80928661
1、为什么使用消息队列(优点)?
解耦、异步、削峰
在传统模式中系统间的耦合性太强,需频繁的修改代码,中间件介入之后,可以将消息写入消息队列,需要消息的系统可以自己从消息队列中订阅,降低耦合。
传统模式中,一些非必要的业务逻辑以同步的方式运行,耗费时间。中间件模式可以让非必要的业务逻辑以异步的方式运行,加快响应速度。
传统模式在并发量大的时候,所有请求直接怼到数据库,造成数据库连接异常。中间件模式可以将请求放到消息队列中,系统可以从队列中拉取消息,以达到削峰的目的。
2、消息队列缺点?
- 降低系统可用性
- 增加系统复杂性
3、不同消息队列的特点及选型?
比较ActiveMQ、RabbitMQ、RocketMQ、Kafka
4、如何保证消息队列是高可用的(其实就是问MQ的分布式集群搭建)?
(要求:在回答高可用的问题时,应该能逻辑清晰的画出自己的MQ集群架构或清晰的叙述出来)
单机集群:
镜像集群:
首先我们知道RabbitMQ有三种模式:单机模式、普通集群模式、镜像集群模式。单机模式就是demo,没人会在生产上使用。普通集群模式就是在多台机器上启动多个RabbitMQ实例,但是你创建的queue只会放在一个rabbit实例上,每个节点仅有相同的元数据,即队列结构。当消费者消费的时候,如果连接到某一个实例,那么这个实例会从queue所在的实例拉取数据过来。但是这样做会导致要么消费者每次连接一个实例然后拉取数据,要么固定连接那个queue所在的实例消费数据,前者有数据拉取的开销,后者导致单实例性能瓶颈。而且如果那个存放queue的实例宕机了,会导致其他实例无法拉取数据,且如果你开启了消息持久化,让RabbitMQ落地存储消息的话,消息不一定会丢,但得等这个实例恢复了才能继续从这个queue拉取数据,这种方案主要是提高吞吐量,就是说让集群中多个节点来服务某个queue的读写操作。好在RabbitMQ有个选项可以配置支持队列复制,比如在有5个节点的集群里,可以指定某个队列的内容在2个节点上进行存储,从而在性能与高可用性之间取得一个平衡,这就是RabbitMQ的镜像集群模式。镜像集群模式与普通模式的不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据的时候临时拉取。但是该模式的副作用也很明显,除了降低系统性能之外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中使用,一个队列想做成镜像队列,需要先设置policy(策略),然后客户端创建队列的时候,RabbitMQ集群根据“队列名称”自动设置是普通队列或是镜像队列。
具体如下:队列通过策略来使能镜像。策略能在任何时刻改变,RabbitMQ队列也尽可能的将队列随着策略变化而变化;非镜像队列和镜像队列之间是有区别的,前者缺乏额外的镜像基础设施,没有任何slave,因此会运行的更快。为了使队列称为镜像队列,你将会创建一个策略来匹配队列。设置策略有两个键:ha-mode、ha-params。
5、如何保证消息不被重复消费(保证消息队列的幂等性)?
(这个问题可以根据具体业务场景来答,没有固定的答案)
可以先说一下为什么会造成重复消费?
首先无论是哪种消息队列,造成重复消费的原因其实都是类似的。正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。只是不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有一个offset概念,简单说就是每一个消息都有一个offset,kafka消费过消息后,需要提交offset,让消息队列知道自己已经消费过了。那造成重复消费的原因?无非就是网络传输等故障,确认消息没有传送到消息队列,导致消息队列不知道自己已经消费过消息了,再次将该消息分发给其他消费者。
如何解决?这个问题针对业务场景来答
(1)比如,你拿到这个消息做数据库的insert操作,那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,会导致主键冲突,避免数据库出现脏数据。
(2)比如,你拿到这个消息做Redis的set操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就是幂等操作。
(3)如果上面两种情况都不行,上大招。准备一个第三方介质,来做消费记录。以Redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入Redis,那消费者开始消费前,先去Redis中查询有无消费记录即可。
(kafka如何保证消息不会丢失?https://hacpai.com/article/1501331595492)
6、如何保证消息的可靠性传输?
(数据丢失怎么办?)
可靠性传输,每种MQ都要从三个角度来分析:生产者丢失数据、消息队列丢失数据、消费者丢失数据
RabbitMQ:
(1)生产者丢失数据:RabbitMQ提供transaction和confirm模式来确保生产者不丢信息。transaction机制就是说,发送消息前,开启事物,然后发送消息,如果发送过程中出现什么异常,事物就会回滚,如果发送成功就提交事物。缺点就是吞吐量下降了,因此实际生产中,confirm模式使用居多。一旦channel进入confirm模式,所有在该通道上面发布的消息都将会被指派一个唯一的ID,一旦消息被投递到所匹配的队列之后,RabbitMQ就会发送一个Ack给生产者(包含ID),这就使得生产者知道消息已经正确到达目的队列了。如果RabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。
(2)消息队列丢失数据:处理消息队列丢失数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果持久化磁盘之前,RabbitMQ阵亡了,那么生产者收不到Ack信号,生产者就会自动重发。
(3)消费者丢失数据:消费者丢失数据一般是采用了自动确认消息模式。这种模式下,消费者会自动确认收到消息,这时RabbitMQ会立即将消息删除,这种情况下如果消费者出现异常而没能处理该消息,就是丢失该消息。至于解决方案,采用手动确认消息即可。
7、如何保证消息的顺序性?
如果存在多个消费者,那么就让每个消费者对应一个queue,然后把要发送的数据全部放到一个queue,这样就能保证所有的数据只到达一个消费者从而保证每个数据到达数据库都是顺序的。
8、两种消息模型:点对点 和发布/订阅?
https://blog.csdn.net/nsxqf/article/details/80600330
9、RabbitMQ架构图?
几个概念:
Broker:它提供一种传输服务,它的角色就是维护一条生产者到消费者的路线,保证数据能按照指定的方式进行传输。
Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
Queue:消息的载体,每个消息都会被投到一个或多个队列。
Binding绑定:它的作用就是把exchange和queue按照路由规则绑定起来。
Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
Producer:消息生产者,就是投递消息的程序。
Consumer:消息消费者,就是接受消息的程序。
Channel:消息通道,在客户端的每个连接里,可建立多个channel。
从示意图可以看出消息生产者并没有直接将消息发送给消息队列,而是通过建立与Exchange的Channel,将消息发送给Exchange,Exchange根据规则,将消息转发给指定的消息队列。消费者通过建立与消息队列相连的Channel,从消息队列中获取信息。
这里谈到的Channel可以理解为建立在生产者/消费者和RabbitMQ服务器之间的TCP连接上的虚拟连接,一个TCP连接上可以建立多个Channel。
10、RabbitMQ交换器种类?
交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。
RabbitMQ中交换机有四种类型,分别是Direct、Topic、Headers、Fanout
Direct:direct类型的行为是“先匹配,后投送”,即在绑定时设定一个routing_key,消息的routing_key匹配时,才会被交换机投递到绑定的队列去;
Topic:按规则转发(最灵活);
Headers:设置header attribute参数类型的交换机;
Fanout:转发消息到所绑定队列。
11、RabbitMQ队列与消费者的关系?
多个消费者监听同一个队列时,会轮流获得消息。
12、RabbitMQ、kafka消息可靠性?
Kafka: https://blog.csdn.net/u013256816/article/details/71091774
当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别:
1(默认):这意味着producer在ISR中的leader已成功收到数据并得到确认。如果leader宕机了,则会丢失数据。
0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
-1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时(前面ISR那一节讲到,ISR中的成员由于某些情况会增加也会减少,最少就只剩一个leader),这样就变成了acks=1的情况。
RabbitMQ:http://cache.baiducontent.com/c?m=9d78d513d99010f604b0d1205a4082310e55f0743ca1c7140f8bce1ec4735b305016e3ac57520705a3d20d1116dc384beb802104331450b08f8fc814d2e1d46e6dc9303503019b0457960eafbc1e7e927cd100aeb81990ebad6d8eeccfd38e53049d03512c82a7805d&p=882a921a8edd5db70fbe9b7c58&newp=9e578315d9c452fc57efcf246153d8224216ed603ad0c44324b9d71fd325001c1b69e7bf23221104d3ce7a6704a4425be9f33175361766dada9fca458ae7c43b&user=baidu&fm=sc&query=acknowledge%2Dmode+%B2%BB%CD%AC%D6%B5&qid=916238fe0004fb75&p1=1
发送
ConfirmCallback ReturnCallback
消费
AcknowledgeMode.NONE:自动确认
AcknowledgeMode.AUTO:根据情况确认
AcknowledgeMode.MANUAL:手动确认