Rabbitmq 如何在Spring AMQP中使用Ack或Nack

Rabbitmq 如何在Spring AMQP中使用Ack或Nack,rabbitmq,spring-amqp,spring-rabbit,Rabbitmq,Spring Amqp,Spring Rabbit,我是春季AMQP的新手。我有一个应用程序,它是生产者向另一个应用程序(消费者)发送消息 一旦消费者收到消息,我们将对数据进行验证 如果数据正确,我们必须确认,并且消息应该从队列中删除。 如果数据不正确,我们必须对数据进行NACK(否定确认),以便在RabbitMQ中重新排队 我偶然发现 **factory.setDefaultRequeueRejected(false)**(它根本不会重新查询消息) **factory.setDefaultRequeueRejected(true)**(发生异常

我是春季AMQP的新手。我有一个应用程序,它是生产者向另一个应用程序(消费者)发送消息

一旦消费者收到消息,我们将对数据进行验证

如果数据正确,我们必须确认,并且消息应该从队列中删除。 如果数据不正确,我们必须对数据进行NACK(否定确认),以便在RabbitMQ中重新排队

我偶然发现

**factory.setDefaultRequeueRejected(false)**(它根本不会重新查询消息)

**factory.setDefaultRequeueRejected(true)**(发生异常时将重新发出消息)

但在我的案例中,我将根据验证确认信息。然后它应该删除该消息。如果是NACK,则重新查询消息

我在RabbitMQ网站上读过

AMQP规范定义了basic.reject方法,该方法允许客户端拒绝单个传递的消息,指示代理丢弃消息或重新获取消息

如何实现上述场景?请给我举几个例子

我尝试了一个小程序

       logger.info("Job Queue Handler::::::::::" + new Date());
        try {

        }catch(Exception e){

            logger.info("Activity Object Not Found Exception so message should be Re-queued the Message::::::::::::::");

        }

        factory.setErrorHandler(new ConditionalRejectingErrorHandler(cause ->{
            return cause instanceof XMLException;
        }));
消息未针对不同的异常重新排队 工厂。setDefaultRequeueRejected(真)

09:46:38854错误[stderr](SimpleAsyncTaskExecutor-1) org.activiti.engine.ActivitiObjectNotFoundException:没有使用键“WF89012”部署进程

09:46:39102信息 [com.example.bip.rabbitmq.handler.ErrorQueueHandler] (SimpleAsynctaskeExecutor-1)从错误队列接收:{Error=can 不提交JPA事务;嵌套异常为 javax.persistence.RollbackException:标记为的事务 仅回滚}

默认情况下,(使用
defaultRequeueRejected=true
)如果侦听器正常退出,容器将确认消息(导致其被删除),或者如果侦听器抛出异常,容器将拒绝(并重新查询)消息

如果侦听器(或错误处理程序)抛出
AmqpRejectAndDontRequeueException
,则默认行为将被覆盖,消息将被丢弃(或路由到DLX/DLQ,如果已配置),容器将调用
basicject(false)
,而不是
basicject(true)

因此,如果验证失败,则抛出一个
AmqpRejectAndDontRequeueException
。或者,使用自定义错误处理程序配置侦听器,以将异常转换为
AmqpRejectAndDontRequeueException

如中所述

如果您确实想自己负责确认,请将确认模式设置为
MANUAL
,并使用
ChannelAwareMessageListener
或使用
@RabbitListener

但是大多数人只是让容器来处理事情(一旦他们了解了发生了什么)。通常,使用手动确认是为了特殊的使用情况,例如延迟确认或提前确认

编辑

我指给你的答案中有一个错误(现已纠正);您必须查看ListenerExecutionFailedException的原因。我刚刚测试了这个,它的工作原理和预期一样

@SpringBootApplication
public class So39530787Application {

    private static final String QUEUE = "So39530787";

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So39530787Application.class, args);
        RabbitTemplate template = context.getBean(RabbitTemplate.class);
        template.convertAndSend(QUEUE, "foo");
        template.convertAndSend(QUEUE, "bar");
        template.convertAndSend(QUEUE, "baz");
        So39530787Application bean = context.getBean(So39530787Application.class);
        bean.latch.await(10, TimeUnit.SECONDS);
        System.out.println("Expect 1 foo:"  + bean.fooCount);
        System.out.println("Expect 3 bar:"  + bean.barCount);
        System.out.println("Expect 1 baz:"  + bean.bazCount);
        context.close();
    }

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setErrorHandler(new ConditionalRejectingErrorHandler(
                t -> t instanceof ListenerExecutionFailedException && t.getCause() instanceof FooException));
        return factory;
    }

    @Bean
    public Queue queue() {
        return new Queue(QUEUE, false, false, true);
    }
    private int fooCount;

    private int barCount;

    private int bazCount;

    private final CountDownLatch latch = new CountDownLatch(5);

    @RabbitListener(queues = QUEUE)
    public void handle(String in) throws Exception {
        System.out.println(in);
        latch.countDown();
        if ("foo".equals(in) && ++this.fooCount < 3) {
            throw new FooException();
        }
        else if ("bar".equals(in) && ++this.barCount < 3) {
            throw new BarException();
        }
        else if ("baz".equals(in)) {
            this.bazCount++;
        }
    }

    @SuppressWarnings("serial")
    public static class FooException extends Exception { }

    @SuppressWarnings("serial")
    public static class BarException extends Exception { }

}

谢谢你的解释。我尝试了一些程序。您能纠正错误吗?总是从错误处理程序中抛出
AmqpRejectAndDontRequeueException
是没有意义的;为此,只需设置
default RequeueRejected=false
。使用中的技术仅为
XMLException
抛出它-您的侦听器必须重新抛出它。
Expect 1 foo:1
Expect 3 bar:3
Expect 1 baz:1