面试整理——MQ

MQ

第一节 待整理

问:RocketMQ的特点有哪些?

问:RocketMQ 由哪些角色组成?

问:rocketmq 的模块功能?

问:rocketmq 的高可用及高性能?

问:消费者获取消息有几种模式?

问:说说你对Consumer的了解?

问:介绍下Kafka?Kafka的优点?Kafka 的设计是什么样的呢?

问:什么情况会导致Kafka运行变慢?

问:说一下Kafka消费者消费过程?Kafka的消费者如何消费数据?

问:Kafka新建的分区会在哪个目录下创建?

问:Kafka 与传统消息系统之间有三个关键区别?

问:请详细说一下推送模式和拉取模式?

问: kafka消息发送的可靠性机制有几种?

问:Kafka如何判断一个节点是否存活?

问:Kafka 数据存储设计?

问:为何需要Kafka集群?

问:kafka 同时设置了 7 天和 10G 清除数据,到第五天的时候消息达到了 10G,这个时候 kafka将如何处理?

面经:如何保障消息100%投递成功、消息幂等性?

https://www.toutiao.com/i6672235084336071179

你知道如何保障生产端100%消息投递成功吗?

如上图:

1)订单服务投递消息给MQ中间件

2)物流服务监听MQ中间件消息,从而进行消费

如何保障订单服务把消息成功投递给MQ中间件?

你知道如何保障生产端100%消息投递成功吗?

一般发送消息就是这么写的,如果MQ服务器突然宕机了会出现什么情况?是不是我们订单服务发过去的消息全部没有了吗?是的,一般MQ中间件为了提高系统的吞吐量会把消息保存在内存中,如果不作其他处理,MQ服务器一旦宕机,消息将全部丢失。这个是业务不允许的,造成很大的影响。

1.消息持久化

有经验的小伙伴会说,我知道一个方法就是把消息持久化,RabbitMQ中发消息的时候会有个durable参数可以设置,设置为true,就会持久化。

你知道如何保障生产端100%消息投递成功吗?

这样的话MQ服务器即使宕机,重启后磁盘文件中有消息的存储,这样就不会丢失了吧。是的这样就一定概率的保障了消息不丢失。

但还会有个场景,就是消息刚刚保存到MQ内存中,但还没有来得及更新到磁盘文件中,突然宕机了。这个场景在持续的大量消息投递的过程中,会很常见。

那怎么办?我们如何作才能保障一定会持久化到磁盘上面呢?

2.confirm机制

上面问题出现在,没有人告诉我们持久化是否成功。好在很多MQ有回调通知的特性,RabbitMQ就有confirm机制来通知我们是否持久化成功?

你知道如何保障生产端100%消息投递成功吗?

confirm机制的原理:

1)消息生产者把消息发送给MQ,如果接收成功,MQ会返回一个ack消息给生产者

2)如果消息接收不成功,MQ会返回一个nack消息给生产者

你知道如何保障生产端100%消息投递成功吗?

上面的伪代码,有两个处理消息方式,就是ack回调和nack回调。

这样是不是就可以保障100%消息不丢失了呢?

我们看一下confirm的机制,试想一下,如果我们生产者每发一条消息,都要MQ持久化到磁盘中,然后再发起ack或nack的回调。这样的话是不是我们MQ的吞吐量很不高,因为每次都要把消息持久化到磁盘中。写入磁盘这个动作是很慢的。这个在高并发场景下是不能够接受的,吞吐量太低了。

所以MQ持久化磁盘真实的实现,是通过异步调用处理的,他是有一定的机制,如:等到有几千条消息的时候,会一次性的刷盘到磁盘上面。而不是每来一条消息,就刷盘一次

所以comfirm机制其实是一个异步监听的机制,是为了保证系统的高吞吐量,这样就导致了还是不能够100%保障消息不丢失,因为即使加上了confirm机制,消息在MQ内存中还没有刷盘到磁盘就宕机了,还是没法处理。

说了这么多,还是没法确保,那怎么办呢???

3.消息提前持久化 + 定时任务

其实本质的原因是无法确定是否持久化?那我们是不是可以自己让消息持久化呢?答案是可以的,我们的方案再一步的演化。

你知道如何保障生产端100%消息投递成功吗?

上图流程:

1)订单服务生产者再投递消息之前,先把消息持久化到Redis或DB中,建议redis,高性能。消息的状态为发送中。

2)confirm机制监听消息是否发送成功?如ack成功消息,删除redis中此消息。

