消息队列高可用、重复消费、消息丢失、顺序消息
原文链接:https://juejin.im/post/6844904078862974984
前言
消息队列在互联网技术存储方面使用如此广泛,几乎所有的后端技术面试官都要在消息队列的使用和原理方面对小伙伴们进行360°的***难。
面试官杠上消息队列?高可用、重复消费、丢失、顺序消息...
什么,这么多问题啊!别慌,现在就来找找解决方案。
高可用
主流的MQ都有高可用模式可以供我们选择!
RabbitMQ可以使用镜像模式搭建高可用集群,可以配置数据同步到所有节点还是指定数量的节点以满足实际需求。
RocketMQ、Kafka天然的是分布式设计让他们天然具有高可用的技能^^ RocketMQ有多master多slave异步复制模式、多master多slave同步双写模式多种集群部署模式可以选择 多master多slave模式部署架构图:
Producer与NameServer集群中的其中一个节点(随机选择)建立长连接,定期从NameServer获取Topic路由信息,并向提供Topic服务的Broker Master建立长连接,且定时向Master发送心跳。Producer只能将消息发送到Broker master。
Consumer则不一样,它同时与提供Topic服务的Master、Slave建立长连接,既可以从Master订阅消息,也可以从Slave订阅消息,订阅规则由Broker配置决定。
Kafka的架构与RocketMQ十分相似
Zookeeper的的作用与NameServer的作用相似, 用于保存集群配置、选举Leader等。
集群中有许多broker用于堆积消息,Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高。
Producer使用push模式将消息发布到broker。
Consumer使用pull模式从broker订阅并消费消息。
重复消费
现在消息队列一般都能保证at least once的,也就是消息至少一次投递。 在这种情况为什么会出现重复消费的问题呢? 通常都是由于网络原因造成的,原因如下: 通常消息被成功消费后消费者都会发送一个成功标志给MQ,MQ收到这个标志就表示消息已经成功消费了,就不会再发送给其他消费者了。 但是如果因为网络这个标志没有送到MQ就丢失了,MQ就认为这个消息没有被成功消费,就会再次发送给其他消费者消费,就造成了重复了。
这时我们看这个问题就变成了我们怎么保证消费端的幂等性。
幂等性 是指一个操作其执行任意多次所产生的影响均与一次执行的影响相同 大白话就是你同样的参数调用我这个接口,调用多少次结果都相同
幂等性需要根据业务需求来具体看,但是主要的原理就是去重 一般可分为强校验、弱校验
- 强校验 一般与金融相关的操作都是强校验的 (人在996,锅从天上来 偷跑) 比如消费者是一个打款服务,在付款成功后都加一条流水记录。且两个操作放入一个事务中。 再次消费的时候就去流水表查一下有没有这条纪录,如果有表示已经消费过了,直接返回。流水表也能起到对账的作用! 一些简单的场景也可以依赖数据库唯一约束实现
- 弱校验 这个就没那么严格,重复一下也没那么重要的情况。 可以将ID保存在redis set中,过期时间看情况设置。 如果ID不能保证唯一可以选择生产方生成一个token存入redis,消费方在消费后将其删除(redis的操作能够保证其原子性,删除失败会返回0)
消息丢失
有些消息怎么跑着跑着就没了呢?
一般来讲消息丢失的途径有三个:
- 生产者弄丢数据
主流的MQ都有确认机制或者事务机制,可以保证生产者将消息送达到MQ。 比如RabbitMQ就有事务模式和confirm模式。 - 消息队列弄丢数据
一般只要开启MQ的持久化磁盘配置就能解决这个问题,写入了磁盘就放心了。 - 消费者弄丢数据
消费者丢数据一般是因为采用了自动确认消息模式。MQ收到确认消息后会删除消息,如果这时消费者异常了,那消息就没了。改用手动确认就能解决这个问题了呢!
顺序消息
顺序消息的场景可能用的比较少,但是还是有的 比如一个电商的下单操作,下单后先减库存然后生成订单,这个操作就需要顺序执行的 那怎么保证顺序呢?
- 首先生产者需要保证入队的顺序,入队都是乱的那再厉害的MQ也招架不住啊 (舌灿莲花)
- 一般的MQ都能保证内部Queue是FIFO的(先进先出),但是只是针对一个Queue,所以在发送消息的时候可以使用Hash取模法将同一个操作的消息发送到同一个Queue里面,这样就能保证出队时是顺序的了。
- 消费者也需要注意,如果多个消费者同时消费一个队列。一样可能出现顺序错乱的情况。这就相当于是多线程消费了!
通过上面的连招基本就能解决顺序消息消费的问题了呢!