简体   繁体   中英

How to not acknowledge only one message with Spring-JMS?

There is a class 'MyConsumer' which receives messages from a queue, and processes them. There are two requirements:

  1. If there is a message contains invalid content, MyConsumer should not acknowledge it, but can process later messages
  2. The unconsumed message will be deliver again when MyConsumer restarts

I tried with spring-jms, with the listener-container supports, but can't find a solution fits the first requirement.

My code:

<amq:queue id="destination" physicalName="org.springbyexample.jms.test"/>

<amq:connectionFactory id="jmsFactory" brokerURL="tcp://localhost:11111"/>

<bean id="jmsConsumerConnectionFactory"
      class="org.springframework.jms.connection.SingleConnectionFactory"
      p:targetConnectionFactory-ref="jmsFactory"/>

<bean id="jmsConsumerTemplate" class="org.springframework.jms.core.JmsTemplate"
      p:connectionFactory-ref="jmsConsumerConnectionFactory"
      p:defaultDestination-ref="destination"/>

<bean id="jmsMessageListener" class="test.MyConsumer"/>
<bean id="errorHandler" class="test.MyErrorHandler"/>

<jms:listener-container container-type="default"
                        connection-factory="jmsConsumerConnectionFactory"
                        error-handler="errorHandler"
                        acknowledge="client">
    <jms:listener destination="org.springbyexample.jms.test" ref="jmsMessageListener"/>
</jms:listener-container>

Class MyConsumer :

@Override
public void onMessage(Message message) {
    TextMessage textMessage = (TextMessage) message;
    try {
        System.out.println("!!!!!!!!! get message: " + textMessage.getText());
    } catch (JMSException e) {
        e.printStackTrace();
    }
    if (theNumberOfMessageIs(3)) {
        throw new RuntimeException("something is wrong");
    }
}

You may notice that the acknowledge in listener-container is client , actually it has 3 values:

  1. auto (default)
  2. client
  3. transacted

I tried all of them, but none fits my requirement. My test scenario:

  1. producer put 3 messages to queue
  2. start a thread to monitor the message count in queue, when the count changes, print it
  3. start consumer, it will receive messages from queue, and processes them
  4. wait a while, put another 3 messages to queue

For auto :

MyConsumer will acknowledge after receiving each message, no matter throwing exception or not

For client :

MyConsumer will acknowledge only if no exception thrown in onMessage . For the 3rd message, it throws exception, there will be a message in the queue unconsummed. But when it get the 4th message and doesn't throw exception, the 3rd message in queue will be disapeared

For transacted :

If exception thrown in MyConsumer, the message will not be acknowledged and be re-delivered several times. After that, the message is disappeared from queue

But none of them fit the requirement 1.

I wonder: if I need to look for other solution than Spring-jms, or my usage is not correct?

auto The DefaultMessageListenerContainer is really designed for transactions - with auto, as you have found, the message is always acknowledged. You can use a SimpleMessagseListenerContainer which will work as you desire, but it has other limitations; see the JavaDocs.

client That's just the way JMS works when you ack #4, #3 is automatically acked too - see the Message JavaDocs. Client mode is used to reduce ack traffic (by, say, acking every 10 messages).

transacted That's a function of the broker, you can configure AMQ to send the bad message to a Dead Letter Queue after some number of retries.

You would need some process to move messages from the DLQ back to the main queue for later retry (perhaps during initialization on restart).

Using WMQ you can achieve the requirement using BackOut feature using BOTHRESH and BOQNAME QUEUE configuration parameters, where BOTHRESH define how many times you will try consume the message and after that parameter BOQNAME define the name of QUEUE that your message you be redelivery. In this case you can use a DLQ QUEUE where you can move messages to main QUEUE after some time or use you main QUEUE as DLQ QUEUE that enable message rotate in you consumer. Hope that helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM