处理Publisher确认Spring AMQP-RabbitMQ中的超时

时间:2015-06-26 19:49:01

标签: spring rabbitmq spring-rabbit

我正在测试AMQP的Spring RabbitMQ实现,我想使用发布者确认。我在文档和代码中遗漏的是我应该如何处理特定年龄的未经证实的消息。

裸RabbitMQ客户端java库提供了一个很好的Channel.waitForConfirmsOrDie(timeout)方法,但是这会迫使我在Spring抽象下更深入,也为什么我不想继续发布并重试未经证实的消息? (顺便说一句,如果可以使用弹簧重试,那将是很棒的,目前我必须实现它。)

我确实找到RabbitTemplate.getUnconfirmed(long),但我遇到的问题是它似乎不是线程安全的,因为当我的发布者连续发送邮件并且我尝试重新发送超过5秒的未经证实的邮件时,它会抛出一个错误:

Exception in thread "publisher-A-500000to999999" java.util.ConcurrentModificationException
at java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1207)
at java.util.TreeMap$EntryIterator.next(TreeMap.java:1243)
at java.util.TreeMap$EntryIterator.next(TreeMap.java:1238)
at org.springframework.amqp.rabbit.core.RabbitTemplate.getUnconfirmed(RabbitTemplate.java:503)
at com.mycompany.rabbitmq.tools.failover.Publisher.resendUnconfirmed(Publisher.java:65)
at com.mycompany.rabbitmq.tools.failover.Publisher.run(Publisher.java:52)
at java.lang.Thread.run(Thread.java:745)

我可能正在做一些完全错误的事情,因为我使用CorrelationData作为消息的持有者,因此重新发送更容易。

我创建了一个MessageConfirmData类:

private static class MessageCorrelationData extends  CorrelationData {

    private final Message message;
    private final long messageIndex;
    private final int retryCount;

    public MessageCorrelationData(Message message, long messageIndex, int retryCount) {
        super(UUID.randomUUID().toString());
        this.message = message;
        this.messageIndex = messageIndex;
        this.retryCount = retryCount;
    }
}

这是我的重发逻辑,我在每发送100条消息后调用它:

private int resendUnconfirmed() {
    Collection<CorrelationData> unconfirmed = rabbitTemplate.getUnconfirmed(5000);
    int numUnconfirmed = 0;
    if (unconfirmed != null ) {
        numUnconfirmed = unconfirmed.size();

        for (CorrelationData correlationData : unconfirmed) {
            MessageCorrelationData messageCorrelationData = (MessageCorrelationData) correlationData;
            trySend(exchange, messageCorrelationData.message, messageCorrelationData.retryCount + 1, messageCorrelationData.messageIndex);
        }
    }
    return numUnconfirmed;
}

我回复确认:

rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
        MessageCorrelationData mcd = (MessageCorrelationData) correlationData;
        if (!ack) {
            LOG.error("NACK, cause: " + cause + " resending, retry: " + mcd.retryCount);
            trySend(exchange,mcd.message, mcd.retryCount + 1, mcd.messageIndex);
        }
    });

最后发送:

    rabbitTemplate.convertAndSend(exchange, "", amqpMessage, new MessageCorrelationData(amqpMessage, messageIndex, retryCount));

1 个答案:

答案 0 :(得分:0)

你发现了一个错误;如果你想跟随它,我已经提出JIRA Issue

我不确定Spring Retry如何在这里提供帮助;如果您有一些想法,请随意打开新功能或“改进”JIRA问题。

修改

Pull request issued

我们应该很快就会发布它。