3)如果nack不成功的消息,这个可以根据自身的业务选择是否重发此消息。也可以删除此消息,由自己的业务决定。

4)这边加了个定时任务,来拉取隔一定时间了,消息状态还是为发送中的,这个状态就表明,订单服务是没有收到ack成功消息。

5)定时任务会作补偿性的投递消息。这个时候如果MQ回调ack成功接收了,再把redis中此消息删除。

这样的机制其实就是一个补偿机制,我不管MQ有没有真正的接收到,只要我的redis中的消息状态也是为【发送中】,就表示此消息没有正确成功投递。再启动定时任务去监控,发起补偿投递。

当然定时任务那边我们还可以加上一个补偿的次数,如果大于3次,还是没有收到ack消息,那就直接把消息的状态设置为【失败】,由人工去排查到底是为什么?

这样的话方案就比较完美了,保障了100%的消息不丢失(当然不包含磁盘也坏了,可以做主从方案)。

不过这样的方案,就会有可能发送多次相同的消息,很有可能MQ已经收到了消息,就是ack消息回调时出现网络故障,没有让生产者收到。那就要要求消费者一定在消费的时候保障幂等性。至于什么是幂等性,如何设计幂等?请看海量订单产生的业务高峰期,如何避免消息的重复消费?

面经:海量订单产生的业务高峰期,如何避免消息的重复消费?

RabbitMQ

MQ消息开启Trace

  1. 连接linux服务器

  2. 输入指令 rabbitmqctl status 确认MQ状态

    image-20210331171126692

  3. 通过指令 rabbitmq-plugins list 查看MQ安装的插件,E* 表示已启用

    image-20210331171303814

  4. 找到 rabbitmq_tracing 确认是否启用,若未启用,则通过指令 rabbitmq-plugins enable rabbitmq_tracing 开启。

    image-20210331171436966

  5. 通过指令 rabbitmqctl trace_on 开启trace。

  6. 虚拟主机 server 开启trace:rabbitmqctl trace_on -p server ,添加完成后会多一个交换器:

    image-20210331171813344

  7. RabbitMQ管理平台新建一个Trace,添加trace追踪文件信息:

    image-20210331172359303

  8. 创建的Trace文件:

    image-20210331172219234

  9. 重新触发消息发送。

  10. 查看步骤8的Trace文件,确认消息是否发送。

添加Trace时报错

1
2
3
4
5
6
7
8
9
10
2021-03-31 20:00:01.475 [info] <0.19340.4> accepting AMQP connection <0.19340.4> (172.24.19.124:35267 -> 10.20.18.81:5672)
2021-03-31 20:00:02.223 [info] <0.19340.4> connection <0.19340.4> (172.24.19.124:35267 -> 10.20.18.81:5672): user 'fa6' authenticated and granted access to vhost 'client'
2021-03-31 20:01:08.413 [info] <0.2285.0> Disabling tracing for vhost 'server'
2021-03-31 20:01:11.453 [info] <0.2285.0> Enabling tracing for vhost 'server'
2021-03-31 20:01:11.481 [error] <0.19570.4> CRASH REPORT Process <0.19570.4> with 0 neighbours exited with reason: no match of right hand value {error,not_allowed} in rabbit_tracing_consumer:init/1 line 58 in gen_server:init_it/6 line 352
2021-03-31 20:01:11.482 [error] <0.19569.4> Supervisor {<0.19569.4>,rabbit_tracing_consumer_sup} had child consumer started with rabbit_tracing_consumer:start_link([{vhost,<<"server">>},{name,<<"server-trace-log">>},{format,<<"text">>},{pattern,<<"#">>},{<<"for...">>,...},...]) at undefined exit with reason no match of right hand value {error,not_allowed} in rabbit_tracing_consumer:init/1 line 58 in context start_error
2021-03-31 20:02:10.759 [warning] <0.10199.4> closing AMQP connection <0.10199.4> (172.24.20.49:54661 -> 10.20.18.81:5672):
missed heartbeats from client, timeout: 30s
2021-03-31 20:02:34.874 [info] <0.19635.4> accepting AMQP connection <0.19635.4> (172.24.20.49:49512 -> 10.20.18.81:5672)
2021-03-31 20:02:35.021 [info] <0.19635.4> connection <0.19635.4> (172.24.20.49:49512 -> 10.20.18.81:5672): user 'fa6' authenticated and granted access to vhost 'client'

MQ的tracing插件默认使用guest用户,给它开下server权限应该就行了

image-20210331214055800

image-20210331214106354