如何根据发布商确认提供回复

时间:2015-07-21 19:42:33

标签: spring-integration spring-amqp

我有一个Web服务,它接收对象,通过AMQP发送通知,并向请求者返回JSON响应。每个请求都在一个线程上执行,我正在尝试实现发布者确认,我正在努力设置它。我有它工作,但我不喜欢我这样做。

我这样做的方式是:

  • 在邮件上添加一些标题
  • 拥有包含2个订阅者的发布 - 订阅频道
  • 订阅者1)创建一个阻塞队列,使其准备就绪 并通过amqp发送消息
  • 订阅者2)开始在该队列上拉5秒钟,直到它得到确认
  • amqp:outbound-channel-adapter将其发布者确认发送给服务激活器
  • publisherConfirmReceiver收到确认并将其置于阻塞队列中,导致订阅者2完成并返回确认结果。

这种技术确实可以正常工作,但我不喜欢假设链将在发布订阅通道中的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);
        }
    } 
}

1 个答案:

答案 0 :(得分:0)

为了同样的目的,如何查看聚合器解决方案?

<recipient-list-router>向聚合器的input-channel发送消息,向<int-amqp:outbound-channel-adapter>发送第二个频道。

confirm-ack-channel必须是在某些转换后将消息带到同一聚合器的东西,例如适当提取correlationKey等等。