Spring AMQP Integration - Consumer Manual Acknowledgement

时间:2014-01-10 11:56:33

标签: java rabbitmq spring-integration spring-amqp spring-rabbit

我正在使用Spring-AMQP支持对Spring-Integration进行测试,我正在进行配置和测试:

<rabbit:connection-factory id="connectionFactory" />
<rabbit:queue name="durableQ"/>
<int:channel id="consumingChannel">
    <int:queue capacity="2"/> <!-- Message get Acked as-soon-as filled in Q -->
</int:channel>

<int-amqp:inbound-channel-adapter 
    channel="consumingChannel"
    queue-names="durableQ" 
    connection-factory="connectionFactory"
    concurrent-consumers="1"
    acknowledge-mode="AUTO"
    />


public static void main(String[] args) {
System.out.println("Starting consumer with integration..");
    AbstractApplicationContext context = new ClassPathXmlApplicationContext(
    "classpath:META-INF/spring/integration/spring-integration-context-consumer.xml");

    PollableChannel consumingChannel = context.getBean("consumingChannel",   
                                                          PollableChannel.class);           
        int count = 0;
        while (true) {
            Message<?> msg = consumingChannel.receive(1000);
            System.out.println((count++) + " \t -> " + msg);

            try { //sleep to check number of messages in queue
                Thread.sleep(50000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

在这种配置中,很明显,只要消息到达consumingChannel,它们就会被Acked并因此从队列中删除。我通过在sleep之后放置一个高receive并检查queue-size来验证这一点。没有进一步的控制。

现在,如果我设置acknowledge-mode=MANUAL,似乎没有办法通过spring集成进行手动ack

我需要处理消息,并在处理后执行manual-ack,直到ack消息保持durableQ为止。

有没有办法用MANUAL处理spring-amqp-integration确认?我希望避免将ChannelAwareMessageListener传递给inbound-channel-adapter,因为我希望控制消费者的receive

更新

listener-container使用自己的inbound-channel-adapter时,似乎无法实现:

// Below creates a default direct-channel (spring-integration channel) named "adapter", to receive poll this channel which is same as above
<int-amqp:inbound-channel-adapter id="adapter" listener-container="amqpListenerContainer" /> 

<bean id="amqpListenerContainer" class="org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer">
    <property name="connectionFactory" ref="connectionFactory" />
    <property name="queueNames" value="durableQ" />
    <property name="acknowledgeMode" value="MANUAL" />

// messageListener not allowed when using with adapter, so no way of having own ChannelAwareMessageListener, so no channel exposed onMessage, hence no way to ack
    <property name="messageListener" ref="listener"/>
</bean>
<bean id="listener" class="com.sd.springint.rmq.MsgListener"/>

以上配置引发错误,因为不允许messageListener属性,请参阅标记的内联注释。因此,使用listner-container的目的失败了(通过channel公开ChannelAwareMessageListener)。

对我来说spring-integration不能用于manual-acknowledgement(我知道,这是一个很难说的!),任何人都可以帮我验证这个或者是否有任何特定的方法/配置要求我不见了?

2 个答案:

答案 0 :(得分:3)

问题是因为您使用QueueChannel使用异步切换。通常最好控制容器中的并发性(concurrent-consumers="2")并且不要在流中执行任何异步切换(使用DirectChannel s)。这样,AUTO ack就可以了。而不是从PollableChannel订阅new MessageHandler()订阅SubscribableChannel

<强>更新

您通常不需要处理SI应用程序中的消息,但使用DirectChannel进行的测试相当于......

    SubscribableChannel channel = context.getBean("fromRabbit", SubscribableChannel.class);

    channel.subscribe(new MessageHandler() {

        @Override
        public void handleMessage(Message<?> message) throws MessagingException {
            System.out.println("Got " + message);
        }
    });

答案 1 :(得分:0)

MANUAL Ack仅允许通过Channel.basicAck()。因此,您应该可以访问收到邮件的Channel

尝试使用advice-chain的{​​{1}}:

  1. <int-amqp:inbound-channel-adapter>实施为Advice
  2. Container上的MethodBeforeAdvice适用于advice-chain
  3. 该方法的第一个参数恰好是ContainerDelegate#invokeListener
  4. 假设您可以放置​​在Channel
  5. 中的MessageProperties.headers Channel
  6. 并将Advice<int-amqp:inbound-channel-adapter>配置为mapped-request-headers
  7. 最后尝试在下游流程的任何位置从Spring Integration Message调用Channel标头上的basicAck()