我们有一个接收事件通知的入站通道适配器。消费者标准的复杂性限制了我们使用简单路由密钥分发消息的能力,因此应用程序使用分离器通过直接交换将该消息发送给感兴趣的订户队列。
我们希望在出站渠道适配器上使用发布商确认,以确保向客户端队列的传递。我们希望等待发布者确认ack
原始邮件,如果发件人确认无法收到,或者ack==false
我们想要解除来自入站通道适配器的原始邮件。
我假设这将在Rabbit模板的confirm-callback
中完成,但我不知道如何实现这一目标。 (或者,如果可能的话)
<rabbit:connection-factory id="rabbitConnectionFactory"
host="${amqpHost}"
username="${amqpUsername}"
password="${amqpPassword}"
virtual-host="${amqpVirtualHost}"
publisher-confirms="true" />
<rabbit:template id="rabbitTemplate"
connection-factory="rabbitConnectionFactory"
confirm-callback="PublisherConfirms" />
<int-amqp:inbound-channel-adapter channel="notificationsFromRabbit"
queue-names="#{'${productNotificationQueue}' + '${queueSuffix}'}"
connection-factory="rabbitConnectionFactory"
mapped-request-headers="*"
message-converter="productNotificationMessageConverter" />
<int:chain input-channel="notificationsFromRabbit" output-channel="notificationsToClients">
<int:service-activator ref="NotificationRouter"
method="addRecipientsHeaders" />
<int:splitter ref="NotificationRouter"
method="groupMessages" />
<int:object-to-json-transformer />
</int:chain>
<int-amqp:outbound-channel-adapter channel="notificationsToClients"
amqp-template="rabbitTemplate"
exchange-name="${servicesClientsExchange}"
routing-key=""
mapped-request-headers="*" />
目前,我们通过将Channel和Delivery标记作为参数传递acking
方法中的groupMessages
消息。但是,如果代理从不发送return
或返回ack=false
,那么nack
来自入站通道适配器的消息为时已晚。
我是否需要一个bean来保留Map<Channel, Long>
频道和投递标签,以便在confirm-callback
中访问,还是有其他方式?
在我收到发布商确认时,来自入站频道适配器的频道是否会关闭?
答案 0 :(得分:2)
只要暂停消费者线程直到收到所有的ack / nack,你就可以做你想做的事。
如果您使notificationsFromRabbit
成为发布 - 订阅频道,您可以添加另一个订阅者(服务激活器)来暂停该线程;等待所有的ack / nacks并采取你想要的行动。
编辑:
您也可以使用Spring Integration to manage the acks for you,它会将它们作为来自出站适配器的消息发出(而不是自己使用回调)。
EDIT2:
然后,您可以在关联数据中使用拆分器的序列大小/序列号标头,从而在收到所有确认时启用消费者。
EDIT3:
这样的事情应该有用......
在出站适配器上,设置confirm-correlation-expression="#this"
(整个出站消息)。
有两种方法的类
private final Map<String, BlockingQueue<Boolean> suspenders;
public void suspend(Message<?> original) {
BlockingQueue<Boolean> bq = new LinkedBlockingQueue();
String key = someKeyFromOriginal(original);
suspenders.put(key, bq);
Boolean result = bq.poll(// some timeout);
try {
if (result == null) {
// timed out
}
else if (!result) {
// throw some exception to nack the message
}
}
finally {
suspenders.remove(key);
}
}
public void ackNack(Message<Message<?>> ackNak) {
Message<?> theOutbound = ackNak.payload;
BlockingQueue<Boolean> bq = suspenders.get(someKeyFromOriginal(theOutbound));
if (bq == null) // late ack/nack; ignore
else {
// check the ack/nack header
// if nack, bq.put(false)
// else, use another map field, to
// keep track of ack count Vs sequenceSize header in
// theOutbound; when all acks received, bq.put(true);
}
}
在第一种方法中暂停使用者线程;将acks / nack从出站适配器路由到第二种方法。
警告:这不是测试,只是在我的头顶;但它应该非常接近。