DATAKV-22
+ add Redis container documentation
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<junit.version>4.8.1</junit.version>
|
||||
<log4j.version>1.2.15</log4j.version>
|
||||
<org.codehaus.jackson.version>1.6.1</org.codehaus.jackson.version>
|
||||
<org.mockito.version>1.8.4</org.mockito.version>
|
||||
<org.mockito.version>1.8.5</org.mockito.version>
|
||||
<org.slf4j.version>1.5.8</org.slf4j.version>
|
||||
<org.spockframework.version>0.5-groovy-1.7-SNAPSHOT</org.spockframework.version>
|
||||
<org.springframework.version>3.0.5.RELEASE</org.springframework.version>
|
||||
|
||||
@@ -5,5 +5,6 @@
|
||||
offered by Spring Data Key Value.</para>
|
||||
|
||||
<para><xref linkend="redis"/> introduces the Redis module feature set.</para>
|
||||
<para><xref linkend="riak"/> introduces the Riak module feature set.</para>
|
||||
|
||||
</partintro>
|
||||
182
src/docbkx/reference/redis-messaging.xml
Normal file
182
src/docbkx/reference/redis-messaging.xml
Normal file
@@ -0,0 +1,182 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE preface PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<section>
|
||||
<section id="redis:pubsub:intro">
|
||||
<title>Introduction</title>
|
||||
|
||||
<para>Spring Data provides dedicated messaging integration for Redis,
|
||||
very similar in functionality and naming to the JMS integration in
|
||||
Spring Framework; in fact, users familiar with the JMS support in Spring, should
|
||||
feel right at home.</para>
|
||||
|
||||
<para>Redis messaging can be roughly divided into two areas of functionality, namely
|
||||
the production or publication and consumption or subscription of messages, hence the shortcut
|
||||
pubsub (Publish/Subscribe). The
|
||||
<classname>RedisTemplate</classname> class is used for message production.
|
||||
For asynchronous reception similar to
|
||||
Java EE's message-driven bean style, Spring Data provides a dedicated message
|
||||
listener containers that is used to create Message-Driven POJOs
|
||||
(MDPs) and for synchronous reception, the <interfacename>RedisConnection</interfacename> contract.</para>
|
||||
|
||||
<para>The package <literal>org.springframework.data.keyvalue.redis.connection</literal> and
|
||||
<literal>org.springframework.data.keyvalue.redis.listener</literal> provide
|
||||
the core functionality for using Redis messaging.</para>
|
||||
</section>
|
||||
|
||||
<section id="redis:pubsub:publish">
|
||||
<title>Sending/Publishing messages</title>
|
||||
|
||||
<para>To publish a message, one can use, as with the other operations, either the low-level
|
||||
<interfacename>RedisConnection</interfacename> or the high-level <classname>RedisTemplate</classname>.
|
||||
Both entities offer the <methodname>publish</methodname> method that accepts as argument the message
|
||||
that needs to be sent as well as the destination channel. While <interfacename>RedisConnection</interfacename>
|
||||
requires raw-data (array of bytes), the <classname>RedisTemplate</classname> allow arbitrary objects to be passed
|
||||
in as messages:</para>
|
||||
|
||||
<programlisting lang="java"><![CDATA[// send message through connection
|
||||
RedisConnection con = ...
|
||||
byte[] msg = ...
|
||||
byte[] channel = ...
|
||||
|
||||
con.publish(msg, channel);
|
||||
|
||||
// send message through RedisTemplate
|
||||
RedisTemplate template = ...
|
||||
template.publish("hello!", "world");]]></programlisting>
|
||||
</section>
|
||||
|
||||
<section id="redis:pubsub:subscribe">
|
||||
<title>Receiving/Subscribing for messages</title>
|
||||
|
||||
<para>On the receiving side, one can subscribe to one or multiple channels either by naming them directly or by using
|
||||
pattern matching. The latter approach is quite useful as it not only allows multiple subscriptions to be created with
|
||||
one command but to also listen on channels not yet created at subscription time (as long as match the pattern).
|
||||
</para>
|
||||
|
||||
<para>At the low-level, <interfacename>RedisConnection</interfacename> offers <methodname>subscribe</methodname> and
|
||||
<methodname>pSubscribe</methodname> methods that map the Redis commands for subscribing by channel respectively by pattern.
|
||||
Note that multiple channels or patterns can be used as arguments. To change the subscription of a connection or simply query
|
||||
whether it is listening or not, <interfacename>RedisConnection</interfacename>
|
||||
provides <methodname>getSubscription</methodname> and <methodname>isSubscribed</methodname> method.</para>
|
||||
|
||||
<important>Subscribing commands are synchronized and thus blocking. That is, calling subscribe on a connection will cause
|
||||
the current thread to block as it will start waiting for messages - the thread will be released only if the subscription
|
||||
is canceled, that is an additional thread invokes <methodname>unsubscribe</methodname> respectively <methodname>pUnsubscribe</methodname>
|
||||
on the <emphasis>same</emphasis> connection.</important>
|
||||
|
||||
<para>As mentioned above, one subscribed a connection starts waiting for messages - no other commands can be invoked on it except
|
||||
for adding new subscriptions or modifying/canceling the existing ones, that is invoking anything else then <methodname>subscribe</methodname>,
|
||||
<methodname>pSubscribe</methodname>, <methodname>unsubscribe</methodname>, <methodname>pUnsubscribe</methodname> or is illegal and will
|
||||
through an exception.</para>
|
||||
|
||||
<para>In order to subscribe for messages, one needs to implement the <interfacename>MessageListener</interfacename> callback: each time
|
||||
a new message arrives, the callback gets invoked and the user code executed through <methodname>onMessage</methodname> method.
|
||||
The interface gives access not only to the actual message but to the channel it has been received through and the pattern (if any) used
|
||||
by the subscription to match the channel. This information allows the callee to differentiate between various messages not just by content but
|
||||
also through data.
|
||||
</para>
|
||||
|
||||
<section id="redis:pubsub:subscribe:containers">
|
||||
<title>Message Listener Containers</title>
|
||||
|
||||
<para>Due to its blocking nature, low-level subscription is not attractive as it requires connection and thread management for every single
|
||||
listener. To alleviate this problem, Spring Data offers <classname>RedisListenerContainer</classname> which does all the heavy lifting
|
||||
on behalf of the user - users familiar with EJB and JMS should find the concepts familiar as it is designed as close as possible to the
|
||||
support in Spring Framework and its message-driven POJOs (MDPs)</para>
|
||||
|
||||
<para><classname>RedisListenerContainer</classname> acts as a message listener container; it is used to receive messages from a
|
||||
Redis channel and drive the MessageListener that are injected into
|
||||
it. The listener container is responsible for all threading of message
|
||||
reception and dispatches into the listener for processing. A message
|
||||
listener container is the intermediary between an MDP and a messaging
|
||||
provider, and takes care of registering to receive messages, resource acquisition and release,
|
||||
exception conversion and suchlike. This allows you as an application
|
||||
developer to write the (possibly complex) business logic associated with
|
||||
receiving a message (and reacting to it), and delegates
|
||||
boilerplate Redis infrastructure concerns to the framework.
|
||||
</para>
|
||||
|
||||
<para>Further more, to minimize the application footprint, <classname>RedisListenerContainer</classname> performs allows one connection and one thread
|
||||
to be shared by multiple listeners even though they do not share a subscription. Thus no matter how many listeners or channels an application tracks,
|
||||
the runtime cost will remain the same through out its lifetime. Moreover, the container allows runtime configuration changes so one can add or remove
|
||||
listeners while an application is running without the need for restart. Additionally, the container uses a lazy subscription approach, using a
|
||||
<interfacename>RedisConnection</interfacename> only when needed - if all the listeners are unsubscribed, cleanup is automatically performed and the used
|
||||
thread released.</para>
|
||||
|
||||
<para>To help with the asynch manner of messages, the container requires a <interfacename>java.util.concurrent.Executor</interfacename> (
|
||||
or Spring's <interfacename>TaskExecutor</interfacename>) for dispatching the messages. Depending on the load, the number of listeners or the runtime
|
||||
environment, one should change or tweak the executor to better serve her needs - in particular in managed environments (such as app servers), it is
|
||||
highly recommended to pick a a proper <interfacename>TaskExecutor</interfacename> to take advantage of its runtime.</para>
|
||||
</section>
|
||||
|
||||
<section id="redis:pubsub:subscribe:adapter">
|
||||
<title>The <classname>MessageListenerAdapter</classname></title>
|
||||
|
||||
<para>The <classname>MessageListenerAdapter</classname> class is the
|
||||
final component in Spring's asynchronous messaging support: in a
|
||||
nutshell, it allows you to expose almost <emphasis>any</emphasis> class
|
||||
as a MDP (there are of course some constraints).</para>
|
||||
|
||||
<para>Consider the following interface definition. Notice that although
|
||||
the interface extends the
|
||||
<interfacename>MessageListener</interfacename> interface,
|
||||
it can still be used as a MDP via the use of the
|
||||
<classname>MessageListenerAdapter</classname> class. Notice also how the
|
||||
various message handling methods are strongly typed according to the
|
||||
<emphasis>contents</emphasis> of the various
|
||||
<interfacename>Message</interfacename> types that they can receive and
|
||||
handle.</para>
|
||||
|
||||
<programlisting language="java">public interface MessageDelegate {
|
||||
|
||||
void handleMessage(String message);
|
||||
|
||||
void handleMessage(Map message);
|
||||
|
||||
void handleMessage(byte[] message);
|
||||
|
||||
void handleMessage(Serializable message);
|
||||
}</programlisting>
|
||||
|
||||
<programlisting language="java">public class DefaultMessageDelegate implements MessageDelegate {
|
||||
<lineannotation>// implementation elided for clarity...</lineannotation>
|
||||
}</programlisting>
|
||||
|
||||
<para>In particular, note how the above implementation of the
|
||||
<interfacename>MessageDelegate</interfacename> interface (the above
|
||||
<classname>DefaultMessageDelegate</classname> class) has
|
||||
<emphasis>no</emphasis> Redis dependencies at all. It truly is a POJO that
|
||||
we will make into an MDP via the following configuration.</para>
|
||||
|
||||
<programlisting language="xml"><lineannotation><!-- this is the Message Driven POJO (MDP) --></lineannotation>
|
||||
<emphasis role="bold"><bean id="messageListener" class="org.springframework.data.keyvalue.redis.listener.adapter.MessageListenerAdapter"></emphasis>
|
||||
<constructor-arg>
|
||||
<bean class="redisexample.DefaultMessageDelegate"/>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<lineannotation><!-- and this is the message listener container... --></lineannotation>
|
||||
<bean id="redisContainer" class="org.springframework.data.keyvalue.redis.listener.RedisMessageListenerContainer">
|
||||
<property name="connectionFactory" ref="connectionFactory"/>
|
||||
<property name="messageListeners">
|
||||
<lineannotation><!-- map of listeners and their associated topics (channels or topics) --></lineannotation>
|
||||
<map>
|
||||
<emphasis role="bold"><entry key-ref="messageListener"></emphasis>
|
||||
<bean class="org.springframework.data.keyvalue.redis.listener.ChannelTopic">
|
||||
<constructor-arg value="chatroom">
|
||||
</bean>
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
</bean></programlisting>
|
||||
|
||||
<para>Each time a message is received, the adapter automatically performs
|
||||
translation (using the configured <interfacename>RedisSerializer</interfacename>)
|
||||
between the low-level format and the required object type transparently. Any exception caused by the method invocation
|
||||
is caught and handled by the container (by default, being logged).
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
Reference in New Issue
Block a user