消息队列
所有的赢家,都是蓄谋已久!
背景:消息队列七大基本问题,同时也是MQ 面试必须要掌握的东西。
一、何谓消息队列?
消息队列就是一个使用队列来通信的组件,也叫消息中间件。
二、消息队列有何作用?
异步处理、服务解耦、流量控制。
三、消息队列模型?
消息队列有两种模型:队列模型&发布/订阅模型。
队列模型图
发布/订阅模型图
区别:
1、队列模型每条消息只能被一个消费者消费,而发布/订阅模型就是为了让一条消息能被多个消费者消费孕育而生的;
2、队列模式也可以通过消息全量存储至多个队列来解决一条消息可被多个消费者消费问题,但会造成数据冗余;
3、发布/订阅模型兼容队列模型,即只有一个消费者的情况下和队列模型基本一致;
4、RabbiMQ 采用队列模型、RocketMQ 和KafKa 采用发布/订阅模型。
四、如何保证消息不丢失?
只要保证生产消息、存储消息和消费消息三个阶段配置得当,消息就不会丢失。
消息生产-存储-消费图
生产消息:
生产者发送消息至Broker,需要处理Broker 的响应,不管是同步还是异步发送消息都要做好try-catch,妥善处理响应;若Broker 返回写入失败等错误信息,则需要重试发送,当多次发送失败时需要作报警、日志记录等操作。
存储消息:
存储消息阶段要在消息刷盘之后再给生产者响应,假设在消息写入缓存中就返回响应了,那么若机器突然断掉消息就没了,而生产者却以为消息发送成功了;如果Broker 是集群部署,有多副本机制,则消息不仅要写入当前Broker,还要写入副机中,配置成至少写入两台机子后再给生产者响应。
消费消息:
保证在消费者真正执行完业务逻辑后,再给Broker 发送消费成功信号。
五、如何处理重复消息?
使用 幂等 处理重复消息,幂等可以理解为同样的参数多次调用同一个接口和调用一次产生的结果是一致的;常用的实现方案有:增加前置条件判断、使用version 版本号控制对比消息中的版本号&数据库中的版本号。
六、如何保证消息的有序性?
消息队列有序性分为:全局有序&部分有序。
全局有序:
要保证全局有序,首先只能有一个生产者向Topic 发送消息,并且一个Topic 内部只能有一个队列(分区);消费者也必须是单线程消费该队列,即可保证全局消息有序;但一般情况都不需要保证全局有序,即使MySQL Binlog 也只是需要保证单表消息有序即可。
全局有序图
部分有序:
绝大部分的有序需求是针对部分有序,保证部分有序可以将Topic 内部划分为我们需要的队列数,把消息通过特定的策略发往固定的队列中,然后每个队列对应一个使用单线程处理的消费者;如此既可确保消息的部分有序,有可以通过队列数量的并发来提高消息的处理效率。
部分有序图
七、如何处理消息堆积?
堆积原因:
消息堆积通常是由于生产者的生产速度与消费者的消费速度不匹配导致的;有可能是消费者消费失败反复重试造成的,也有可能是消费者消费能力弱,导致消息积压。
解决方案:
首先,需要定位消费慢的原因,若是Bug 则修复Bug;若是本身消费能力较弱,则可以优化消费代码逻辑,比如之前是一条一条消息消费处理的,可以优化为批量处理。如果优化后消息消费速度还是慢,那就需要考虑水平扩容,增加Topic 的队列数和消费者数量,注意队列数一定要增加,不然新增加的消费者是没东西消费的;因为一个Topic 中,一个队列只会分配给一个消费者。
所有的赢家
都是蓄谋已久