一、序言
提早工作利用宽泛,提早工作典型利用场景有订单超时主动勾销
;领取回调重试
。其中订单超时勾销具备幂等性属性,无需思考反复生产问题;领取回调重试须要思考反复生产问题。
提早工作具备如下特点:在将来的某个工夫点执行;个别仅执行一次。
1、实现原理
生产者将带有提早信息的音讯发送到RabbitMQ交换机中,期待延迟时间完结方将音讯转发到绑定的队列中,消费者通过监听队列生产音讯。提早工作的要害在音讯在交换机中停留。
不言而喻,基于RabbitMQ实现提早工作对服务器的可靠性要求极高,交换机内部消息无长久化机制,比方单机模式服务重启,未开始的提早工作均失落。
2、组件选型
二、方案设计
(一)服务器
RabbitMQ服务须要装置x-delayed-message
插件以解决提早音讯。
(二)生产者
提早工作的实现对生产者的要求是将音讯牢靠的投递到交换机,因而应用confirm确认
机制即可。
订单生成之后,先入库,而后以订单ID为key将订单详情存入Redis中(长久化),向RabbitMQ发送异步confirm确定申请。如果收到失常投递返回,则删除Redis中订单ID为key的数据,回收内存,否则以订单ID为key,从Redis中查问出订单数据,从新发送。
(三)消费者
提早工作的实现对消费者的要求是以信息不失落的形式生产音讯,具体表现在:手动确认音讯的生产,避免音讯失落;生产端继续稳固,避免音讯沉积;音讯生产失败有重试机制。
思考到订单提早勾销属于幂等性操作,因而无需思考音讯的反复生产问题。
三、SpringBoot实现
实现局部仅贴一部分外围源码,残缺我的项目请拜访GitHub。
(一)生产者
思考到下单是极为重要的操作,因而首先将订单落库、存盘,而后进行后续操作。
for (long i = 1; i <= 10; i++) { /* 1.模仿生成订单 */ BuOrder order = createOrder(i); /* 2.订单入库 */ orderService.removeById(order); orderService.saveOrUpdate(order); /* 3.将订单存入信息Redis */ RedisUtils.setObject(RabbitTemplateConfig.ORDER_PREFIX + i, order); /* 4.向RabbitMQ异步投递音讯 */ rabbitTemplate.convertAndSend(RabbitmqConfig.DELAY_EXCHANGE_NAME, RabbitmqConfig.DELAY_KEY, order, RabbitUtils.setDelay(30000), RabbitUtils.correlationData(order.getOrderId()));}
生产者牢靠投递音讯
public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (correlationData == null) { return; } String key = ORDER_PREFIX + correlationData.getId(); if (ack) { /* 如果音讯投递胜利,则删除Redis中订单数据,回收内存 */ RedisUtils.deleteObject(key); } else { /* 从Redis中读取订单数据,从新投递 */ BuOrder order = RedisUtils.getObject(key, BuOrder.class); /* 从新投递音讯 */ rabbitTemplate.convertAndSend(RabbitmqConfig.DELAY_EXCHANGE_NAME, RabbitmqConfig.DELAY_KEY, order, RabbitUtils.setDelay(30000), RabbitUtils.correlationData(order.getOrderId())); }}
(二)消费者
消费者端手动确认,防止音讯失落;失败主动重试。
@RabbitListener(queues = RabbitmqConfig.DELAY_QUEUE_NAME)public void consumeNode01(Channel channel, Message message, BuOrder order) throws IOException { if (Objects.equals(0, order.getOrderStatus())) { /* 批改订单状态,设置为敞开状态 */ orderService.updateById(new BuOrder(order.getOrderId(), -1)); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); log.info(String.format("消费者节点01生产编号为【%s】的音讯", order.getOrderId())); }}
消费者牢靠生产应至多开启两个及以上利用,确保音讯队列中不积压音讯。
(三)通用工具包
上述代码波及一个工具类RabbitUtils
,存在于如下依赖中,次要封装RabbitMQ极罕用的工具办法。
<dependency> <groupId>xin.altitude.cms</groupId> <artifactId>ucode-cms-common</artifactId> <version>1.4.3.1</version></dependency>
喜爱本文点个♥️赞♥️反对一下,如有须要,可通过微信dream4s
与我分割。相干源码在GitHub,视频解说在B站,本文珍藏在博客天地。