0

I have a spring integration code that connects a JMS listener (that listens to a mq series local queue)

    <int-jms:message-driven-channel-adapter>

and forwards the message to a rabbitmq (not controlled by me) via a

    <int-amqp:outbound-channel-adapter>

I am trying to keep this transactional meaning that if the rabbitmq did not receive the message I want to keep it in the MQ series local queue. However I noticed that if the exchange that was mentioned in the rabbitmq configurations did not exist I see in my logs this line:

ERROR (CachingConnectionFactory.java:292)     - Channel shutdown: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'xxx' in vhost 'xxx', class-id=60, method-id=40)

But my message disappears for the mq series local queue.

What shall i do do make it retry to send the message if the rabbitmq broker fails or if the exchange does not exists?

Thanks for your help, My configuration:

   <int-jms:message-driven-channel-adapter
        id="x.y.z" channel="channel-in"
        error-channel="errorChannel" header-mapper="jmsIntegrationHeaderMapper"
        acknowledge="transacted" destination-name="a.b.c" />

    <int:channel id="channel-in">

    </int:channel>

    <int:header-enricher input-channel="channel-in"
        output-channel="channel-out">
        <int:header name="url"
            expression="'amqp://${amqp.user}@${amqp.host}:${amqp.port}/${amqp.vhost}'"></int:header>
    </int:header-enricher>


    <int:channel id="channel-out">
    </int:channel>

    <int-amqp:outbound-channel-adapter
        channel="channel-out" amqp-template="amqpTemplate"
        routing-key="crd" mapped-request-headers="*" exchange-name="${amqp.exchange}">
    </int-amqp:outbound-channel-adapter>


    <rabbit:connection-factory id="amqpConnectionFactory" addresses="${amqp.host}:${amqp.port}"
                           cache-mode="CONNECTION"
                           channel-cache-size="25"
                           username="${amqp.user}"
                           password="${amqp.pass}"
                           virtual-host="${amqp.vhost}"/>

    <rabbit:template id="amqpTemplate"
                 connection-factory="amqpConnectionFactory" mandatory="true" channel-transacted="true"/>
mortada
  • 23
  • 6
  • What version of Spring Integration are you using? – Gary Russell Aug 07 '19 at 15:18
  • I am using spring integration 4. – mortada Aug 07 '19 at 19:45
  • You need to set `acknowledge=transcted` so that an exception will roll back the transaction; we changed the default to that in 4.2. – Gary Russell Aug 07 '19 at 20:47
  • It was already set ` ` The strange this is that there is no exception only a line of error that I mentioned in the post. – mortada Aug 08 '19 at 06:31
  • I use a CachingConnectionFactor `public void shutdownCompleted(ShutdownSignalException cause) { if (RabbitUtils.isPassiveDeclarationChannelClose(cause)) { if (logger.isDebugEnabled()) { logger.debug("Channel shutdown: " + cause.getMessage()); } } else if (!RabbitUtils.isNormalChannelClose(cause)) { logger.error("Channel shutdown: " + cause.getMessage()); } }` the code only logs the error but does not raise an exception, maybe that's why? – mortada Aug 08 '19 at 07:44

1 Answers1

0

Unlike JMS, publishing to RabbitMQ is asynchronous (which is why it's generally faster), so the failure for a missing exchange is reported on a different thread; you can add a listener to get the notification instead of it just being logged.

You can enable transactions template.setChannelTransacted(true); and then the failure will be reported on the publishing thread because it will be blocked waiting for the reply from the txCommit() when the IO error occurs.

However, transactions are quite expensive so can hurt performance.

For other errors (e.g. exchange exists, but no route to a queue), you can use publisher confirms and returns but, again, waiting for a confirmation for each individual message published will slow down performance.

EDIT

You should get an exception on txCommit.

Caused by: org.springframework.amqp.AmqpIOException: java.io.IOException
    at org.springframework.amqp.rabbit.connection.RabbitUtils.commitIfNecessary(RabbitUtils.java:107) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.doSend(RabbitTemplate.java:2003) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.lambda$send$3(RabbitTemplate.java:865) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:1841) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:1784) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.send(RabbitTemplate.java:864) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:927) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(RabbitTemplate.java:921) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at com.example.So47454769Application.lambda$0(So47454769Application.java:29) [classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780) [spring-boot-2.0.0.M7.jar:2.0.0.M7]
    ... 5 common frames omitted
Caused by: java.io.IOException: null
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:126) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:122) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:144) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:1519) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.ChannelN.txCommit(ChannelN.java:52) ~[amqp-client-5.0.0.jar:5.0.0]
    at org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl.txCommit(PublisherCallbackChannelImpl.java:588) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_181]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_181]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_181]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_181]
    at org.springframework.amqp.rabbit.connection.CachingConnectionFactory$CachedChannelInvocationHandler.invoke(CachingConnectionFactory.java:981) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    at com.sun.proxy.$Proxy60.txCommit(Unknown Source) ~[na:na]
    at org.springframework.amqp.rabbit.connection.RabbitUtils.commitIfNecessary(RabbitUtils.java:104) ~[spring-rabbit-2.0.2.BUILD-SNAPSHOT.jar:2.0.2.BUILD-SNAPSHOT]
    ... 14 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'junk' in vhost '/', class-id=60, method-id=40)
    at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:36) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:494) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:288) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:138) ~[amqp-client-5.0.0.jar:5.0.0]
    ... 24 common frames omitted
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no exchange 'junk' in vhost '/', class-id=60, method-id=40)
    at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:504) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:346) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:178) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:111) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:643) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:47) ~[amqp-client-5.0.0.jar:5.0.0]
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:581) ~[amqp-client-5.0.0.jar:5.0.0]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_181]

Gary Russell
  • 166,535
  • 14
  • 146
  • 179