消息最终一致性解决方案之RabbitMQ实现

本文详细解析了RabbitMQ的消息确认机制,包括生产者与消费者如何通过确认回调确保消息最终一致性和异常处理策略。特别关注网络故障、消息丢失及消费者异常情况下的重试和补偿措施。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RabbitMQ遵循了AMQP规范,用消息确认机制来保证:只要消息发送,就能确保被消费者消费来做到了消息最终一致性。而且开源,文档还异常丰富,貌似是实现分布式事务的良好载体

6.1 RabbitMQ消息确认机制

rabbitmq的整个发送过程如下

1. 生产者发送消息到消息服务

2. 如果消息落地持久化完成,则返回一个标志给生产者。生产者拿到这个确认后,才能放心的说消息终于成功发到消息服务了。否则进入异常处理流程。

rabbitTemplate.setConfirmCallback(
    (correlationData, ack, cause) -> { 
        if (!ack) { 
            //try to resend msg 
        } else { 
            //delete msg in db 
        } 
    }
);

3. 消息服务将消息发送给消费者

4. 消费者接受并处理消息,如果处理成功则手动确认。当消息服务拿到这个确认后,才放心的说终于消费完成了。否则重发,或者进入异常处理。

final Consumer consumer = new DefaultConsumer(channel) { 

    @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 

        String message = new String(body, "UTF-8"); 

        System.out.println(" [x] Received '" + message + "'"); 

        try { 
    
            doWork(message); 

        } finally { 

            //确认收到消息 
            channel.basicAck(envelope.getDeliveryTag(), false); 

        } 

    } 

};

6.2 异常

我们来看看可能发送异常的四种

1. 直接无法到达消息服务

网络断了,抛出异常,业务直接回滚即可。如果出现connection closed错误,直接增加 connection数即可

connectionFactory.setChannelCacheSize(100);

2. 消息已经到达服务器,但返回的时候出现异常

rabbitmq提供了确认ack机制,可以用来确认消息是否有返回。因此我们可以在发送前在db中(内存或关系型数据库)先存一下消息,如果ack异常则进行重发

/**confirmcallback用来确认消息是否有送达消息队列*/ 
rabbitTemplate.setConfirmCallback(
    
    (correlationData, ack, cause) -> { 

        if (!ack) { 

            //try to resend msg 

        } else { 

            //delete msg in db 

        } 

    }

); 

/**若消息找不到对应的Exchange会先触发returncallback */ 
rabbitTemplate.setReturnCallback(

    (message, replyCode, replyText, tmpExchange, tmpRoutingKey) -> { 

        try { 

            Thread.sleep(Constants.ONE_SECOND); 

        } catch (InterruptedException e) { 

            e.printStackTrace(); 

        } 

        log.info("send message failed: " + replyCode + " " + replyText); 

        rabbitTemplate.send(message); 

    }

);

3. 消息送达后,消息服务自己挂了

如果设置了消息持久化,那么ack= true是在消息持久化完成后,就是存到硬盘上之后再发送的,确保消息已经存在硬盘上,万一消息服务挂了,消息服务恢复是能够再重发消息

4. 未送达消费者

消息服务收到消息后,消息会处于"UNACK"的状态,直到客户端确认消息

channel.basicQos(1); 

// accept only one unack-ed message at a time (see below) 

final Consumer consumer = new DefaultConsumer(channel) { 

    @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException { 

        String message = new String(body, "UTF-8"); 

        System.out.println(" [x] Received '" + message + "'"); 

        try { 

            doWork(message); 

        } finally { 

            //确认收到消息 

            channel.basicAck(envelope.getDeliveryTag(), false); 

        } 

    } 

}; 

boolean autoAck = false; 

channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);

5. 确认消息丢失

消息返回时假设确认消息丢失了,那么消息服务会重发消息。注意,如果你设置了autoAck= false,但又没应答channel.baskAck也没有应答channel.baskNack,那么会导致非常严重的错误:消息队列会被堵塞住,所以,无论如何都必须应答

6. 消费者业务处理异常

消息监听接受消息并处理,假设抛异常了,第一阶段事物已经完成,如果要配置回滚则过于麻烦,即使做事务补偿也可能事务补偿失效的情况,所以这里可以做一个重复执行,比如guavaretry,设置一个指数时间来循环执行,如果n次后依然失败,发邮件、短信,用人肉来兜底。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值