RabbitMQ-消息的可靠性投递实现方案

保障100%的消息可靠性投递方案落地实现

保障100%消息投递成功设计方案(一)

在这里插入图片描述

蓝色区域: 生产者

红色区域: broker(MQ服务)

总共有7个步骤

第一步: 存储业务数据(订单), 存储MQ消息记录(MQ日志)

第二步: 发送确认类型的MQ消息到broker

第三步: 监听该结果, broker收到消息,返回(应答)确认收到

第四步: 操作MQ消息记录表做状态更新(0消息发送中,1消息发送成功,2消息发送失败)

如果没有收到第三步的确认则会有定时任务定时轮询

第五步: 定时任务获取状态为0的消息, 为0走第6步进行消息重新投递

第六步: 消息重投

第七步: 当第五步最大努力重投达到一定次数后状态还是0, 就把消息直接设为失败2

代码实现

生产者

MQ消息记录表

-- 表broker_message_1og消息记录结构
CREATE TABLE IF NOT EXISTS `broker_message_log` (
	`message_id` varchar (128) NOT NULL, -- 消息唯一ID
	`message` varchar(4000) DEFAULT NULL, -- 消息内容
	`try_count` int(4) DEFAULT '0'-- 重试次数
	`status` varchar (10) DEFAULT '', -- 消息投递状态:0投递中,1投递成功,2投递失败
	`next_retry` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',  -- 下一次重试时间 或 超时时间
	`create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'-- 创建时间
	`update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', -- 更新时间
	PRIMARY KEY (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

发送MQ消息类

@Component
public class RabbitOrderSender {
    // 自动注入RabbitTemplate模板类
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Autowired
    private BrokerMessageLogMapper brokerMessageLogMapper;
    
    // 回调函数: confirm确认
	final ConfirmCal1back confirmCallback = new RabbitTemplate.ConfirmCallback() {
		@Override
        public void confirm(CorrelationData correlationData, boolean ack, String cause) {
            System.err.println("correlationData:" + correlationData);
			String messageId = correlationData.getId();
            if (ack) {
            	// 如果confirm返回成功则进行更新
            	brokerMessageLogMapper.changeBrokerMessageLogStatus (messageId, Constants.ORDER_SEND_SUCCESS, new Date());
            } else {
            	// 失败则进行具体的后续操作: 重试或者补偿等手段
                System.err.println("异常处理...");
            }
        }
    };
    
    // 发送消息方法调用: 构建自定义对象消息
	public void sendOrder(Order order) throws Exception {
		rabbitTemplate.setConfirmCallback (confirmCallback) ;
		//消息唯一ID
		CorrelationData correlationData = new CorrelationData (order.getMessageId());
		rabbitTemplate.convertAndSend( "order-exchange", "order.ABC", order, correlationData);
    }
}

创建订单并发送MQ

@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private BrokerMessageLogMapper brokerMessageLogMapper;
    @Autowired
    private RabbitOrderSender rabbitOrderSender;
    
    public void createOrder(Order order) throws Exception {
        // order current time
        Date orderTime = new Date() ;
        // order insert //业务数据入库
        orderMapper.insert(order);
        // log insert // 构建消息日志记录对象
        BrokerMessageLog brokerMessageLog = new BrokerMessageLog() ;
        brokerMessageLog.setMessageId (order.getMessageId()) ;
        // save order message as json
        brokerMessageLog.setMessage(FastJsonConvertUtil.convertObjectToJSON(order));
        brokerMessageLog.setStatus("0"); // 设置订单的发送状态为0表示发送中
        brokerMessageLog.setNextRetry(DateUtils.addMinutes(orderTime, Constants.ORDER_TIMEOUT)); // 为1分钟,表示1分钟后状态还没改为1就是超时了, 定时任务就会重新投递
        brokerMessageLog.setCreateTime(new Date());
        brokerMessageLog.setUpdateTime(new Date());
        // 保存MQ消息日志
        brokerMessageLogMapper.insert(brokerMessageLog); 
        // order message sender
        rabbitOrderSender.sendOrder(order) ;
    }
}

定时任务(补偿机制)

@Component
public class RetryMessageTasker {
    @Autowired
    private RabbitOrderSender rabbitOrderSender;
    @Autowired
    private BrokerMessageLogMapper brokerMe ssageLogMapper;
    
    @Scheduled (initialDelay = 3000, fixedDelay = 10000)
    public void reSend() {
        System.err.println ("--------------定时任务开始---------------");
        //pu11 status = 0 and timeout message(超时时间小于当前时间)
        List<BrokerMessageLog> list = brokerMessageLogMapper.query4StatusAndTimeoutMessage();
        list.forEach (messageLog -> {
            if (messageLog.getTryCount() >= 3) {
                // update fail message 重试次数达到3次, 修改消息状态为2失败, 由人工补偿
               brokerMessageLogMapper.changeBrokerMessageLogStatus(messageLog.getMessageId(), Constants.ORDER_SEND_FAILURE, new Date());
            } else {
                // resend
                // 更新重试次数
                brokerMessageLogMapper.update4ReSend (messageLog.getMessageId(), new Date());
                // 取出消息, 反序列化为对象
                OrderreSendOrder = FastJsonConvertUtil.convertJSONToobject(message);
                try {
                    // 重新投递
                    rabbitOrderSender.sendOrder(reSendOrder);
                } catch (Exception e) {
                    e.printStackTrace () ;
                    System.err.println ("----------异常处理----------");
                }
            }
        }
    }
}

消费者

消费者配置

spring.rabbitmq.addresses=192.168.11.81:5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.connection-timeout=15000

spring.rabbitmq.listener.simple.acknowledge-mode=manual
spring.rabbitmq.listener.simple.concurrency=5
spring.rabbitmq.listener.simple.prefetch=1
spring.rabbitmq.listener.simple.max-concurrency=10
spring.rabbitmq.listener.order.queue.name=order-queue
spring.rabbitmq.listener.order.queue.durable=true
spring.rabbitmq.listener.order.exchange.name=order-exchange
spring.rabbitmq.listener.order.exchange.durable=true
spring.rabbitmq.listener.order.exchange.type=topic
spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
spring.rabbitmq.listener.order.key=order.*

消费代码

@Component
public class RabbitReceiver {
    /**
     *
     * spring.rabbitmq.listener.order.queue.name=order-queue
     * spring.rabbitmq.listener.order.queue.durable=true
     * spring.rabbitmq.listener.order.exchange.name=order-exchange
     * spring.rabbitnq.listener.order.exchange.durable=true
     * spring,rabbitmq.listener.order.exchange.type=topic
     * spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions=true
     * spring.rabbitmq.listener.order.key=order.*
     *
     * @param order
     * @param channeI
     * @param headers
     * @throws Exception
     */
    @RabbtListener (bindings = @QueueBinding(
            value = @Queue (value = "${spring.rabbitmq.listener.order.queue.name}"),
            	durable = "${spring.rabbitmq.listener.order.queue.durable}"),
            exchange = @Exchange (value = "${spring.rabbitmq.listener.order.exchange.name}")
            	durable = "${spring.rabbitnq.listener.order.exchange.durable}",
            	type = "${spring,rabbitmq.listener.order.exchange.type}",
            	ignoreDeclarationExceptions = "${spring.rabbitmq.listener.order.exchange.ignoreDeclarationExceptions}"),
            	key = "${spring.rabbitmq.listener.order.key}"
            )  
	)
    @RabbitHandler
    public void onOrderMessage (@Payload Order order,
            Channel channel,
            @HeadersMap<String, Object> headers) throws Exception {
        System.err.println("--------------------------------------") ;
        System.err.println("消费端order:" + order.getId());
        Long deliveryTag = (Long)headers.get(AmqpHeaders.DELIVERY_TAG) ;
        //手工 ACK
        channel.basicAck(deliveryTag, false) ;
    }

}

测试代码

@Autowired
private OrderService orderService;
@Test
public void testCreateOrder() throws Exception {
    Order order = new Order () ;
    order.setId ("2018081900000001") ;
    order.setName ("测试创建订单");
    order.setMessageId (System.currentTimeMillis() + "$" + UUID.randomUUID().toString());
    orderService.createOrder(order);
}

保障100%消息投递成功设计方案(二)

在这里插入图片描述

两次投递方案

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值