解耦、 异步、削峰
解耦:我有ABCD四个系统,A要分别发送数据给BCD,如果C不要了 我是不是得取消?
如果又来个E 我是不是又要发给E?
而且如果C挂了怎么办 那我还得整个重发机制
所以不如我直接弄个消息队列 A把数据发给消息队列 BCD直接去取即可
异步:用户点击提交以后,我得自己调用一次数据库,然后调用B系统完成相关操作,再给用户返回成功信息,那响应时间长
所以我不如用户点提交后 我把数据放消息队列中,然后直接给用户返回成功,那么用户体验感就会好很多,剩下的让B系统收到消息后去处理,做数据库操作即可
再者,我的消息不必即发即收,我随时发都可以,B想要的时候再收就行了
削峰:如果说比如中午A系统需要执行每秒5000条的sql请求,mysql可能会直接被打死。但是过了中午 到了下午 他每秒钟就只需要执行每条10条sql的请求
中午的时候呢 我们可以把每秒5000的请求写入到mq里面,系统从mq里面慢慢拉去请求,每秒钟拉取2000个,不要超过自己的每秒最大处理量 保证系统A不挂掉。这样下去 可能会有几十万甚至几百万的积压, 其实这是没问题的 只要高峰期一过,系统A 很快就会将积压的消息解决掉
MQ带来的问题:
1.系统可用性降低、MQ一旦故障,系统A就没办法发送消息给MQ了,就后面的系统BCD也没办法消费到消息
2.导致系统复杂性增加,系统A本来只需要给B发送一条数据即可,但是因为系统A与MQ协调出现问题,不小心把同一条数据给系统B发送了两次,导致系统B内部插入了两条一模一样的数据
3.消息在MQ内部丢失
4.一致性问题,有人给A发送了消息,本来是ABCD都要完成处理,但是D出了问题,没有走完逻辑,但在A这里已经返回了成功结果
activeMQ、RabbitMQ、RocketMQ、kafka各自有什么区别?
activeMQ是每秒万级的吞吐量、有可能丢消息。现在社区对他的维护越来越小
RabbitMq,万级,源码基于erlang,延迟最低,高可用,开源提供管理界面非常好用。社区非常活跃。
RacketMQ,阿里开源,10万级,topic可以达到几百,几千个级别,源码是java,阿里是java系
kafka,10万级别,功能比较简单,适合大数据领域和日志采集
中小型公司用rabbitMQ,好维护 不容易黄,大型公司用RocketMq,有技术实力去维护
如果是大数据领域的实时计算,用kafka是业内标准,几乎是全世界的事实性规范
怎么提高消息队列高可用?
高可用:“高可用性”(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。
意思是宕机之后不用过多久马上就能用
Rabbit不是分布式的,镜像集群模式可以保证高可用
普通集群模式:queue是放在一个节点上的,里面包含了元数据和实际的数据
可能其他机器上的queue只有元数据, 元数据上是有实际数据的地址的 ,所以我们可以通过其他节点的元数据来找到实际数据 拉过来消费
缺点:可能会在集群中产生大量数据传输,可用性几乎没有什么保障
镜像集群模式:每个节点上都有queue的节点镜像,每个节点上都包含了这个queue的全部数据,你从任何一个节点去消费都没问题,任何一个节点宕机了, 其他节点上还有着这个节点的全部数据,别的消费者都可以到其他节点上去消费数据
缺点:不是分布式的,如果queue的数据量很大,因为每个节点都有全部数据,所以可能大到机器上无法承载。
kafka的高可用,kafka是分布式的
它会在每个机器上提供一个broker进程,可以认为是kafka的一个节点,topic可以划分为很多个portition 它会把数据存在不同的partition上面
0.8以后保证了高可用 他为每个porttition保存了一个副本
leader与follower,无论生产还是消费 都是往leader里面,而写入数据到leader ,leader会将数据同步到follower
如果leader挂了,该leader的follower会马上变成新的leader
如何保证消息不被重复消费?
01kafka消费端可能出现的重复消费问题:
消费者在准备提交offeset 11,但是还没有提交的时候,挂了,这时候消费者如果重启,
他会再次去拿数据,可能会再次拿到11这条数据
重复消费后怎么保证幂等性?
简而言之,一条数据重复发来两次,但是数据库里面只有一条数据,这就保证 了幂等性
常用方法:每次消费完以后,往一个地方去插入一条消息,可以是redis也可以是mysql数据库,下一次就看记录里面是不是已经存在了该条消息,用来去重,如果有,就说明已经消费过,不要再进行消费
如果是mysql,就查是否有过这条记录,如果是redis,每次都用set那么可以保证天然的幂等性
如果不是这两种场景,那就让生产者每次产生一个全局唯一id,订单id之类,先根据id去redis查看是否消费过。
如何保证消息可靠性传输(不丢失)?
有可能生产者还没给rabbitmq就丢了,也可能rabbitmq挂了 ,没存,也可能消费者还没处理,自己挂了
生产者->rabbitmq
1.生产者事务方案,失败重发,但是因为是同步的,影响吞吐量
2.confirm,异步机制。生产者每次发送消息都会给一个回调,告诉你成功还是失败,如果失败,你可以重刷消息 更推荐这一种
rabbitmq:
让他把接到的数据持久化到磁盘中去 1、在创建queue的时候将其设置为持久化,发送消息的时候将消息的deliveryMode设置为2,也是将消息设置为持久化。必须这两个同时设置才行
消费端丢失数据:
在消费者还在消费过程中的时候,消费者就自动autoACK了,告诉mq已经消费了,但是这时候不巧系统宕机,导致该条消息丢失
解决方案:将autoACK给关闭,每次自己确定已经处理完消息,再通知消费者确定,如果宕机,mq会将这条消息重新给其他的消费者去处理
kafka:
必须保证有足够多的follower 并且每个follower都同步到了数据才能返回成功的ACK
我该怎么保证从消息队列中拿到的数据按顺序执行?
比如一开始是增、改、删,结果顺序被打乱,变成了删增改,那么导致这条数据被保留下来了,跟需要的结果不符合
rabbitmq 保证同一个queue中的消息顺序
kafka 保证同一个patition中的消息顺序,在消费者这一部分,如果单线程比较慢得话,就开启多线程来处理
怎么解决消息队列延时,过期失效问题?
创建一个新的topic,新建原来十倍数量的partition,比如原来三个,现在三十个,然后让原来的消费者不直接写进数据库,而是写到三十个partition里面,再创建三十个消费者,来消费这些积压的消息
如果设置了过期时间呢? 一般是不会设置的 如果过期了 只能手动去把那丢失的消息查出了重新导进去
如果让你来开发一个消息队列中间件,你怎么设计他的架构?
结合前面的问题来回答