我们在分布式服务架构中使用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()确认消息并将其从队列中删除?!
非常感谢与我的关系,甚至更多的任何有用的答案!
答案 0 :(得分:1)
BlockingQueueConsumer
不能直接使用;它是SimpleMessageListenerContainer
的一个组件,它将在侦听器使用它之后处理消息(容器调用commitIfNecessary
)。
直接使用此消费者可能会产生其他意外的副作用。
我强烈建议使用侦听器容器来使用消息。
如果您只是希望按需接收消息,请改用RabbitTemplate
receive()
或receiveAndConvert()
方法。