Spring-RabbitMQ 消费者消息确认案例实践

Springboot 版本: 2.7.0

消费者消息确认模式分类

  1. NONE:等同于rabbitMQ客户端的自动确认,只要投递了就认为是成功的。
  2. MANUAL:需要用户通过 channel 的 ack/nack 手动确认。
  3. AUTO(默认值):自动模式,消费者正常执行结束认为成功,报错认为失败。

代码实现

配置类:

@Slf4j
@Configuration
public class RabbitConfiguration {
    public final static String TOPIC_EXCHANGE = "myExchange";
    public final static String QUEUE_NAME = "myQueue";
    @Bean
    public RabbitAdmin amqpAdmin(ConnectionFactory connectionFactory) {
        return new RabbitAdmin(connectionFactory);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        template.setMessageConverter(jsonConverter());
        template.setExchange(TOPIC_EXCHANGE);
        template.setConfirmCallback((correlationData, ack, cause) -> {
            if (ack) {
                log.info("消息:{}发送成功", correlationData.getId());
            } else {
                log.error("消息:{}发送失败,失败原因为:{}", correlationData.getId(), cause);
            }
        });

        template.setMandatory(true);
        template.setReturnsCallback(returned -> {
            log.error("消息:{}路由失败, 失败原因为:{}", returned.getMessage().toString(), returned.getReplyText());
        });
        return template;
    }

    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(TOPIC_EXCHANGE, true, false);
    }

    @Bean
    public Queue queue() {
        return new Queue(QUEUE_NAME);
    }

    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(topicExchange()).with("my.test.*");
    }

    @Bean
    public Jackson2JsonMessageConverter jsonConverter() {
        return new Jackson2JsonMessageConverter();
    }
}

配置文件:

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: admin
    virtual-host: my_vhost
    # 消息确认(ACK)
    publisher-confirm-type: CORRELATED #correlated #确认消息已发送到交换机(Exchange)
    publisher-returns: true #确认消息已发送到队列(Queue)

生产者:


@Component
public class PublisherService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void send(){

        CorrelationData correlationData = new CorrelationData();
        rabbitTemplate.convertAndSend("my.test.message", new User("Kleven", 18), correlationData);

    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User implements Serializable {
    private static final long serialVersionUID = -5079682733940745661L;

    private String name;
    private Integer age;

}

模式一、NONE

当确认模式设置为NONE时,只要中间件投递了消息就认为成功并将消息从队列中移除。

    @RabbitListener(queues = "myQueue", messageConverter = "jsonConverter", ackMode = "NONE")
    public void noneAckListener(User user) {
        log.info("收到消息 -> {}", user);
        // 添加个错误用于测试
        int a = 1 / 0;
    }

结果:
可以看到,即使消费者出错了,队列中的消息依然被删除了。Spring-RabbitMQ 消费者消息确认案例实践

模式二、MANUAL

channel.basicAck 确认一个或多个消息

/**
* @param deliveryTag 当前消息的投递标签,是一个自增的数字。
* @param multiple true:确认 deliveryTag <= 当前消息deliveryTag 的所有消息; false:只确认当前收到的消息。
*/
void basicAck(long deliveryTag, boolean multiple) throws IOException;
    @Autowired
    private Jackson2JsonMessageConverter jsonConverter;

    @RabbitListener(queues = "myQueue", ackMode = "MANUAL")
    public void manualAckListener(Message message, Channel channel) throws IOException {

        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        log.info("成功消费消息 -> {}", jsonConverter.fromMessage(message));

        channel.basicAck(deliveryTag, false);
    }

结果:
消息消费成功,且从队列中删除。

消息:aaa9b3b7-85b4-42fb-8a12-0aad488817f1发送成功
成功消费消息 -> User(name=Kleven, age=18)

Spring-RabbitMQ 消费者消息确认案例实践

channel.basicNack 拒绝一个或多个消息

    /**
     *
     * @param multiple 拒绝 deliveryTag <= 当前消息deliveryTag 的所有消息; false:只拒绝当前收到的消息。
     * @param requeue true 将拒绝对的消息重新加入队列。
     */
    void basicNack(long deliveryTag, boolean multiple, boolean requeue)
            throws IOException;
    @Autowired
    private Jackson2JsonMessageConverter jsonConverter;

    @RabbitListener(queues = "myQueue", ackMode = "MANUAL")
    public void manualAckListener(Message message, Channel channel) throws IOException {

        long deliveryTag = message.getMessageProperties().getDeliveryTag();

        log.info("消费消息 -> {}", jsonConverter.fromMessage(message));

        channel.basicNack(deliveryTag, false, true);
    }

结果:
当 requeue 为 true时,拒绝消息后消息从重新入队,可以看到队列中任然有一条数据。
当 requeue 为 false时,拒绝消息后消息也还是从队列中删除掉了。

Spring-RabbitMQ 消费者消息确认案例实践

模式三、AUTO

默认值,消费者成功时认为成功并从队列中删除消息。消费者失败时认为失败,不会从队列中删除消息。

    @RabbitListener(queues = "myQueue", messageConverter = "jsonConverter")
    public void autoAckListener(User user) {
        log.info("收到消息 -> {}", user);
        // 添加个错误用于测试
        int a = 1 / 0;
    }

结果:
可以看到,消费者出错后,消息依然在队列中。当移除消费者中的错误代码后,成功消费消息后,队列中的数据被删除。
Spring-RabbitMQ 消费者消息确认案例实践

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在 Spring Boot 中使用 RabbitMQ 时,我们可以通过在配置文件中配置绑定关系,将交换机和队列进行绑定。具体步骤如下: 1. 在 application.properties 或 application.yml 中配置 RabbitMQ 连接信息: ``` spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest ``` 2. 创建一个交换机和一个队列,并将它们绑定在一起: ``` @Configuration public class RabbitConfig { @Bean public Queue queue() { return new Queue("myqueue", true); } @Bean public DirectExchange exchange() { return new DirectExchange("myexchange"); } @Bean public Binding binding(Queue queue, DirectExchange exchange) { return BindingBuilder.bind(queue).to(exchange).with("mykey"); } } ``` 3. 在需要发送消息的地方,注入 RabbitTemplate 并调用 convertAndSend 方法: ``` @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String message) { rabbitTemplate.convertAndSend("myexchange", "mykey", message); } ``` 4. 在需要接收消息的地方,注入 SimpleMessageListenerContainer 并实现 MessageListener 接口: ``` @Autowired private Queue queue; @Bean public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames(queue.getName()); container.setMessageListener(listenerAdapter); return container; } @Bean public MessageListenerAdapter listenerAdapter() { return new MessageListenerAdapter(new MyMessageListener()); } public class MyMessageListener implements MessageListener { @Override public void onMessage(Message message) { String body = new String(message.getBody()); System.out.println("Received message: " + body); } } ``` 通过以上步骤,我们就可以实现交换机和队列的绑定,以及在队列中发送和接收消息

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

i余数

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值