我有一个Web服务,它接收对象,通过AMQP发送通知,并向请求者返回JSON响应。每个请求都在一个线程上执行,我正在尝试实现发布者确认,我正在努力设置它。我有它工作,但我不喜欢我这样做。
我这样做的方式是:
这种技术确实可以正常工作,但我不喜欢假设链将在发布订阅通道中的waitForPublisherConfirm Service Activator之前接收消息。在这种情况下,关于哪个组件首先接收消息的顺序很重要。
如果waitForPublisherConfirms服务激活器首先收到消息,它将阻塞该线程5秒,然后允许链通过amqp:outbound-channel-adapter发送消息。
我尝试将waitForPublisherConfirms放在amqp:outbound-channel-adapter之后,但由于outbound-channel-adapter没有“返回”任何内容,所以服务激活器永远不会在链中调用它。
我觉得应该有更好的方法来做到这一点。我的目标是在向请求者发送响应之前等待发布者确认(或在spring的发布者确认中找不到支持的超时)。
你能否帮助我更好地塑造解决方案,或者让我知道是否可以依赖发布 - 订阅频道的第一个订阅者将始终首先收到消息这一事实。
对不起,这个太久了。
一些配置
<int:header-enricher input-channel="addHeaders" output-channel="metadataIngestNotifications">
<int:header name="routingKey" ref="routingKeyResolver" method="resolveReoutingKey"/>
<int:header name="notificationId" expression="payload.id" />
</int:header-enricher>
<int:chain input-channel="metadataIngestNotifications" output-channel="nullChannel" >
<int:service-activator id="addPublisherConfirmQueue"
requires-reply="false"
ref="publisherConfirmService"
method="addPublisherConfirmQueue" />
<int:object-to-json-transformer id="transformObjectToJson" />
<int-amqp:outbound-channel-adapter id="amqpOutboundChannelAdapter"
amqp-template="rabbitTemplate"
exchange-name="${productNotificationExchange}"
confirm-ack-channel="publisherConfirms"
confirm-nack-channel="publisherConfirms"
mapped-request-headers="*"
routing-key-expression="headers.routingKey"
confirm-correlation-expression="headers.notificationId" />
</int:chain>
<int:service-activator id="waitForPublisherConfirm"
input-channel="metadataIngestNotifications"
output-channel="publisherConfirmed"
requires-reply="true"
ref="publisherConfirmService"
method="waitForPublisherConfirm" />
<int:service-activator id="publisherConfirmReceiver"
ref="publisherConfirmService"
method="receivePublisherConfirm"
input-channel="publisherConfirms"
output-channel="nullChannel" />
类
public class PublisherConfirmService {
private final Map<String, BlockingQueue<Boolean>> suspenders = new HashMap<>();
public Message addPublisherConfirmQueue(@Header("notificationId") String id, Message m){
LogManager.getLogger(this.getClass()).info("Adding publisher confirm queue.");
BlockingQueue<Boolean> bq = new LinkedBlockingQueue<>();
suspenders.put(id, bq);
return m;
}
public boolean waitForPublisherConfirm(@Header("notificationId") String id) {
LogManager.getLogger(this.getClass()).info("Waiting for publisher confirms for Notification: " + id);
BlockingQueue<Boolean> bq = suspenders.get(id);
try {
Boolean result = bq.poll(5, TimeUnit.SECONDS);
if(result == null){
LogManager.getLogger(this.getClass()).error("The broker took too long to return a publisher confirm. NotificationId: " + id);
return false;
}else if(!result){
LogManager.getLogger(this.getClass()).error("The publisher confirm indicated that the message was not confirmed. NotificationId: " + id);
return false;
}
} catch (InterruptedException ex) {
LogManager.getLogger(this.getClass()).error("Something went wrong polling for the publisher confirm for notificationId: " + id, ex);
return false;
}finally{
suspenders.remove(id);
}
return true;
}
public void receivePublisherConfirm(String id, @Header(AmqpHeaders.PUBLISH_CONFIRM) boolean confirmed){
LogManager.getLogger(this.getClass()).info("Received publisher confirm for Notification: " + id);
if (suspenders.containsKey(id)){
BlockingQueue<Boolean> bq = suspenders.get(id);
bq.add(confirmed);
}
}
}
答案 0 :(得分:0)
为了同样的目的,如何查看聚合器解决方案?
<recipient-list-router>
向聚合器的input-channel
发送消息,向<int-amqp:outbound-channel-adapter>
发送第二个频道。
confirm-ack-channel
必须是在某些转换后将消息带到同一聚合器的东西,例如适当提取correlationKey
等等。