使用多个RabbitTemplate对象时超时后收到回复

时间:2018-06-12 14:40:51

标签: rabbitmq spring-amqp spring-rabbit

我有一个Java Spring Boot 1.5.10应用程序,我将连接到两个不同的RabbitMQ服务器。一个RabbitMQ服务器与Spring Boot应用程序在同一主机上运行,​​另一个在另一个/远程主机上运行。  这个版本的Spring Boot包括org.springframework.amqp:spring-amqp:jar:1.7.6.RELEASE,顺便说一下。所以,这里有一些与本地RabbitMQ服务器有关的配置代码:

@Bean
public ConnectionFactory rabbitConnectionFactory() {
    CachingConnectionFactory factory = new CachingConnectionFactory(host);
    factory.setVirtualHost(vhost);
    factory.setUsername(username);
    factory.setPassword(password);
    factory.setChannelCacheSize(2);

    // Add a custom client connection property, which will show up in the Admin UI (useful for troubleshooting).
    factory.getRabbitConnectionFactory().getClientProperties().put("Connection Type", "Local");

    return factory;
}

@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory rabbitConnectionFactory,
        MessageConverter jackson2JsonMessageConverter) {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitConnectionFactory);
    rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
    return rabbitTemplate;
}

@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory rabbitConnectionFactory) {
    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(rabbitConnectionFactory);
    return factory;
}

@Bean
public RabbitAdmin admin(ConnectionFactory rabbitConnectionFactory) {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitConnectionFactory);
    rabbitAdmin.afterPropertiesSet();
    rabbitAdmin.setAutoStartup(false);
    return rabbitAdmin;
}

以下是我在另一台主机上远程运行的RabbitMQ服务器的一些配置代码:

@Bean
public ConnectionFactory remoteConnectionFactory() {
    CachingConnectionFactory factory = new CachingConnectionFactory(remoteHost);
    factory.setVirtualHost(remoteVhost);
    factory.setUsername(remoteUsername);
    factory.setPassword(remotePassword);

    // By default, only one Channel will be cached, with further requested Channels being created and disposed on demand.
    // Consider raising the "channelCacheSize" value in case of a high-concurrency environment.
    factory.setChannelCacheSize(3);

    factory.setConnectionThreadFactory(new CustomizableThreadFactory("RemoteRabbit-"));

    // Add a custom client connection property, which will show up in the Admin UI (useful for troubleshooting).
    factory.getRabbitConnectionFactory().getClientProperties().put("Connection Type", "Remote");

    return factory;
}

@Bean
public RabbitAdmin remoteAdmin(ConnectionFactory remoteConnectionFactory) {
    RabbitAdmin rabbitAdmin = new RabbitAdmin(remoteConnectionFactory);
    rabbitAdmin.setIgnoreDeclarationExceptions(true);
    return rabbitAdmin;
}

@Bean
public SimpleRabbitListenerContainerFactory remoteContainerFactory(ConnectionFactory remoteConnectionFactory,
        MessageConverter jackson2JsonMessageConverter) {

    SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
    factory.setConnectionFactory(remoteConnectionFactory);
    factory.setConcurrentConsumers(1);
    factory.setMessageConverter(jackson2JsonMessageConverter);
    factory.setMaxConcurrentConsumers(5);
    factory.setDefaultRequeueRejected(false); // on error, don't put back in the queue
    return factory;
}

现在是好的部分。我有另一个RabbitTemplate我正在调用convertSendAndReceive(),在其上配置了remoteConnectionFactory。

@Bean
public RabbitTemplate payTemplate(ConnectionFactory remoteConnectionFactory,
        TopicExchange fromExchange, MessageConverter jackson2JsonMessageConverter) {
    RabbitTemplate rabbitTemplate = new RabbitTemplate(remoteConnectionFactory);
    rabbitTemplate.setReplyAddress(fromExchange.getName() + "/" + buildMyBindingKey());
    rabbitTemplate.setReplyTimeout(30000L);
    rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);
    return rabbitTemplate;
}

@Bean
public SimpleMessageListenerContainer payReplyContainer(ConnectionFactory remoteConnectionFactory,
        RabbitTemplate payTemplate, Queue fromQueue) {
    SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
    container.setConnectionFactory(remoteConnectionFactory);
    container.setQueues(fromQueue);
    container.setMessageListener(payTemplate);
    return container;
}

@Bean
public TopicExchange fromExchange(RabbitAdmin remoteAdmin) {
    TopicExchange exchange = new TopicExchange("some.from.exchange", true, false);
    exchange.setAdminsThatShouldDeclare(remoteAdmin);
    return exchange;
}

@Bean
public Queue fromQueue(RabbitAdmin remoteAdmin) {
    Queue queue = new Queue("myReplyQueue");
    queue.setAdminsThatShouldDeclare(corporateAdmin);
    return queue;
}

private String buildMyBindingKey() {
    return "someBindingKeyHere";
}

当我在payTemplate上调用convertSendAndReceive()时,会出现问题。回复收到很好,但几乎似乎收到了两次。当我连接到一个RabbitMQ服务器时,这很有用,但在配置到两个服务器的连接之后,我得到了这个:

2018-06-11 18:29:57,156|WARN||payReplyContainer-1|org.springframework.amqp.rabbit.core.RabbitTemplate|||||Reply received after timeout for 1

2018-06-11 18:29:57,165|WARN||payReplyContainer-1|org.springframework.amqp.rabbit.listener.ConditionalRejectingErrorHandler|||||Execution of Rabbit message listener failed.
org.springframework.amqp.rabbit.listener.exception.ListenerExecutionFailedException: Listener threw exception
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.wrapToListenerExecutionFailedExceptionIfNeeded(AbstractMessageListenerContainer.java:949)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:902)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:790)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$001(SimpleMessageListenerContainer.java:105)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$1.invokeListener(SimpleMessageListenerContainer.java:208)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.invokeListener(SimpleMessageListenerContainer.java:1349)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.executeListener(AbstractMessageListenerContainer.java:760)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:1292)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:1262)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$1800(SimpleMessageListenerContainer.java:105)
        at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1518)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.amqp.AmqpRejectAndDontRequeueException: Reply received after timeout
        at org.springframework.amqp.rabbit.core.RabbitTemplate.onMessage(RabbitTemplate.java:1759)
        at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:899)
        ... 10 common frames omitted

同样,payTemplate确实得到了它正在等待的回复/响应,但它像容器一样收到了另一条没有人在等待的消息。我很难过。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:0)

实际上,这是一种交换(一种不应该存在的额外约束),它负责复制响应。非常感谢Gary检查我的代码,并且可能没有看错,并提出了一些建议。