Tomcat Developers: This is a forward of a message that I sent to Bip and Craig a few days ago, regarding distributed session managment (aka Clustering). I haven't gotten any feedback just yet, so I thought I'd throw this out to the whole dev list.
The current implementation is broken. The following message explains why and describes some possible solutions to this problem. This feature (e.g. distributed session management) is an absolute requirement for any deployment that needs to scale beyond a single Tomcat instance, and does not want the overhead of depending on JDBC for storing sessions. I've also attached, at the bottom of this message, Two 'preliminary' RMI interfaces that describe (see scenario 1 below) how I think this session server and it's clients (e.g. tomcat instances) should converse. SessionServer - to be implemented by the remote session manager/server SessionClient - to be implemented by clients (e.g. Tomcat) of the remote session manager/server. I'm interested in making a contribution in this area and am anxious to receive some feedback from the dev-list members on this subject. Regards, Tom Drake Email: [EMAIL PROTECTED] ----- Original Message ----- From: Tom Drake To: [EMAIL PROTECTED] Cc: [EMAIL PROTECTED] Sent: Saturday, November 10, 2001 10:48 PM Subject: Tomcat: Distributed Session Management revisited Bip: I've looked closely at the existing catalina distributed session management code, and determined that the following problems exist. Since, I'm new to the code, it's highly likely that I've missed something. Please correct any errors in my analysis, and provide any input / feedback. I'm interested in contributing to this and would greatly appreciate any input you can provide. Problems with current solution: - Session updates are not communicated to the other nodes in the cluster - If a new Tomcat instance is added to the cluster, it has no means of discovering any pre-existing sessions (being managed by the other nodes), (unless JDBCStore is used to persist sessions). - If a Tomcat instance is brought down and brought back up 'later', it will read stale session data from it's file system store - The fact that DistributedManager derives from PersistentManager appears to create these problems: If a Tomcat instance is brought down, it will save all of it's active sessions via a 'Store'. If the instance is brought up again later, it will, read stale session data from an out-of-date store. If brought down again, it could (potentially) write these out-of-date entries over the top of the store. Creates extra overhead with no benefit. Session persistance is not the goal of 'clustering' in the first place. The goal of clustering is to provide (part of) a seamless load-balancing and/or fail-over solution. The idea being that I can have 'n' Tomcats running, to spread the client load. I should be able to bring up new instances and bring down running instances on the fly, without 'losing' any client sessions. ---------------------------------------------------------------------------- ----------------------------------- I've come up with the following ideas for solution to these problems: 1) Separate Session manager 2) Cooperative Session management (extending the existing multicast solution). Here are some more detailed descriptions of these solutions: 1) Implement a separate, Session manager, whose job is to: - provide a means for new Tomcat instances to register themselves. Upon registration, this server may send a list of 'active' session ids back to the new Tomcat instance. - keep an up-to-date copy of all sessions in a given 'cluster'. - destroy sessions that are no longer valid (due to logout, or timeout). - communicate new, updated, and obsoleted Session ID's to all 'registered' cluster members, so that they may maintain their own hash of 'known' session ids. Tomcat instances would need to do the following: - on startup, register itself with the remote session manager. It may receive a list of active session id's from the remote session manager as a response, so it must store these id's in it's own internal hash (of known session id's). - to create a new session, it can simply do so in it's own memory space, (assuming that the MessageDigest is guaranteed to produce a session identifier that's unique across the 'cluster'). Upon completion of the client HTTP/AJP/Warp request, serialize the new session object and send it off to the remote manager. - to retrieve a session, it would need to check it's own internal Session 'cache' first, if not found, query the remote session manager. The remote session manager would send the requested Session object back to the caller, or, if not found, return an error. - to update a session*, it would need to send a serialized copy of the Session to the remote session manager. The manager would store the updated session data, then notify all the 'other' registered Tomcat instances that the given Session has changed (it only needs to send the Session ID). * Update notification depends on implementation of HttpSessionAttributeListener and HttpSessionBindingEvent. - to delete / invalidate a Session, it must send a request to the remote Session Manager. The remote manager, would then need to destroy the Session, and send a message to all the 'other' Tomcat instances, containing the Session ID that is no longer valid (so that the other Tomcat's can destroy any related data). NB 1: Tomcat can 'cache' Session objects, eliminating the need to retrieve them from the remote session manager. If the cached session is updated by a different tomcat instance, it will communicate the updated session to the remote session manager which will in turn notify all other Tomcat instances of the update. The 'other' Tomcats can simply remove the Session from their internal cache when an update notification is recieved. If an instance needs to access a Session that's not in it's cache, it would have to retrieve the session from the remote session manager at that time. NB 2: This Session manager could be made to run as a stand-alone process or as part of one of the Tomcat instances. The key is that only one of them can be 'managing' the sessions for a given 'cluster'. If this code is present inside Tomcat, then the Tomcats in the cluster could 'negotiate' to decide who is to be manager. If the manager is brought down, it could notify the other cluster members of it's impending demise so that they can elect a new manager, which will then get a copy of all 'active' sessions and then take over the manager functionality. Personally, I like the idea of a separate standalone Session Manager because of it's simplicity. Furthermore, I'm in favor of an RMI based solution; partly because of it's simplicity, but also because of it's power. RMI callbacks, provide a very natural means of implementing this type of collaborative client/server application. The potential drawback to this approach is that the single remote server could wind up being a bottleneck. However, it would generate much less network traffic than the current solution. And, in the type of environment the 'cluster' is really targeted to run in (e.g. multiple boxes), network traffic is likely to be a major cause of latency (as opposed to being CPU bound in the Session Manager). 2) Cooperative Session management (extending the existing multicast solution). The existing solution would need to modified to: - broadcast updated/deleted Session's to the other cluster members. - provide a means for bringing a new Tomcat instance on-line (such that it can obtain all the stored Session's. The big drawback to the current approach, I feel, is that it generates a lot of UDP traffic. Complete Sessions are broadcast to all members of the cluster. This would need to include all updated Session objects as well. Lot's of traffic, that may not ever be needed. Adding all updated sessions to the UDP traffic increases the likelyhood that UDP packets will get lost. Which, in-turn, reduces confidence that all Tomcats will have the 'same' Session data. Regards, Tom Drake President, software/etc inc. Cell: 408-505-6864 Email: [EMAIL PROTECTED] ---------------------------------------------------------------- org.apache.sessionMgr.SessionServer.java: ---------------------------------------------------------------- package org.apache.sessionMgr; import java.rmi.*; import javax.servlet.http.HttpSession; /** * The interface implemented by a Distributed Session Management server. * Clients (e.g. Tomcat instances) may register themselves with such * a server. Having done so, they must send any new or updated Sessions * to the server. Clients may retrieve Session objects from the server * (by sessionId). Clients must also pay attention to <tt>invalidateSession</tt> * messages received from the server and update their internal Session caches * accordingly. * * @author Tom Drake * @version 1.0 */ public interface SessionServer extends java.rmi.Remote { /** * Register the given client (e.g. Tomcat) with a SessionServer instance. * After the client has registered itself with a SessionServer, * the SessionServer will start sending the client * <tt>invalidateSession(String sessionId)</tt> messages, as sessions * are updated or deleted. */ public void register(SessionClient client) throws RemoteException; /** * Disassociate the client (e.g. Tomcat instance) from the Distributed * Session Management Server. The client will no receive messages from * the server. And the client may no longer send messages to the server * unless, it re-registers itself with the server. */ public void deRegister(SessionClient client) throws RemoteException; /** * Store the given sessionId / session association. No other clients * are notified of this, however, they may retrieve this session via * the <tt>getSession</tt> method. */ public void addSession(String sessionId, HttpSession session) throws RemoteException; /** * Retrieve the Session that corresponds with the given sessionId from * the Session Server. * * @return a valid session object or null, if the sessionId was not defined * or the session was not valid. */ public HttpSession getSession(String sessionId) throws RemoteException; /** * Update the last access time of the corresponding Session. This method * is called by clients, when a Session is accessed, but not otherwise updated. * Other clients are <b>not</b> notified in this case! */ public void updateSessionAccessTime(String sessionId) throws RemoteException; /** * Replace the session associated with the given sessionId with the given * session object, and notify other clients that their copies, if any, are * now invalid. */ public void updateSession(String sessionId, HttpSession session) throws RemoteException; /** * Inform the SessionServer to destroy the corresponding Session, and notify * other clients of it's destruction. */ public void destroySession(String sessionId) throws RemoteException; } ---------------------------------------------------------------- org.apache.sessionMgr.SessionClient.java: ---------------------------------------------------------------- package org.apache.sessionMgr; import java.rmi.*; /** * The SessionClient interface is implemented by clients (e.g. Tomcat instances) * of the SessionServer. These methods are invoked on the client by the server * to notify the client that either the given session was invalidated, or that the server * will be going down shortly. * * @author Tom Drake * @version 1.0 */ public interface SessionClient extends Remote { /** * Notify this client (e.g. Tomcat) that the the Session data that corresponds * to the given Session Id is no longer valid. This could mean that that the * Session has been updated, or that it has exceeded its maximum allowable age. * In any case, the client should respond to this message by removing any * corresponding Session objects from it's own internal cache. */ public void invalidateSession(String sessionId) throws RemoteException; /** * Notify this client that the SessionServer will be shutting down. * Any further attempts to contact this SessionServer will not be successful * (unless and until it is restarted). */ public void serverShutdownNotification() throws RemoteException; } -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>