Monday 2 December 2013

Ehcache Replication in Spring using JMS

JMS can also be used as the underlying mechanism for replication operations in Ehcache. The Ehcache jmsreplication module lets organisations with a message queue investment leverage it for caching. It provides replication between cache nodes using a replication topic, pushing of data directly to cache nodes from external topic publishers, and a JMSCacheLoader, which sends cache load requests to a queue.

Ehcache replicates using JMS as follows:

Each cache node subscribes to a predefined topic, configured as the <topicBindingName> in ehcache.xml.
Each replicated cache publishes cache Elements to that topic. Replication is configured per cache.

To set up replicated caching using JMS you need to configure a JMSCacheManagerPeerProviderFactory which is done globally for a CacheManager.

For each cache that wishing to replicate, you add a JGroupsCacheReplicatorFactory element to the cache element.

Ehcache Image

Configuration


Each cluster needs to use a fixed topic name for replication. Set up a topic using the tools in your message queue. Out of the box, both ActiveMQ and Open MQ support auto creation of destinations, so this step may be optional.

For this example, an ActiveMQ server has been used. You can download it from here.

Download and extract it and locate the bin folder. Running the batch file - activemq will start the ActiveMQ server. Its default port is 61616.



There are two things to configure for ehcache:


- The JMSCacheManagerPeerProviderFactory which is done once per CacheManager and therefore once per ehcache.xml file.

- The JMSCacheReplicatorFactory which is added to each cache's configuration if you want that cache replicated.

The main configuration happens in the JGroupsCacheManagerPeerProviderFactory connect sub-property. A connect property is passed directly to the JGroups channel and therefore all the protocol stacks and options available in JGroups can be set.
Contents of ehcache.xml:


<?xml version="1.0" encoding="UTF-8"?>

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="true" monitoring="autodetect" dynamicConfig="true">
<cacheManagerPeerProviderFactory

class="net.sf.ehcache.distribution.jms.JMSCacheManagerPeerProviderFactory"

properties="initialContextFactoryName=com.spring.hibernate.activemq.MyActiveMQInitialContextFactory,
providerURL=tcp://10.35.34.193:61616, 
replicationTopicConnectionFactoryBindingName=topicConnectionFactory,
replicationTopicBindingName=ehcache, 
getQueueConnectionFactoryBindingName=queueConnectionFactory,
getQueueBindingName=ehcacheGetQueue, 
topicConnectionFactoryBindingName=topicConnectionFactory,topicBindingName=ehcache"

propertySeparator="," 

/>
<cacheManagerPeerListenerFactory
 class="org.terracotta.ehcachedx.monitor.probe.ProbePeerListenerFactory"
 properties="monitorAddress=10.35.34.193, monitorPort=9889,              memoryMeasurement=true" />

<cache name="employees" eternal="false" maxElementsInMemory="1000"
overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0"
timeToLiveSeconds="500" memoryStoreEvictionPolicy="LRU" statistics="true">

<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jms.JMSCacheReplicatorFactory"
properties="replicateAsynchronously=true,
                  replicatePuts=true,
                  replicateUpdates=true,
                  replicateUpdatesViaCopy=true,
                  replicateRemovals=true,
                  asynchronousReplicationIntervalMillis=1000"
propertySeparator="," />
</cache>

</ehcache>

You need to provide your own ActiveMQInitialContextFactory for the initialContextFactoryName. An example which should work for most purposes is 

MyActiveMQInitialContextFactory.java:-

package com.spring.hibernate.activemq;


import java.net.URISyntaxException;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


import javax.naming.Context;
import javax.naming.NamingException;


import net.sf.ehcache.distribution.jms.JMSUtil;


import org.apache.activemq.jndi.ActiveMQInitialContextFactory;


public class MyActiveMQInitialContextFactory extends
ActiveMQInitialContextFactory {


/**
* Creates an initial context with {@inheritDoc}
*/
@SuppressWarnings("rawtypes")
@Override
public Context getInitialContext(Hashtable environment)
throws NamingException {


Map<String, Object> data = new ConcurrentHashMap<String, Object>();


String replicationTopicConnectionFactoryBindingName = (String) environment
.get(JMSUtil.TOPIC_CONNECTION_FACTORY_BINDING_NAME);
if (replicationTopicConnectionFactoryBindingName != null) {
try {
data.put(replicationTopicConnectionFactoryBindingName,
createConnectionFactory(environment));
} catch (URISyntaxException e) {
throw new NamingException(
"Error initialisating TopicConnectionFactory with message "
+ e.getMessage());
}
}
String getQueueConnectionfactoryBindingName = (String) environment
.get(JMSUtil.GET_QUEUE_CONNECTION_FACTORY_BINDING_NAME);


try {
data.put(getQueueConnectionfactoryBindingName,
createConnectionFactory(environment));
} catch (URISyntaxException e) {
throw new NamingException(
"Error initialisating TopicConnectionFactory with message "
+ e.getMessage());
}


String replicationTopicBindingName = (String) environment
.get(JMSUtil.REPLICATION_TOPIC_BINDING_NAME);
String getQueueBindingName = (String) environment
.get(JMSUtil.GET_QUEUE_BINDING_NAME);
if (replicationTopicBindingName != null) {
data.put(replicationTopicBindingName,
createTopic(replicationTopicBindingName));
}
data.put(getQueueBindingName, createQueue(getQueueBindingName));
return createContext(environment, data);
}
}


Note that you need to provide the as for this class as the initialContextFactoryName property for cacheManagerPeerProviderFactory

Maven Dependancies : 

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.6</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-jmsreplication</artifactId>
<version>0.5</version>
</dependency>
<dependency>
<groupId>javax.jms</groupId>
<artifactId>javax.jms-api</artifactId>
<version>2.0-b06</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>4.1.1</version>
</dependency>


NOTE:

Make sure that the firewall is turned off for the caching to work.

Only Serializable are suitable for replication.Some operations, such as remove, work off Element keys rather than the full Element itself. In this case the operation will be replicated provided the key is Serializable, even if the Element is not.


You can refer to my previous blog -
Caching in a Spring Maven Project if you have just started learning caching


You can get the complete source code from here under the SpringHibernateProjectReplicationJms

No comments:

Post a Comment