Java RabbitMQ consumer.nextMessage始终获取相同的消息

时间:2017-09-12 13:36:46

标签: java spring rabbitmq amqp spring-rabbit

我们在分布式服务架构中使用Java rabbitMq和spring boot。一个服务获取HTTP请求并将其转发到未知队列进行处理。同时,它必须等待另一个队列的响应才能终止HTTP请求。 (它是一个预览请求,可以通过渲染器完成其工作)。

ServiceA(HTTP接口)和ServiceB(渲染器)可以有多个实例,因此对于每个预览消息,我们还会发送一个唯一ID作为路由密钥。

我遇到BlockingConsumer的问题。每当我调用consumer.nextMessage()时,我会一遍又一遍地得到相同的消息。这是双重奇怪的,因为它应该被确认并从队列中删除,而另一个消费者不应该为它烦恼,因为我们使用的唯一ID不再绑定到队列。 nextMessage甚至在渲染器服务完成之前返回,并且已经发送完成的消息。

以下是简化设置:

常规

所有服务都对所有邮件使用全局DirectExchange

@Bean
  public DirectExchange globalDirectExchange() {
    return new DirectExchange(EXCHANGE_NAME, false, true);
  }

ServiceA (处理HTTP请求):

 private Content requestPreviewByKey(RenderMessage renderMessage, String previewKey) {
    String renderDoneRoutingKey= UUID.randomUUID().toString();
    renderMessage.setPreviewDoneKey(renderDoneId);
    Binding binding = BindingBuilder.bind(previewDoneQueue).to(globalDirectExchange)
        .with(renderDoneRoutingKey);
    try {
      amqpAdmin.declareBinding(binding);
      rabbitProducer.sendPreviewRequestToKey(renderMessage, previewKey);
      return getContentBlocking();
    } catch (Exception e) {
      logErrorIfDebug(type, e);
      throw new ApiException(BaseErrorCode.COMMUNICATION_ERROR, "Could not render preview");
    } finally {
      amqpAdmin.removeBinding(binding);
    }
  }


  private Content getContentBlocking() {
    BlockingQueueConsumer blockingQueueConsumer = new BlockingQueueConsumer(rabbitMqConfig.connectionFactory(), new DefaultMessagePropertiesConverter(), new ActiveObjectCounter<>(), AcknowledgeMode.AUTO, true, 1, PREVIEW_DONE_QUEUE);
    try {
      blockingQueueConsumer.start();
      Message message = blockingQueueConsumer.nextMessage(waitForPreviewMs);
      if (!StringUtils.isEmpty(message)) {
        String result = new String(message.getBody());
        return JsonUtils.stringToObject(result, Content.class);
      }    
      throw new ApiException("Could not render preview");
    } catch (Exception e) {
      logError(e);
      throw new ApiException("Could not render preview");
    } finally {
      blockingQueueConsumer.stop();
    }

}

服务B

我将为您节省大部分代码。我的日志说一切进展顺利,一旦完成,服务就会将正确的消息发送到与初始呈现请求一起发送的UUID密钥。

public void sendPreviewDoneMessage(Content content, String previewDoneKey) {
    String message = JsonUtils.objectToString(content);
    rabbitTemplate.convertAndSend(globalDirectExchange, previewDoneKey, message);
}

整件事有效......一旦... 真正的问题似乎是消费者的设置。当我使用nextMessage()时,为什么我不断从队列中获取相同的(第一个)消息。 不创建和删除Bindung确保在该实例中只接收绑定到该routingKey的消息吗?并且没有nextMessage()确认消息并将其从队列中删除?!

非常感谢与我的关系,甚至更多的任何有用的答案!

1 个答案:

答案 0 :(得分:1)

BlockingQueueConsumer不能直接使用;它是SimpleMessageListenerContainer的一个组件,它将在侦听器使用它之后处理消息(容器调用commitIfNecessary)。

直接使用此消费者可能会产生其他意外的副作用。

我强烈建议使用侦听器容器来使用消息。

如果您只是希望按需接收消息,请改用RabbitTemplate receive()receiveAndConvert()方法。