9.7. Durable Subscriber
Overview
A durable subscriber, as shown in Figure 9.6, “Durable Subscriber Pattern”, is a consumer that wants to receive all of the messages sent over a particular publish-subscribe channel, including messages sent while the consumer is disconnected from the messaging system. This requires the messaging system to store messages for later replay to the disconnected consumer. There also has to be a mechanism for a consumer to indicate that it wants to establish a durable subscription. Generally, a publish-subscribe channel (or topic) can have both durable and non-durable subscribers, which behave as follows:
- non-durable subscriber—Can have two states: connected and disconnected. While a non-durable subscriber is connected to a topic, it receives all of the topic's messages in real time. However, a non-durable subscriber never receives messages sent to the topic while the subscriber is disconnected.
- durable subscriber—Can have two states: connected and inactive. The inactive state means that the durable subscriber is disconnected from the topic, but wants to receive the messages that arrive in the interim. When the durable subscriber reconnects to the topic, it receives a replay of all the messages sent while it was inactive.
Figure 9.6. Durable Subscriber Pattern
JMS durable subscriber
The JMS component implements the durable subscriber pattern. In order to set up a durable subscription on a JMS endpoint, you must specify a client ID, which identifies this particular connection, and a durable subscription name, which identifies the durable subscriber. For example, the following route sets up a durable subscription to the JMS topic,
news
, with a client ID of conn01
and a durable subscription name of John.Doe
:
from("jms:topic:news?clientId=conn01&durableSubscriptionName=John.Doe"). to("cxf:bean:newsprocessor");
You can also set up a durable subscription using the ActiveMQ endpoint:
from("activemq:topic:news?clientId=conn01&durableSubscriptionName=John.Doe"). to("cxf:bean:newsprocessor");
If you want to process the incoming messages concurrently, you can use a SEDA endpoint to fan out the route into multiple, parallel segments, as follows:
from("jms:topic:news?clientId=conn01&durableSubscriptionName=John.Doe"). to("seda:fanout"); from("seda:fanout").to("cxf:bean:newsproc01"); from("seda:fanout").to("cxf:bean:newsproc02"); from("seda:fanout").to("cxf:bean:newsproc03");
Where each message is processed only once, because the SEDA component supports the competing consumers pattern.
Alternative example
Another alternative is to combine the Message Dispatcher or Content-Based Router with File component or JPA component components for durable subscribers then something like SEDA component for non-durable.
Using the Fluent Builders
from("direct:start").to("activemq:topic:foo"); from("activemq:topic:foo?clientId=1&durableSubscriptionName=bar1").to("mock:result1"); from("activemq:topic:foo?clientId=2&durableSubscriptionName=bar2").to("mock:result2");
Using the Spring XML Extensions
<route> <from uri="direct:start"/> <to uri="activemq:topic:foo"/> </route> <route> <from uri="activemq:topic:foo?clientId=1&durableSubscriptionName=bar1"/> <to uri="mock:result1"/> </route> <route> <from uri="activemq:topic:foo?clientId=2&durableSubscriptionName=bar2"/> <to uri="mock:result2"/> </route>
Here is another example of JMS durable subscribers, but this time using virtual topics (recommended by AMQ over durable subscriptions)
Using the Fluent Builders
from("direct:start").to("activemq:topic:VirtualTopic.foo"); from("activemq:queue:Consumer.1.VirtualTopic.foo").to("mock:result1"); from("activemq:queue:Consumer.2.VirtualTopic.foo").to("mock:result2");
Using the Spring XML Extensions
<route> <from uri="direct:start"/> <to uri="activemq:topic:VirtualTopic.foo"/> </route> <route> <from uri="activemq:queue:Consumer.1.VirtualTopic.foo"/> <to uri="mock:result1"/> </route> <route> <from uri="activemq:queue:Consumer.2.VirtualTopic.foo"/> <to uri="mock:result2"/> </route>