Distributed Web-Applications using the Keel Meta-Framework
Java applications are often deployed in an environment where future expansion of the user base is anticipated. As a result, some care is taken in choosing an architecture and component model that can allow scalability to higher user loads, often through the use of additional servers, forming an application server cluster, sometimes also called "horizontal scalability".
Recent industry surveys have indicated that Java and J2EE applications suffer from a larger percentage of downtime than is acceptable in an enterprise environment. Eliminating single points of failure can be a step towards improving both uptime percentages and scalability of an application, as we will see.
Recently, tools have become available in open source that make the process of building a complete solution capable of clustering, failover, and grid-style distributed processing practical and easy.
In this article we will examine one such tool, the Keel meta-framework (www.keelframework.org), and how it can be used as an enabler with several other open source technologies to build a completely scalable web application. Keel is a project that aims to not re-invent any wheels, but instead to provide the backbone to connect many different kinds of wheels together. It provides a generic component container, based on the powerful Apache Avalon framework, then layers on dozens of different services. Each service has several different available implementations. The business logic classes (called Models in Keel) see only the interface to the service - never the underlying implementation. This allows any given implementation to be "swapped out" much like a hard drive on a SCSI bus, without changing application logic at all. Database access, for example, can use the popular Hibernate (www.hibernate.org) frame! work to begin with. Then, as needs dictate, perhaps CMP EJBs would be added - the application logic doesn't know or care. In fact, both implementations can even be used at once. The same is true of the user-interface framework chosen: an application's front-end could be written using Struts and its powerful tag library for JSP pages, then the exact same application, without a line of code changed, can be deployed with a front-end of Cocoon (or Velocity, WebWorks2, and many others), when and if the needs change. This unprecedented wealth of choice allows a project to change and evolve in a way that was never possible before, as changing environments and requirements demand. Application logic can be created in Keel using ordinary Java classes, Session EJB's, message-driven EJBs, or web services, as needed. Then the deployment can be mixed and matched for the specific requirements of a given project. Keel can use any one of several different communication layers to enable compo! nents and services to be distributed, including JMS and web services. In this article, we will examine how this ability to "mix and match" can allow you to build highly scalable and failure resistant web applications using 100% open source technologies while remaining "future proof" at the same time.
It's always good to look before you leap, and given the uncertainties in today's business world, it is important to allow for both the best case and worst case. An application can be deployed in a small, single-server environment and yet still be fully operational as a pilot project. If you were tied to a given implementation technology, however, you might find that the future (and so far, theoretical) needs for scalability have you doing more work than you need to in order to get an initial application running. Keel allows you to scale in *both* directions - up and down, as the circumstances require. Let's see how a project might start off small, and grow into a truly scalable solution.
Keel provides a deployment option whereby the application server/container runs in the same virtual machine as the web application itself (although with an isolated classpath by means of a custom classloader), allowing the entire application to be deployed as a single .war file. The hardware needed to support this is trivial: easily available for a few thousand dollars. The entire solution (like all of our examples) can be built using freely available open source packages and tools.
Single Server Deployment
We're using in our diagrams, specific examples of open source packages for each of the tiers - in actual fact, a lot of options exist at each of these levels. For example, we might choose Jetty instead of Tomcat, or JBoss instead of both Tomcat and OpenJMS (as JBoss has JBossMQ, an alternate JMS provider). We might select MySQL for our database, instead of PostgreSQL. We might use Web Services (Apache Axis is fully supported) as our communications layer, as opposed to JMS. In every case, our application logic doesn't change.
In our initial diagram, as see a single server system, running one Java virtual machine. In this virtual machine we're running Tomcat, the Struts framework for our UI interface, and an "embedded" Keel server/container. The Keel server is called by Struts to perform the application logic as needed, and the application logic uses the same container to get access to the services it requires to operate, such as database persistence, scheduling, email, event handling, workflow, reporting, and authentication and authorization, all via a single simple container interface. We write our application's business logic by creating classes that implement the Model interface define by Keel, inheriting as needed in whatever way our application dictates.
On the same machine we might choose to run an instance of the PostgreSQL database engine to store our application's data. We would build and deploy our simple .war file, and our application is up and running, self-contained.
Let's say our little pilot project goes well, and user load starts to go up. Depending on the application, bottlenecks will begin to appear. We will be able to monitor and see where these bottlenecks are happening using the Instrumentation capability of Keel. This is a simple API provided by the underlying Avalon framework that allows any component to "report" on its load, its number of instances, and other vital statistics. A graphical interface allows us to connect to a running instance of the Keel container and monitor and chart these statistics. We can see, for example, that the database connection pool is getting heavily loaded, and that database calls are taking most of the processing time for our application. A simple first step towards scalability is then to break out the database to a separate server, as we see in our second diagram:
First Breakout: Separate Database Processing
This is usually a smart first step, as the system tuning and overall load on a server running a database is quite different than that of a system running a web server (or web application). This breakout allows us to tune the machine running the database for optimal performance, and will likely allow us to increase our overall user load substantially. The change is simple to make: a few configuration settings to let the persistence service know where the database is now located and we're up and running again.