消息中间件面试(RocketMQ、RabbitMQ、Kafka、ActiveMQ)

前言

Github:https://github.com/yihonglei/message-middle

RabbitMQ基础:https://blog.csdn.net/yhl_jxy/category_6979741.html

RocketMQ基础:https://blog.csdn.net/yhl_jxy/category_6979742.html

Kafka基础:https://blog.csdn.net/yhl_jxy/category_6979739.html

一 为什么使用消息队列?

三个核心场景:解耦、异步、削峰。

1、解耦

接口调用方式:

假如有A、B、C、D四个系统,A系统有重要数据,通过接口调用下游B、C、D分别给他们不同的数据,

A系统负责人用烦躁的心情调了他们的接口,假如这个时候有其它系统也告诉他,哥们,我们也要数据,

我们提供了写入的接口,辛苦你调下,谁都让他调,A系统的负责人会哭的,他还要处理每个调用的超时等异常情况,

兄弟,你保证身体!

消息解耦方式:

A系统只需要把相应数据发个MQ,B、C、D订阅消息,消费做业务处理就行,如果再有人要让A系统负责人调下接口,

可以喝着咖啡,用愉悦的心情告诉他,订阅XXXTopic,自己消费就行。

2、异步

接口调用方式:

假如有A、B、C、D系统,用户触发调A系统,A系统调B、C、D系统,用户需要等60ms+30ms+500ms+200ms=790ms的时间,

快1s之后才能看到结果,可能A系统处理的数据是用户想看的,B、C、D的处理的数据可能是我们系统内部的一些记录数据之类

的,或者用户不需要实时看到的数据,这些数据可以异步处理,用户的耐心是有限的,别让用户等太久,得到用户的心太难,

但是失去确很容易。

消息异步方式:

A系统处理完核心的数据,马上就返回给用户结果,然后发送相应消息,B、C、D进行消费处理,将同步处理的业务化为

异步处理,目的是提高响应速度。

3、削峰

流量直接打到服务:

如果平时A系统流量比较小,但是高峰期或者做活动的时候,流量非常大,这个时候数据库承受不了这么大的压力。

一般可以用缓存或者MQ方式解决。

流量先到MQ,A系统消费处理:

流量都先打到MQ,由MQ缓冲,A系统负责从MQ根据自己数据库情况分批消费处理。

二 消息队列优缺点?

Kafka、ActiveMQ、RabbitMQ、RocketMQ 优缺点对比?

特性 可用性 消息可靠性 时效性 单机吞吐量 功能支持 应用领域
RocketMQ 分布式架构实现,基于Netty通信实现,非常高 经过参数优化配置,可以做到 0 丢失 ms 级 10 万级,支撑高吞吐 MQ 功能较为完善,还是分布式的,扩展性好 业务用得多
RabbitMQ 基于主从架构实现高可用,比较高 基本不丢,可以开启消息持久化功能 微秒级,这是 RabbitMQ 的一大特点,延迟最低 万级,比 RocketMQ、Kafka 低一个数量级 基于 erlang 开发,并发能力很强,性能极好,延时很低 业务用得多
ActiveMQ 同RabbitMQ 有较低的概率丢失数据 ms 级 同RabbitMQ MQ 领域的功能极其完备 业务用得多
Kafka 分布式,一个数据多个副本,非常高 同RocketMQ 延迟在 ms 级以内 10 万级,高吞吐 功能较为简单,主要支持简单的 MQ 功能 在大数据领域的实时计算以及日志采集被大规模使用

三 如何保证消息消费的幂等性?

在正常情况下,使用MQ就是生产消息,消费消息,然后给MQ确认,不同的消息队列发送的确认信息形式不同,

例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,kafka实际上有个offset的概念。

看上去很完美,但是,很多情况下可能存在重复消费,比如,就是因为网络原因闪断,ACK返回失败等等故障,

确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。

(因为消息重试等机制的原因,如果一个consumer断了,rocketmq有consumer集群,会将该消息重新发给其他consumer)。

针对重复消费一般在消费端处理,使用MQ如果消费端幂等性没做好,会产生很多脏数据,业务都可能错乱了。

常规处理消费幂等性方法:

1、数据库层面建立唯一索引,插入重复就异常,不会产生脏数据;

2、Redis的setnx,分布式锁形式,天然支持幂等性;

3、Redis里面建立唯一id,记录每次的消费,如果已经存在,则不再消费;

实际要根据业务情况处理幂等性,有必要的情况还需要做适当的补偿机制。

四 如何保证消息不丢?

使用MQ有些非核心场景,可以允许丢失,但是有些数据非常重要,如果用MQ处理,就需要保证你的消息不能丢。

消息丢失可能在生产端,压根可能没发出来,也有可能发到MQ了,丢失在MQ里面,还有可能在消费端没拉到消息。

如果用的RocketMQ,他帮我们提供了消息不丢的一些处理。

RocketMQ消息大致流程过程

producer(生产者生产消息) --》broker(存储消息) --》cunmser(消费消息)

producer如何保证消息不丢失:

1、默认情况下,可以通过同步的方式阻塞式的发送,check SendStatus,状态是OK,表示消息一定成功的投递到了Broker,

状态超时或者失败,则会触发默认的2次重试。此方法的发送结果,可能Broker存储成功了,也可能没成功。

2、采取事务消息的投递方式,并不能保证消息100%投递成功到了Broker,但是如果消息发送Ack失败的话,

此消息会存储在CommitLog当中,但是对ConsumerQueue是不可见的。可以在日志中查看到这条异常的消息,

严格意义上来讲,也并没有完全丢失。

3、RocketMQ支持 日志的索引,如果一条消息发送之后超时,也可以通过查询日志的API,来check是否在Broker存储成功。

broker如何保证消息不丢失:

1、消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的。

2、Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中。

3、Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失。

cunmser如何保证消息不丢失:

1、Consumer自身维护一个持久化的offset(对应MessageQueue里面的min offset),标记已经成功消费或者已经成功发回到broker的

消息下标。

2、如果Consumer消费失败,那么它会把这个消息发回给Broker,发回成功后,再更新自己的offset。

3、如果Consumer消费失败,发回给broker时,broker挂掉了,那么Consumer会定时重试这个操作。

4、如果Consumer和broker一起挂了,消息也不会丢失,因为consumer 里面的offset是定时持久化的,重启之后,

继续拉取offset之前的消息到本地。

五 如何保证消息顺序消费?

有时候我们发出的消息是按顺序发的,消费的时候也需要顺序消费,如果错乱了,会导致数据异常。

每种MQ通用处理办法,就是建多个queue,发送的时候保证需要顺序消费的消息都顺序的发送到同一个queue,

消费端需要按照队列的FIFO顺序消费,如果采用多线程消费,交给多个worker处理,就需要注意了,

多线程消费你保证不了每个线程处理的先后顺序,需要特别小心。

持续更新完善中......

发布了502 篇原创文章 · 获赞 358 · 访问量 118万+

猜你喜欢

转载自blog.csdn.net/yhl_jxy/article/details/105350892
今日推荐