Как использовать Ack или Nack весной AMQP

Я новичок в Spring AMQP. У меня есть приложение, которое является производителем, отправляющим сообщения другому приложению, которое является потребителем.

как только потребитель получит сообщение, мы сделаем проверку данных.

если данные правильные, мы должны ACK и сообщение должно быть удалено из очереди. Если данные неправильны, мы должны NACK (отрицательное подтверждение) данные, так что он будет повторно в очереди в RabbitMQ.

Я пришел поперек

**factory.setDefaultRequeueRejected(false);** (он не будет запрашивать сообщение вообще)

**factory.setDefaultRequeueRejected(true);** (он запросит сообщение, когда произойдет исключение)

но в моем случае я признаю сообщение, основанное на проверке. Затем он должен удалить сообщение. Если NACK затем requeue сообщение.

Я прочитал на сайте RabbitMQ

спецификация AMQP определяет основное.метод 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 (true)

09: 46: 38,854 ошибка [stderr] (SimpleAsyncTaskExecutor-1) org.активити.двигатель.ActivitiObjectNotFoundException: нет процессов развернуто с ключом 'WF89012'

09:46:39,102 информация [com.образец.бип.в RabbitMQ.обработчик.ErrorQueueHandler] (SimpleAsyncTaskExecutor-1) получено из очереди ошибок: {ERROR=Could не фиксировать транзакцию JPA; вложенное исключение javax.стойкость.RollbackException: транзакция, помеченная как rollbackOnly}

1 ответов


посмотреть документация.

по умолчанию (с defaultRequeueRejected=true) контейнер будет проверять сообщение (вызывая его удаление), Если прослушиватель выходит нормально или отклоняет (и запрашивает) его, если прослушиватель выдает исключение.

если прослушиватель (или обработчик ошибок) выдает AmqpRejectAndDontRequeueException, поведение по умолчанию переопределяется, и сообщение отбрасывается (или направляется в DLX / DLQ, если так настроено) - контейнер вызывает basicReject(false) вместо basicReject(true).

Итак, если ваша проверка не удалась, бросьте AmqpRejectAndDontRequeueException. Или настройте прослушиватель с помощью пользовательского обработчика ошибок, чтобы преобразовать исключение в AmqpRejectAndDontRequeueException.

это описано в ответ.

если вы действительно хотите взять на себя ответственность за acking себя, Установите режим подтверждения MANUAL и использовать ChannelAwareMessageListener или эта техника если вы используете @RabbitListener.

но большинство людей просто дайте контейнер позаботится о вещах (как только они поймут, что происходит). Как правило, использование ручных acks для особых случаев использования, таких как отсрочка acks или раннее acking.

редактировать

в ответе, на который я указал вам (теперь исправлено), была ошибка; вы должны посмотреть на причину 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 { }

}

результат:

Expect 1 foo:1
Expect 3 bar:3
Expect 1 baz:1