1 消息队列的常见使用场景(为什么使用消息队列)
**解耦,异步,削峰**
1.1解耦
不用MQ的耦合场景:假设有系统A发送数据到B,C,D三个系统,此时增加系统E,则系统A需要修改对应代码。若是B系统弃用。则也需要修改A系统代码。
使用MQ的解耦场景:若是加入MQ消息队列系统,系统A就可以将需要发送的数据发送到消息队列,B、C、D系统均可以自行消费消息队列里边的数据。此时若是加入E系统,则自行消费消息队列里边的数据就行。若是弃用B、C、D、E,取消对消息队列数据的消费即可。此时,系统A和其他系统彻底解耦。
1.2异步
不使用MQ的同步高延时场景:用户对系统A发起请求,并且系统A调用系统B、C、D三个系统,总耗时将近1秒。并且B、C、D三个系统的处理结果并不及时同步返回给用户。这样用户使用系统A发送请求需要近一秒才有返回,体验极差。
使用MQ的异步接口性能优化:若是在系统A和B、C、D之间加入消息队列,则用户请求系统A之后,系统A只需将需要调用B、C、D系统的三条消息发送到消息队列中,B、C、D系统只需监听各自需要的数据类型,若是有消息进行消费即可。此时对于用户来说调用系统A只是花费了20ms,几乎是无感知的。用户体验极好。
1.3削峰
不使用MQ时高峰期系统崩溃的场景:每天的12点左右,用户对系统A请求为每秒5000条,但是MySQL数据库每秒仅能处理2000条数据,此时系统崩溃。
使用MQ进行削峰的场景:将用户的大量请求发送到消息队列中,每秒只是向A系统发送2000条,则确保了A系统的稳定性,不会崩溃。虽然有消息的积压,但是等高峰期一过,则将会被快速的消费掉。
2 消息队列的优点和缺点
2.1优点:
上边说过的解耦,异步,削峰
2.2缺点:
系统可用性降低:一旦MQ出现故障,则整个系统崩溃,A无法发送消息,BCD无法消费消息,无法正常运转。系统的复杂性变高:多加了一个系统,便有更多的问题,比如如何保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?一致性问题:当用户请求系统A时,消息发送到MQ成功即给用户返回成功。但是系统B、C消费消息成功,但是系统D处理失败。此时结果应为失败,但是已给用户返回成功。
3 Kafka、activemq、rabbitmq、rocketmq都有什么优缺点啊
4 如何保证消息队列的高可用性
4.1RabbitMQ的高可用性
基于主从做高可用
rabbitmq有三种模式:单机模式,普通集群模式,镜像集群模式
1.单机模式:用作demo,或者本地测试用。
2.普通集群模式:
假如rabbitmq集群有三台机器,此时向集群中的MQ-2机器写入一条消息,则MQ-1和MQ-3只会同步这个queue的元数据,即这条消息存在哪个机器上等一些配置信息,并不会同步实际数据。当消费者在MQ-3上请求消费这条消息时,MQ-3会根据元数据的信息向MQ-2请求这条queue的实际数据并将它返回给消费者。
3.镜像集群模式:
假如rabbitmq集群有三台机器,此时向集群中的任意一个节点发送消息,那么这条消息会被同步到其余的节点。当消费者消费消息的时候,可以从任意一个节点消费消息。
任何一个节点宕机,其他节点上还包含了完整的数据,消费者可以读取并消费数据。
4.2Kafka的高可用性
**Kafka是一个纯分布式的MQ架构。**一个kafka集群是由多个broker组成,每个broker是一个节点;每创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition存一部分数据。
就是说每个topic的数据是分开存放在多个机器上的,每个机器存放一部分数据。
高可用性:每个partition都有多个副本,多个副本的数据相同,并在多个副本中选举一个成为leader,其余的则为follower。生产者和消费者只与leader进行交互,即只有leader对外有读写的服务。写入数据到leader的时候,leader会将数据同步到它的follower上去,即follower只是被动同步leader的数据。此时高可用架构成型,若是某一台leader机器宕机,kafka会感知到并且从这个leader的follower机器中重新选举出一个leader。
5 如何保证消息不被重复消费
5.1为什么会有重复数据
kafka消费端可能出现的重复消费的问题:
offset:kafka会给每条消息分配表示消息顺序的唯一标识,这个标识就叫offset。
消费者消费数据是按照offset的顺序去消费的。消费者会按照一定的时间周期提交offset到zookeeper,表示自己消费到了哪条数据。
若是在消息2消费完之后,但是并没有到提交offset的时间,此时发生故障,断开连接亦或消费端重启。恢复正常之后,消费者向kafka请求继续消费数据,此时kafka查看zookeeper为消息1,2,3消费之前。此时kafka将会把消息1,2,3再次发送给消费者。导致消息1,2被重复消费。
5.2如何保证消息不被重复消费(保证消息的幂等性)
- 消费端直接判断消息是否重复,舍弃重复的消息。
- 在内存中用redis做一个记录,每次收到消息的时候进行比较。
- 基于数据库的唯一键来保证
6 如何避免消息的丢失
6.1 RabbitMQ
可能的情况:
生产者丢失数据:写消息到RabbitMQ的时候,消息在网络传输中丢失。并没有成功写入RabbitMQ。RabbitMQ丢失数据:RabbitMQ收到消息之后,暂存在在即的内存里。在消费者消费之前RabbitMQ故障重启,导致数据丢失。消费者丢失数据:消费者取到数据,还未消费,消费者故障重启,导致数据丢失。
解决方案:
1.生产者丢失数据:
1)使用RabbitMQ提供的事务功能:在 生产者发送消息时创建事务(channel.txSelect),若是消息发送失败,则回滚事务(channel.txRollback),重新发送这条消息。消息发送成功,则提交事务(channel.txCommit)。
事务机制是同步的,生产者发送的各个消息会同步阻塞,等待返回结果。会影响生产发送消息的吞吐量。
2)confirm模式:可以把对应的channel设置成confirm模式,在RabbitMQ接收到之后,会回调生产者的回调接口,告知生产者消息是否发送成功。
生产者主要使用confirm机制保证消息不丢失,它是异步的模式,不会阻塞,不影响性能。
2.RabbitMQ丢失****数据
开启RabbitMQ的持久化,设置持久化又两个步骤:
1)在创建queue的时候设置为持久化的。这样不会持久化数据,只会持久化queue的元数据。
2)发送消息的时候将deliveryMode设置为2,即将消息设置为持久化的。
3.消费者丢失数据
消费者消费完成后回调rabbitMQ发送ack确认消息。
6.2 Kafka
1.消费者丢失数据
kafka会自动提交offset,只要关闭自动提交,处理完之后手动提交,就可以保证数据不会丢。
2.kafka丢失数据
当某个broker宕机,恰好这个broker是一个leader,数据还没有被follower同步,此时重新选举leader之后,就少了一些数据。
解决方案,设置以下四个参数:
topic设置replication.factor参数:这个值需要大于1,即为每个partition必须有两个副本。
kafka服务端设置min.insync.replicas参数:这个值需要大于1,要求一个leader至少感知到一个follower与自己保持联系。
producer端设置acks=all:要求每个数据,写入所有replica之后,才认为是写成功了。
producer端设置retries=MAX:一旦写入失败,无限重试。
3.生产者丢失数据
当设置acks=all时,生产者不会丢失数据。
7 如何保证消息的顺序性
7.1 Rabbit可能出现的消息顺序不正确的情况及解决方案
1.消息顺序不正确的情况:
当RabbitMQ收到消息发送给消费者的时候,若是有多个消费者,且把消息发给了不同的消费者。此时,消息的顺序性没法保证,任何一个都可能先执行。
2.解决方案:
一个queue对应一个消费者,在同一个queue内保证消息的顺序性即可。
7.2 kafka可能出现的消息顺序不正确的情况及解决方案
1.情况
在kafka中写入同一个partition的数据一定是有顺序的,生产者在写入消息的时候可以指定一个key,拥有这个key的消息会被发送到同一个partition中。
一个partition的消息只会发送给一个消费者,三个partition的消息只会分别发送给三个消费者,若是有第四个消费者,那么它将收不到消息。
问题出在消费者多线程消费消息,消费者接收到的消息是有序的,但是消费者消费消息的多线程无法确定消息的有序性。
2.解决方案
在消费消息之前,将同一个key的消息放到同一个内存队列,由同一个线程消费。
8 如何解决消息队列的延时以及过期失效问题
8.1 消息积压了几百万条在消息队列怎么办
- 修复故障的consumer,使其可以正常工作。
- 新建一个topic,partition是原来的10倍,临时建立原先设备的queue数量
- 写一个临时分发的consumer程序,消费积压的消息,并均匀存储到临时建立的新的queue
- 临时征用10倍的机器部署consumer,每一批consumer消费一个临时的queue的数据
- 这样就会以10倍的速度消费掉积压的数据
8.2 RabbitMQ,设置了过期时间,消息过期清除了怎么办
这个时候只能批量重新导入,写一个程序将丢失的数据查出来,重新写入MQ。
8.3 消息积压再MQ里,长时间没处理掉,快满了怎么办
这个是第二个问题没有及时处理,只能写临时的消费者,直接写到其他的临时的MQ里边或者直接舍弃消息,快速消费消息,等到峰值过去,再重新写。