(待完善))
消息队列1.什么是消息队列消息列队Message Queue简称 MQ是一个异步通讯机制生产者和消费者只需通过消息队列就能够进行通讯。2.消息队列有什么作用异步生产者将消息发送到消息队列后不需要等待消费者处理完成就可以去处理其他任务削峰将大量请求暂时存在消息列队中由服务器根据自己的能里去慢慢消费避免大量请求压垮服务器。MySQL 库存服务的处理能力只有5000 QPS每秒最多扣 5000 次库存当10万请求MySQL数据库时可以先将请求暂存到MQ后续根据自己数据库真实的抗压能力再去慢慢消费。解耦上下游系统不再直接硬调用而是通过消息队列中转一方发消息、一方消费双方不需要进行通讯场景订单系统下单后需通知库存、物流、积分、短信等多个服务。不用 MQ订单服务依次调用所有下游下游宕机 / 改接口订单服务跟着受影响耦合极强。用 MQ订单只发一条消息到队列各服务自行监听消费。新增 / 下线服务无需改动订单代码。3.消息队列有什么缺点系统复杂度变高由原来的A 系统直接调 B 系统到现在的A系统到MQ再到B系统。当MQ宕机、网络故障时整个业务链路直接瘫痪。数据一致性问题MQ 是异步的A系统发送消息成功但B系统可能会处理失败系统的开发难度上升原本很简单的一个方法调用变成了需要额外编写消息发送、接收、重试、幂等逻辑还需要处理消息丢失、重复、乱序等问题。4.消息队列如何选型没有最好的 MQ只有最匹配业务的 MQ对比维度KafkaRocketMQRabbitMQActiveMQ开发 / 维护开源Apache社区活跃开源Apache阿里维护国内生态强开源社区成熟国外主流开源Apache逐步衰退更新慢底层语言Scala/JavaJavaErlangJava单机吞吐量百万级 / 秒最高十万级 / 秒万级 / 秒千级 / 秒最低消息延迟毫秒级毫秒级微秒低毫秒最低延迟中等毫秒可靠性高副本、持久化极高事务、重试、死信、HA 完善高持久化、镜像队列一般高并发易丢消息消息堆积擅长海量堆积支持大规模堆积堆积能力弱易卡顿堆积极易出问题适用场景日志采集、大数据流、监控、海量流式消息电商、金融、业务解耦、事务消息、高可靠业务实时业务、路由复杂、跨协议、小消息低延迟老旧系统兼容、低并发传统项目比如要支持淘宝双十一类超大型的秒杀活动看重吞吐量。优先选Kafka和RocketMQ这种更高吞吐的比如一个金融类业务那么重点考虑的就是稳定性、安全性分布式部署的Kafka和Rocket就更有优势。5.消息队列的模型有哪些队列模型在队列模型中消息从生产者发送到队列并且每条消息只能被一个消费者消费一次。消费之后消息在队列中被删除。主题模型 (发布/订阅模型)在主题模型中消息的生产者称为发布者(Publisher)消息的消费者称为订阅者(Subscriber)存放消息的容器称为主题(Topic)。发布者将消息发送到指定主题中订阅者需要提前订阅主题才能接受特定主题的消息RocketMQ1.RocketMQ的消息模型RabbitMQ中的消息模型就是按照“主题模型”所实现的。主要分为Producer生产者发消息到 TopicRocketMQ。Broker服务端存消息、转发消息Consumer消费者从 Topic 收消息RocketMQ。Topic主题消息的逻辑分类2.RocketMQ的架构RocketMQ 技术架构分为Broker、NameServer、Producer、ConsumerBroker消息的实际载体用于存储消息。NameServer类似于注册中心。动态存储Broker将元数据。如Broker 的名称、Broker 的网络地址、Topic 路由信息、存活状态标识为生产者 / 消费者提供 Topic 路由信息。也就是告诉生产者和消费者某个Topic存在哪个Broker中。Producer生产者负责消息发送到BrokerConsumer 消费者负责订阅消息和消费消息3.RocketMQ整体工作流程Broker启动时会向所有NameServer建立常连接每30秒发送一次心跳Producer发送消息时从NameServer获取Broker的地址然后向Broker发送消息Consumer消费消息时从NameServer获取Broker的地址然后从Broker拉取消息4.Broker和NameServer的心跳机制1.为什么需要心跳机制NameServer无法感知Broker的存活状态生产者和消费者会连接到无效的Broker会导致消息发送失败等问题。2.什么是心跳机制Broker每30秒会给NameServer发送一次心跳告诉NameServer自己还活着。NameServer默认每隔10 秒执行一次任务去扫描每个Broker最后一次发送的心跳时间如果一个Broker心跳时间与当前时间距离120秒就会认为Broker 已经宕机。5.RocketMQ消息发送模式同步发送生产者发送消息后会阻塞当前线程执行直到Broker返回明确的成功或失败消息线程才能够继续执行。异步发送生产者发送消息后立即返回不需要等待Broker响应后续Broker处理消息完成后通过回调函数通知生产者。单向发送生产者发送消息后立即返回不关心Broker是否接受到消息6.RocketMQ的刷盘机制异步刷盘消息存入Broker内存后Broker立即返回ACK给到生产者之后再进行刷盘同步刷盘消息存入到Broker内存并进行刷盘后Broker立即返回ACK给到生产者7.RocketMQ如何保证消息不丢失消息丢失只出现在三个阶段消息生产阶段、消息存储阶段、消息消费阶段所以要保证消息就是保证三个环节都不能丢失数据。消息生产阶段丢失原因因为生产者在将消息发送到Broker时可能因为网络波动问题导致消息未成功发送到Broker 。解决方法可以使用同步发送模式Broker收到消息时才会返回一个ACK给到生产者如果Broker没有收到消息生产者会重试发送消息这样就保证了消息不丢失。消息存储阶段丢失原因消息刚放内存还没写硬盘Broker 宕机 消息没了解决方法Broker采用同步刷盘策略消息只有在持久化到磁盘后Broker才会返回ACK给到生产者。消息消费阶段丢失原因消费者消息刚拿到还没处理完就告诉 Broker “我消费完了”。结果消费者宕机消息丢失解决办法关闭自动提交改为手动提交当消费者收到消息并处理完业务逻辑后手动提交ACK到Broker。8.RocketMQ如何处理消息重复问题1.为什么会重复1、生产者发送消息后由于网络波动生产者未能收到确认响应导致消息被重复发送。2、消费者在处理消息后由于系统崩溃或网络问题未及时向MQ中间件发送 ACKMQ重新发送消息2.解决办法为了避免重复消息带来的副作用关键在于使消费者的处理逻辑具有幂等性。幂等性是指一个操作无论执行多少次产生的结果都是一样的。如何保证幂等性消息唯一标识ID为每条消息引入全局唯一ID如UUID、订单号等利用Redis或数据库来存储已处理过的消息ID。消费者在处理消息时先检查该消息ID是否已存在如果存在则说明消息已被处理过。如果不存在把 ID 存入 Redis / 数据库。数据库唯一索引给业务字段建立唯一索引重复消息插入就会失败9.RocketMQ如何处理消息堆积问题消息积压是因为生产者的生产速度大于消费者的消费速度。解决办法先定位消费慢的原因如果是bug则处理bug如消费线程发生死锁、线程池中消费者线程数配置不合理如果是因为消费者本身消费能力较弱则优化消费者代码逻辑比如减少单个消息处理时间、使用批量处理消息如果代码逻辑也优化了还是慢则可以增加消费者的线程数量提高消费能力或者进行扩容增加多个消费者提高消费能力。(增加多个消费者不同机器 /不同虚拟服务器/同一服务器的不同进程 / 端口)对生产者进行限流控制生产者发送消息的速度消费者降级对低优先级的消息进行丢弃或延迟处理。10.RocketMQ如何保证消息的有序性1.为什么要保证消息的有序性消息的有序性是指消息的消费顺序能够与消息的发送顺序一致。例如一个订单产生了3条消息分别是订单创建、订单付款和订单完成。在消息消费时同一条订单要严格按照这个顺序进行消费否则业务会发生混乱。2.如何保障消息有序性的局部顺序RocketMQ 按照业务划分成不同的队列不同业务的队列并发消费同一业务中的队列按照发送消息的顺序消费场景订单、物流、交易等不同业务并行执行同一个业务内部必须严格有序。比如同一个订单的 创建→付款→发货→完成 必须按顺序执行。全局顺序RocketMQ将所有业务消息放入到同一个队列中消费时严格遵循发送先后顺序逐条执行场景订单、物流、交易等所有业务消息按发送顺序全部存入同一个队列。11.RocketMQ如何实现延迟消息的1.什么是延迟消息延迟消息是指将消息发送到MQ后不会立即发送给消费者而是设定一个定时时间当时间一到就才会发送。如在电商平台下单后 30 分钟未支付“订单已取消” 的信息就会发送给消费者2.如何实现延迟消息一、先记住RocketMQ 不支持 “任意时间” 延迟只能选默认的18 个固定级别1s、5s、10s、30s、 1m、2m、3m、4m、5m、6m、7m、8m、9m、10m、20m、30m、 1h、2h二、实现整体流程Broker 接收延迟消息后存入到延迟队列中定时调度服务轮询检查延迟队列当消息到期时Broker 将消息放入到正常队列中消费者开始正常消费。12.RocektMQ怎么处理分布式事务1.什么是分布式事务指一个业务操作需要跨 2 个及以上独立服务 / 数据库 / 服务器才能够完成而且这个业务操作要么全部成功要么全部失败。场景A 服务给 B 服务转 100块钱A服务减100B服务加100。两个服务要么一起成功还要么失败2.如何来保证事务的一致性生产者向 Broker发送半消息 半消息消息成功发送给Broker 端但消息被标记为不可投递状态消费者无法消费生产者收到Broker成功响应的话生产者开始执行本地事务生产者根据本地事务执行结果向Broker发送提交或回滚如果发送的是提交Broker将消息标记为正常消息消费者可以正常消费如果发送的是回滚Broker将消息删除如果Broker没收到二次确认Broker 会主动回查生产者的事务执行结果再根据结果进行提交处理或者回滚处理13.死信队列死信队列是专门存储哪些无法被正常消费的消息当一条消息初次被消费者消费失败时RocketMQ会自动重试消费消息当达到最大重试次数后依然消费失败则将消息放入到死信队列中等待人工干预处理。