Clustering wicket apps
Wednesday, August 25th, 2010I had a hard time figuring out what was required to cluster our Wicket apps. None of the Wicket books discuss it in any detail – presumably because its not considered a Wicket-specific topic. I bothered the guys in the ##Wicket IRC Channel for a while and Victor Igumnov helped me figure it out and provided me with all the Wicket and Jetty pieces I needed.
Before I start, I should explain that we’re running our Wicket apps with embedded Jetty (”don’t run your apps in Jetty, run Jetty in your apps”). We really like it because it essentially turns web apps into plain old java apps which you can develop, debug and deploy without all that tomcat clutter. So we wanted clustering solution which worked with embedded Jetty, preferably in an uncomplicated fashion.
My first idea was to use Hazelcast – we’re already using it for distributed locking and queuing (its a paragon of simplicity, exemplifying the “it just works” principle). However, I was unable to find exactly what I needed based on Hazelcast, so I followed Victor’s advice and used memcached instead.
You need three things to cluster a Wicket app:
(1) A load balancer – we use HAProxy (as does everybody else).
(2) A distributed map to store the application sessions – we’re using memcached here.
(3) A http session implementation which stores the session in the distributed map instead of locally.
Since the main point of clustering is fault-tolerance, you need to make sure that you avoid single points of failure in the system. Unfortunately, this means that you have to cluster not only your application, but also provide failover for HAProxy and for memcached. I won’t cover that here, but you just need to be aware that when you install those components, you’ll need to install at least two of each with failover. This is simple for memcached – for HAProxy its a bit harder, but at least you can reuse this infrastructure for multiple apps.
HAProxy is the load balancer which will take care of presenting one address for your app to clients, even though your app is running as a cluster of servers. I’m not going to explain how to configure HAProxy here except to say that you’ll need session affinity (aka sticky-sessions) turned on – this means that users stay on a server for a whole session unless the server goes down. Wicket prefers sticky-session load balancing, and its more efficient, so its best to use it.
Installing memcached is no problem. Just follow the instructions. The default configuration only allows connections from localhost, so you’ll need to allow access from the subnet containing the app servers, but otherwise there’s nothing to configure.
Then you need to have your http session persist itself to memcached instead of to the local machine. Frsi download Victor’s tcache distributed cache client library from http://github.com/victori/tcache. Then get Victor’s jetty-session-cache from http://github.com/victori/jetty-session-cache. Add the built jars to your project (or maven repository). Victor describes how to configure jetty via XML to use the MemcachedSessionManager. Since we confgure the embedded Jetty programatically, we needed the following line in our Jetty startup code:
context.setSessionHandler( new SessionHandler( new MemcachedSessionManager(memcachedServers, "my_app_session_store")));
where memcachedServers is a String[] containing a list of your memcached servers, like “server1:11211″, “server2:11211″.
You then need to tell Wicket to store its pages in the http session instead of the default disk-based page store. However, this apparently doesn’t work very efficiently, so the recommended approach is to use a custom page store which stores its pages directly to memcached instead of using the http session.
Victor has a solution here too, go to http://letsgetdugg.com/2010/02/07/clustering-wicket-for-fun-and-profit/ and follow the instructions to override your applications newSessionStore() and provide a memcached page store.
Now your Wicket app is clustered. You can try it out by accessing it via HAProxy, logging on to your app, killing the server with your app’s session and then continue using the app (for test purposes, I display the server running the app session, so I can see when it fails over).
I’d still like to replace memcached with Hazelcast (fully java-based embedded clustering in your app with no single point of failure)- maybe Victor will embrace Hazelcast and add it to the list of caches supported by tcache.
=================================
26.08.2010
I got a mail from Talip Ozturk of Hazelcast to say that Hazelcast supports the memcached protocol, so it should be possible to use a memcached session and page store implementation against a Hazelcast server. This would be fantastic, since it would eliminate the need to have multiple memcached servers – one less piece of infrastructure to worry about. I’ll try to get this working and let you know how I get on.