为什么使用MQ (优缺点)
优点
1.解耦
一个系统可能有abcd四个模块,a 模块是核心模块,bcd 都需要从 a 获取数据,那么就需要在 a 模块写3个接口给bcd传输数据,这样的话,不管是模块增减,还是数据有变,都需要在a模块修改代码,强耦合。此时 ,mq 可以解耦,a 把数据发送到mq,bcd 自行订阅消费即可。
2.异步
一个接口可能调用多个模块的接口,耗时较大。通过 mq ,可以让各个模块自行在后台消费,该接口只需要执行完自己的操作即可返回结果。
3.削峰
系统可能在某个时间点,有大量请求过来,可能会导致系统直接崩掉,而其他时间请求很少,此时就可以通过 mq 存储数据,在后台以每秒系统能够承受的压力去处理请求。
缺点
1.可用性降低
系统依赖mq之后,必须保证mq的高可用,因为mq如果挂了的话,系统可能就崩了。
2.复杂性提高
系统依赖mq之后,需要考虑如何解决消息的重复消费、消息的丢失、保证顺序消费。
3.一致性
系统依赖mq之后,如果 a 系统成功发送到mq消息,此时返回结果成功,但是bcd系统消费失败了,导致一致性没有保证。
MQ 如何保证高可用
RabbitMq 高可用
1.普通集群
有 a、b、c 三个节点,但是只有 b 节点的queue有元数据和实际数据,而 ac 节点只有queue的元数据,用户请求到ac时,那么ac需要从b节点获取实际数据,再返回给用户。只是提高了吞吐量。
缺点:
1、3个节点内部传输消耗可能会过大
2、b 节点挂了,服务就不可用了
2.镜像集群
有a、b、c 三个节点,它们都存有queue的元数据和实际数据。
缺点:
不是分布式,如果数据过大,节点存不下的话,不好处理。
Kafka 高可用
kafka 是分布式的,它的数据是分批存在多个节点的,每个节点存一部分数据,每个节点还可以有自己的副本节点,主节点用来写数据,同步到副本,当一个主节点挂了之后,kafka会把它相应的副本节点提升为主节点,继续工作。
MQ 如何保证消息幂等性
避免生产者重发消息
基于mq的本身特性,对每条消息,mq系统内部会生成一个inner-msg-id,全局唯一,与业务无关;当mq接收到消息时,会先根据该id判断消息是否重复发送,进而决定是否接收该消息。
避免消费者重复消费
1、乐观锁,修改数据时,判断version值是否变化,变化的就直接丢弃消息。
2、去重表,可以使用业务中有唯一属性的字段,也可以自己生成一个全局唯一id,每消费一个消息,将其唯一属性存入redis,下次消费消息时,先查redis里面是否有此数据,有的话就直接丢弃消息。
MQ 如何保证消息顺序消费
RabbitMq
当一个mq有多个消费者时,会产生消费者消费信息顺序错乱的情况。
可以通过设置多个queue,每个queue只对应一个消费者,根据业务中的字段,将相同类型的数据hash到同一个queue里,这样就保证了每个消费者消费的消息是按照我们预期的顺序执行。
Kafka
kafka 的消息写入一个partition是一定有序的,同时一个 partition 只能被一个消费者消费。但是一个消费者读取消息时,可能会开启多个线程消费,毕竟单线程消费的能力有限,这就可能导致信息消费的顺序错乱。
可以通过为每个 partition 开启多个内存队列,将相同类型的数据hash到同一个内存队列里,每个线程消费其中的一个内存队列,这样就保证了每个消费者消费的消息是按照我们预期的顺序执行。
MQ 如何保证消息100%投递成功
消息落库,对消息状态打标
在发送消息前,先把消息存入数据库,状态为 0 ,表示已投递待确认,通过消息的监听,收到消息投递成功返回的确认时,更改数据库状态为1,表示投递成功。当然,这中间会有一个定时任务,定时查询库中超过一定时间的状态还不是1的消息,把它重新发送。
消息的延迟投递,做二次确认,回调检查
发送消息时,先发送一次消息到mq,再发送同样的延时消息到mq,这两个消息发送的队列是不一样的, 第一个是真正的消费者接受消息的队列,第二个延迟消息发送给一个专用的消息补偿服务接受消息的队列。当真正的消费者消费了消息时,也会发送一个消息到专用的消息补偿服务接受消息的队列,表明自己已经成功接收处理了该消息。此时,专用的消息补偿服务就把该消息落库。当时间超过延迟消息的延迟时间时,这时专用的消息补偿服务会接收到那条延迟消息,然后查库,发现确实没有这条消息,那就说明消息没有成功被消费,这时专用的消息补偿服务会再次从头走一遍上述过程。
未完待续……