Search code examples
javajmsspring-jmsactivemq-artemisjms-topic

Guaranteed Delivery with JMS topics and ActiveMQ Artemis


I'm discovering the whole JMS world, and for now every time I have tried to use a topic (pub/sub) I ultimately switched to a queue and either used a recipient-list pattern or directly performed the various computations in the consumer (which transaction-wise is not ideal, but I'm not sure if defining plenty of queues is a better practice).

I need to be sure all subscribers will get all the messages, and as I configured it that doesn't seems to be compatible with topics.

I'm using an embedded ActiveMQ Artemis configuration (so no console, which doesn't help debugging), and Spring JMS. My broker.xml looks as follow:

<configuration
    xmlns="urn:activemq"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="urn:activemq /schema/artemis-server.xsd">

    <jms xmlns="urn:activemq:jms">
    </jms>
    <core xmlns="urn:activemq:core">

        <persistence-enabled>true</persistence-enabled>
        <security-enabled>false</security-enabled>

        <acceptors>
            <acceptor name="in-vm">vm://0</acceptor>
        </acceptors>

        <address-settings>
            <address-setting match="#">
                <!-- all queues have redelivery -->
                <redelivery-delay>200</redelivery-delay>
                <max-redelivery-delay>60000</max-redelivery-delay>
                <redelivery-delay-multiplier>1.5</redelivery-delay-multiplier>
                <redelivery-collision-avoidance-factor>0.15</redelivery-collision-avoidance-factor>
                <!-- all queues have dead-letters -->
                <max-delivery-attempts>10</max-delivery-attempts>
                <auto-create-dead-letter-resources>true</auto-create-dead-letter-resources>
            </address-setting>
        </address-settings>
    </core>
</configuration>

My jmsListenerContainerFactory is defined as follow:

DefaultJmsListenerContainerFactory listenerContainerFactory = new DefaultJmsListenerContainerFactory();
listenerContainerFactory.setConnectionFactory(aConnectionFactory);
listenerContainerFactory.setDestinationResolver(aDestinationResolver);
listenerContainerFactory.setSessionTransacted(true);
listenerContainerFactory.setSessionAcknowledgeMode(JMSContext.SESSION_TRANSACTED);
listenerContainerFactory.setMessageConverter(aMessageConverter);
listenerContainerFactory.setTransactionManager(aTransactionManager);
return listenerContainerFactory;

My consumers are @Jmslistener annotated methods in various classes (sometime multiple for one topic in the same class if that matters).

It seems to me as if the messages are not always sent to all the consumers. Or as if the consumers would disconnect themselves from the topic while processing a message.

The behavior is consistent and happens 100% of the time on a test.

Did I missed something big, or aren't topics meant for reliable communication?


Solution

  • What you're seeing is the expected behavior. Generally speaking, a subscriber has to be connected to the broker in order to receive messages. This is traditional publish/subscribe semantics.

    You can change this behavior by using a durable subscription (use setSubscriptionDurable(true) on your instance of DefaultJmsListenerContainerFactory). However, this can be problematic if subscribers disconnect for an extended period of time as messages will accumulate in their subscriptions.