Home > 16ms > Use MemCacheStore in Tomcat…

Use MemCacheStore in Tomcat…

October 10th, 2008 AVC

About:

memcache-client is a Java interface to memcached; memcached is a distributed caching system. Originally, memcached was developed for LiveJournal.com, which was one of the earliest popular blogging communities. It is reported that the newly developed memcached was able to decrease LiveJournal’s database load to nearly nothing using only existing hardware. For a site that at the time handled over 20 million page views a day and had over a million different users, that’s very significant. A number of other popular sites use memcached, including Slashdot and Wikipedia. This section describes how to setup  MemCache with Sessionstore module from Tomcat 5.x(it works in jboss also). A simple class that implements Store interface and use a memcacheclient to store sessions.

Hier is a simple MemCacheStore class that implemts all functionality:

import com.danga.MemCached.MemCachedClient;
import com.danga.MemCached.SockIOPool;

/**
 * Implementation of a Tomcat Session {@link Store} that's backed by
 * memcached.
 *
 * @author avc
 */
public class MemCacheStore extends StoreBase implements Store {

	/**
	 * The descriptive information about this implementation.
	 */
	protected static String info = "MemCacheStore/1.0";

	/**
	 * The thread safe and thread local memcacheclient instance.
	 */
	private static final ThreadLocal<MemCachedClient> memclient = new ThreadLocal<MemCachedClient>();

	/**
	 * The server list for memcache connections.
	 */
	private List<String> servers = new ArrayList<String>();

	/**
	 * all keys for current request session.
	 */
	private List<String> keys = Collections
			.synchronizedList(new ArrayList<String>());

	/**
	 * Return the info for this Store.
	 */
	public String getInfo() {
		return (info);
	}

	/**
	 * Clear all sessions from the cache.
	 */
	public void clear() throws IOException {
		getMemcacheClient().flushAll();
		keys.clear();
	}

	/**
	 * Return local keyList size.
	 */
	public int getSize() throws IOException {
		return getKeyList().size();
	}

	/**
	 * Return all keys
	 */
	public String[] keys() throws IOException {
		return getKeyList().toArray(new String[] {});
	}

	/**
	 * Load the Session from the cache with given sessionId.
	 *
	 */
	public Session load(String sessionId) throws ClassNotFoundException,
			IOException {

		try {

			byte[] bytes = (byte[]) getMemcacheClient().get(sessionId);
			if (bytes == null)
				return null;
			ObjectInputStream ois = bytesToObjectStream(bytes);

			StandardSession session = (StandardSession) manager
					.createEmptySession();
			session.setManager(manager);
			session.readObjectData(ois);
			if (session.isValid() && !keys.contains(sessionId)) {
				keys.add(sessionId);
			}
			return session;

		} catch (Exception e) {
			return (null);
		}
	}

	/**
	 * transform a vaild Session from objectinputstream.
	 * Check which classLoader is responsible for the current instance.
	 *
	 * @param bytes
	 * @return ObjectInputStream with the Session object.
	 * @throws IOException
	 */
	private ObjectInputStream bytesToObjectStream(byte[] bytes)
			throws IOException {
		ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
		ObjectInputStream ois = null;
		Loader loader = null;
		ClassLoader classLoader = null;
		Container container = manager.getContainer();
		if (container != null)
			loader = container.getLoader();
		if (loader != null)
			classLoader = loader.getClassLoader();
		if (classLoader != null)
			ois = new CustomObjectInputStream(bais, classLoader);
		else
			ois = new ObjectInputStream(bais);
		return ois;
	}

	/**
	 * remove the session with given sessionId
	 */
	public void remove(String sessionId) throws IOException {
		getMemcacheClient().delete(sessionId);
		List<String> keyList = getKeyList();
		keyList.remove(sessionId);
	}

	/**
	 * Store a objectstream from the session into the cache.
	 */
	public void save(Session session) throws IOException {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(baos);
		StandardSession standard = (StandardSession) session;
		standard.writeObjectData(oos);
		getMemcacheClient().add(session.getId(), baos.toByteArray());
		Object ob = getMemcacheClient().get(session.getId());
		List<String> keyList = getKeyList();
		keyList.add(session.getId());
	}

	/**
	 *
	 * @return
	 */
	private List<String> getKeyList() {
		return keys;
	}

	/**
	 * Simple instanc of the Memcache client and SockIOPool.
	 * @return memchacheclient
	 */
	private MemCachedClient getMemcacheClient() {
		if (memclient == null) {

			Integer[] weights = { 1 };
			// grab an instance of our connection pool
			SockIOPool pool = SockIOPool.getInstance();
			if (!pool.isInitialized()) {
				String[] serverlist = servers.toArray(new String[] {});
				// set the servers and the weights
				pool.setServers(serverlist);
				pool.setWeights(weights);

				// set some basic pool settings
				// 5 initial, 5 min, and 250 max conns
				// and set the max idle time for a conn
				// to 6 hours
				pool.setInitConn(5);
				pool.setMinConn(5);
				pool.setMaxConn(250);
				pool.setMaxIdle(1000 * 60 * 60 * 6);

				// set the sleep for the maint thread
				// it will wake up every x seconds and
				// maintain the pool size
				pool.setMaintSleep(30);

				// set some TCP settings
				// disable nagle
				// set the read timeout to 3 secs
				// and don't set a connect timeout
				pool.setNagle(false);
				pool.setSocketTO(3000);
				pool.setSocketConnectTO(0);

				// initialize the connection pool
				pool.initialize();
			}

			// lets set some compression on for the client
			// compress anything larger than 64k

			memclient.get().setCompressEnable(true);
			memclient.get().setCompressThreshold(64 * 1024);
		}
		return memclient.get();
	}

	public List<String> getServers() {
		return servers;
	}

	public void setServers(String serverList) {
		StringTokenizer st = new StringTokenizer(serverList, ", ");
		servers.clear();
		while (st.hasMoreTokens()) {
			servers.add(st.nextToken());
		}
	}

}

The configuration for the context:

<Context path="/web" docBase="./deploy/web-0.0.1.war">
	<Manager className="org.apache.catalina.session.PersistentManager"
		distributable="true">
		<Store className="com.avc.hq.MemcachedStore"
			servers="192.168.17.90:11211" />
	</Manager>

</Context>

Thats all. The quite simple way :)

-avc (arkadiusz victor czarnik)

Categories: 16ms Tags:
  1. No comments yet.
Comments are closed.