Category: General

  • Asynchronous Rest with Jetty-9

    This blog is an update for jetty-9 of one published for Jetty 7 in 2008 as an example web application  that uses Jetty asynchronous HTTP client and the asynchronoous servlets 3.0 API, to call an eBay restful web service. The technique combines the Jetty asynchronous HTTP client with the Jetty servers ability to suspend servlet processing, so that threads are not held while waiting for rest responses. Thus threads can handle many more requests and web applications using this technique should obtain at least ten fold increases in performance.

    Screenshot from 2013-04-19 09:15:19

    The screen shot above shows four iframes calling either a synchronous or the asynchronous demonstration servlet, with the following results:

    Synchronous Call, Single Keyword
    A request to lookup ebay auctions with the keyword “kayak” is handled by the synchronous implementation. The call takes 261ms and the servlet thread is blocked for the entire time. A server with a 100 threads in a pool would be able to handle 383 requests per second.
    Asynchronous Call, Single Keyword
    A request to lookup ebay auctions with the keyword “kayak” is handled by the asynchronous implementation. The call takes 254ms, but the servlet request is suspended so the request thread is held for only 5ms. A server with a 100 threads in a pool would be able to handle 20,000 requests per second (if not constrained by other limitations)
    Synchronous Call, Three Keywords
    A request to lookup ebay auctions with keywords “mouse”, “beer” and “gnome” is handled by the synchronous implementation. Three calls are made to ebay in series, each taking approx 306ms, with a total time of 917ms and the servlet thread is blocked for the entire time. A server with a 100 threads in a pool would be able to handle only 109 requests per second!
    Asynchronous Call, Three Keywords
    A request to lookup ebay auctions with keywords “mouse”, “beer” and “gnome” is handled by the asynchronous implementation. The three calls can be made to ebay in parallel, each taking approx 300ms, with a total time of 453ms and the servlet request is suspended, so the request thread is held for only 7ms. A server with a 100 threads in a pool would be able to handle 14,000 requests per second (if not constrained by other limitations).

    It can be seen by these results that asynchronous handling of restful requests can dramatically improve both the page load time and the capacity by avoiding thread starvation.
    The code for the example asynchronous servlet is available from jetty-9 examples and works as follows:

    1. The servlet is passed the request, which is detected as the first dispatch, so the request is suspended and a list to accumulate results is added as a request attribute:
      // If no results, this must be the first dispatch, so send the REST request(s)
      if (results==null) {
          final Queue> resultsQueue = new ConcurrentLinkedQueue<>();
          request.setAttribute(RESULTS_ATTR, results=resultsQueue);
          final AsyncContext async = request.startAsync();
          async.setTimeout(30000);
          ...
    2. After suspending, the servlet creates and sends an asynchronous HTTP exchange for each keyword:
      for (final String item:keywords) {
        _client.newRequest(restURL(item)).method(HttpMethod.GET).send(
          new AsyncRestRequest() {
            @Override
            void onAuctionFound(Map<String,String> auction) {
              resultsQueue.add(auction);
            }
            @Override
            void onComplete() {
              if (outstanding.decrementAndGet()<=0)
                async.dispatch();
            }
          });
      }
    3. All the rest requests are handled in parallel by the eBay servers and when each of them completes, the call back on the exchange object is called. The code (shown above) extracts auction information in the base class from the JSON response and adds it to the results list in the onAuctionFound method.  In the onComplete method, the count of expected responses is then decremented and when it reaches 0, the suspended request is resumed by a call to dispatch.
    4. After being resumed (dispatched), the request is re-dispatched to the servlet. This time the request is not initial and has results, so the results are retrieved from the request attribute and normal servlet style code is used to generate a response:
      List> results = (List>) request.getAttribute(CLIENT_ATTR);
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("");
      for (Map m : results){
        out.print("");
      ...
      out.println("");
      
    5. The example does lack some error and timeout handling.

    This example shows how the Jetty asynchronous client can easily be combined with the asynchronous servlets of Jetty-9 (or the Continuations of Jetty-7) to produce very scalable web applications.

  • Jetty, SPDY, PHP and WordPress

    Having discussed the business case for Jetty 9 and SPDY, this blog presents a simple tutorial for runing PHP web applications like WordPress on Jetty with SPDY.

    Get Jetty

    First you’ll need a distribution of Jetty, which you can download, unpack and run with the following (I use wget to download from the command line, or you can just download with a browser from here):

    wget -U none http://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/9.0.2.v20130417/jetty-distribution-9.0.2.v20130417.zip
    unzip jetty-distribution-9.0.2.v20130417.zip
    cd jetty-distribution-9.0.2.v20130417
    java -jar start.jar

    You can point your browser at http://localhost:8080/ to verify that Jetty is running (Just ctrl-C jetty when you want to stop it).

    Configure SPDY

    Next you’ll need to download NPN (for SPDY protocol negotiation) from  here and save in the lib directory:

    wget -O lib/npn-boot-1.1.5.v20130313.jar
    -U none
    http://repo1.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.5.v20130313/npn-boot-1.1.5.v20130313.jar

    To configure SPDY create the file start.d/spdy.ini with the following content:

    --exec
    -Xbootclasspath/p:lib/npn-boot-1.1.5.v20130313.jar
    OPTIONS=spdy
    jetty.spdy.port=8443
    jetty.secure.port=8443
    etc/jetty-ssl.xml
    etc/jetty-spdy.xml

    Restart jetty (java -jar start.jar) and you can now verify that you are running SPDY by pointing a recent Chrome or Firefox browser at https://localhost:8443/.  You may have to accept the security exception for the self signed certificate that is bundled with the jetty distro.    FF indicates that they are using SPDY with a little green lightening symbol in the address bar.

    Enable PHP

    There are several ways to PHP enable Jetty, but the one I’m using for this demonstration is php-java-bridge, which you can download in a complete WAR file from here.   To install and test in a context ready for wordpress:

    mkdir webapps/wordpress
    cd webapps/wordpress
    unzip /tmp/JavaBridgeTemplate621.war
    cd ../..
    java -jar start.jar

    You can then test that PHP is working by browsing to http://localhost:8080/wordpress/test.php and you can test that PHP is working under SPDY by browsing https://localhost:8443/wordpress/test.php.

    Install WordPress

    You now have a Jetty SPDY server serving PHP, so let’s install WordPress as an example of PHP webapplication. You can download WordPress from here and install it as follows:

    cd webapps
    rm index.php
    unzip /tmp/wordpress-3.5.1.zip
    cd ..
    java -jar start.jar

    You can browse to WordPress at http://localhost:8080/wordpress/ where you should see a screen inviting you to “Create a Configuration File”.   You’ll need a MYSQL database instance to proceed and 2 screens later you are running WordPress over HTTP.

    You’ll note that if you try immediately to access wordpress with SPDY, you get badly redirected back to the 8080 port with the https protocol!  This is just WordPress being a bit dumb when it comes to SSL and I suggest you google WordPress SSL and have a read of some of the configuration and plugin options available. Take special note of how you can easily lock yourself out of the admin pages!  Which you will do if you simply update the wordpress URL under general settings to https://localhost:8443/wordpress.   You’ll also need to read up on running WordPress on non standard ports, but this is not a blog about wordpress, so I wont go into the options here, other than to say that difficulties with the next few steps are the SPDY as they are for SSL (and that the wordpress guys should really read up on using the host header)!  If you want a quick demonstration, just change the home URI in general settings and you’ll be able to see the main site under SPDY at https://localhost:8443/wordpress/,  but will be locked out of the admin pages.

    Conclusion

    That’s it! A few simple steps are all you need to run a complex PHP site under Jetty with SPDY!        Of course if you want help with setting this up and tuning it, then please consider the Intalio’s migration, performance and/or production support services.

  • The Need For SPDY and why upgrade to Jetty 9?

    So you are not Google!  Your website is only taking a few 10’s or maybe 100’s of requests a second and your current server is handling it without a blip.  So you think you don’t need a faster server and it’s only something you need to consider when you have 10,000 or more simultaneous users!  WRONG!   All websites need to be concerned about speed in one form or another and this blog explains why and how Jetty with SPDY can help improve your  business no matter how large or small you are!

    TagMan conversion rate study for Glasses Direct

    Speed is Relative

    What does it mean to say your web site is fast? There are many different ways of measuring speed and while some websites are concerned with all of them, many if not most need only be concerned with some aspects of speed.

    Requests per Second

    The first measure of speed that many web developers think about is throughput, or how many requests per second can your web site handle?  For large web business with millions of users this is indeed a very important measure, but for many/most websites, requests per second is just not an issue.  Most servers will be able to handle thousands of requests per second, which represents 10’s of thousands of simultaneous users and far exceeds the client base and/or database transaction capacity of small to medium enterprises.     Thus having a server and/or protocol that will allow even greater requests per second is just not a significant concern for most  [ But if it is, then Jetty is still the server for you, but just not for the reasons this blog explains] .

    Request Latency

    Another speed measure is request latency, which is the time it takes a server to parse a request and generate a response.   This can range from a few milliseconds to many seconds depending on the type of the request and complexity of the application.  It can be a very important measure for some websites, specially web service or REST style servers that  handling a transaction per message.   But as an individual measure it is dominated by network latency (10-500 ms) and application processing (1-30000ms), then the time the server spends (1-5ms) handling a request/response is typically not an important driver when selecting a server.

    Page Load Speed

    The speed measure that is most apparent to users of your website is how long a page takes to load.  For a typical website, this involves fetching on average 85 resources (HTML, images, CSS, javascript, etc.) in many HTTP requests over multiple connections. Study summaries below, show that page load time is a metric that can greatly affect the effectiveness of a web site. Page load times have typically been primarily influenced by page design and the server had little ability to speed up page loads.  But with the SPDY protocol, there are now ways to greatly improve page load time, which we will see is a significant business advantage regardless of the size of your website and client base.

    The Business case for Page Load Speed

    The Book Of Speed presents the business benefits of reduced page load speed as determined by many studies summaries below:

    • A study at Microsofts live.com found that slowing page loads by 500ms reduced revenue per user by 1.2%. This increased to 2.8% at 1000ms delay and 4.3% at 2000ms, mostly because of a reduced click through rate.
    • Google found that the negative effect on business of slow pages got worse the longer users were exposed to a slow site.
    • Yahoo found that a slowdown of 400ms was enough to drop the completed page loads by between 5% and 9%. So users were clicking away from the page rather than waiting for it to load.
    • AOL’s studied several of its web properties and found a strong correlation between page load time and the number of page view per user visit. Faster sites retained their visitors for more pages.
    • When Mozilla improved the speed of their Internet Explorer landing page by 2.2s, they increase their rate of conversions by 15.4%
    • Shopzilla reduce their page loads from 6s to 1.2s and increased their sales conversion by 7-12% and also reduced their operation costs due to reduced infrastructure needs.

    These studies clearly show that page load speed should be a significant consideration for all web based businesses and they are backed up by many more such as:

    If that was not enough, Google have also confirmed that they use page load speed as one of the key factors when ranking search results to display.  Thus a slow page can do double damage of reducing the users that visit and reducing the conversion rate of those that do.

    Hopefully you are getting the message now, that page load speed is very important and the sooner you do something about it, the better it will be.   So what can you do about it?

    Web Optimization

    The traditional approach to improving has been to look at Web Performance Optimization, to improve the structure and technical implementation of your web pages using techniques including:

    • Cache Control
    • GZip components
    • Component ordering
    • Combine multiple CSS and javascript components
    • Minify CSS and javascript
    • Inline images, CSS Sprites and image maps
    • Content Delivery Networks
    • Reduce DOM elements in documents
    • Split content over domains
    • Reduce cookies

    These are all great things to do and many will provide significant speed ups.  However, most of these techniques are very intrusive and can be at odds with good software engineer; development speed and separation of concerns between designers and developers.    It can be a considerable disruption to a development effort to put in aggressive optimization goals along side functionality, design and time to market concerns.

    SPDY for Page Load Speed

    The SPDY protocol is being developed primarily by Google to replace HTTP with a particular focus on improving page load latency.  SPDY is already deployed on over 50% of browsers and is the basis of the first draft of the HTTP/2.0 specification being developed by the IETF.    Jetty was the first java server to implement SPDY and Jetty-9 has been re-architected specifically to better handle the multi protocol, TLS, push and multiplexing features of SPDY.

    Most importantly, because SPDY is an improvement in the network transport layer, it can greatly improve page load times without making any changes at all to a web application.  It is entirely transparent to the web developers and does not intrude into the design or development!

    SPDY Multiplexing

    One of the biggest contributors to web page load latency is the inability of the HTTP to efficiently use connection.  A HTTP connection can have only 1 outstanding request and browsers have a low limit (typically 6) to the number of connections that can be used in parallel.  This means that if your page requires 85 resources to render (which is the average), it can only fetch them 6 at a time and it will take at least 14 round trips over the network before the page is rendered.  With network round trip time often hundreds of ms, this can add seconds to page load times!

    SPDY resolves this issue by supporting multiplexed requests over a single connection with no limit on the number of parallel requests.  Thus if a page needs 85 resources to load, SPDY allows all 85 to be requested in parallel and thus only a single round trip latency imposed and content can be delivered at the network capacity.

    More over, because the single connection is used and reused, then the TCP/IP slow start window is rapidly expanded and the effective network capacity available to the browser is thus increased.

    SPDY Push

    Multiplexing is key to reducing round trips, but unfortunately it cannot remove them all because browser has to receive and parse the HTML before it knows the CSS resources to fetch; and those CSS resources have to be fetched and parsed before any image links in them are known and fetch.  Thus even with multiplexing, a page might take 2 or 3 network round trips just to identify all the resources associated with a page.

    But SPDY has another trick up it’s sleeve.  It allows a server to push resources to a browser in anticipation of requests that might come.  Jetty was the first server to implement this mechanism and uses relationships learnt from previous requests to create a map of associated resources so that when a page is requested, all it’s associated resources can immediately be pushed and no additional network round trips are incurred.

    SPDY Demo

    The following demonstration was given and Java One 2012 and clearly shows the SPDY page load latency improvements for a simple page with 25 images blocks over a simulated 200ms network:

    How do I get SPDY?

    To get the business benefits of speed for your web application, you simply need to deploy it on Jetty and enable SPDY with an SSL Certificate for your site.  Standard java web applications can be deployed without modification on Jetty and there are simple solutions to run sites built with PHP, Ruby, GWT etc on Jetty as well.

    If you want assistance setting up Jetty and SPDY, why not look at the affordable Jetty Migration Services available from Intalio.com and get the Jetty experts help power your web site.

  • Jetty 9.1 in Techempower benchmarks

    Jetty 9.1.0 has entered round 8 of the Techempower’s Web Framework Benchmarks. These benchmarks are a comparison of over 80 framework & server stacks in a variety of load tests. I’m the first one to complain about unrealistic benchmarks when Jetty does not do well, so before crowing about our good results I should firstly say that these benchmarks are primarily focused at frameworks and are unrealistic benchmarks for server performance as they suffer from many of the failings that I have highlighted previously (see Truth in Benchmarking and Lies, Damned Lies and Benchmarks).

    But I don’t want to bury the lead any more than I have already done, so I’ll firstly tell you how Jetty did before going into detail about what we did and what’s wrong with the benchmarks.

    What did Jetty do?

    Jetty has initially entered the JSON and Plaintext benchmarks:

    • Both tests are simple requests and trivial requests with just the string “Hello World” encode either as JSON or plain text.
    • The JSON test has a maximum concurrency of 256 connections with zero delay turn around between a response and the next request.
    • The plaintext test has a maximum concurrency of 16,384 and uses pipelining to run these connections at what can only be described as a pathological work load!

    How did Jetty go?

    At first glance at the results, Jetty look to have done reasonably well, but on deeper analysis I think we did awesomely well and an argument can be made that Jetty is the only server tested that has demonstrated truly scalable results.

    JSON Results

    json-tp

    Jetty came 8th from 107 and achieved 93% (199,960 req/s) of the first place throughput.   A good result for Jetty, but not great. . . . until you plot out the results vs concurrency:

    json-trend

    All the servers with high throughputs have essentially maxed out at between 32 and 64 connections and the top  servers are actually decreasing their throughput as concurrency scales from 128 to 256 connections.

    Of the top throughput servlets, it is only Jetty that displays near linear throughput growth vs concurrency and if this test had been extended to 512 connections (or beyond) I think you would see Jetty coming out easily on top.  Jetty is investing a little more per connection, so that it can handle a lot more connections.

    Plaintext Results

    plain-tp

    First glance again is not so great and we look like we are best of the rest with only 68.4% of the seemingly awesome 600,000+ requests per second achieved by the top 4.    But throughput is not the only important metric in a benchmark and things look entirely different if you look at the latency results:

    plain-lat

    This shows that under this pathological load test, Jetty is the only server to send responses with an acceptable latency during the onslaught.  Jetty’s 353.5ms is a workable latency to receive a response, while the next best of 693ms is starting to get long enough for users to register frustration.  All the top throughput servers have average latencies  of 7s or more!, which is give up and go make a pot of coffee time for most users, specially as your average web pages needs >10 requests to display!

    Note also that these test runs were only over 15s, so servers with 7s average latency were effectively not serving any requests until the onslaught was over and then just sent all the responses in one great big batch.  Jetty is the only server to actually make a reasonable attempt at sending responses during the period that a pathological request load was being received.

    If your real world load is anything vaguely like this test, then Jetty is the only server represented in the test that can handle it!

    What did Jetty do?

    The jetty entry into these benchmarks has done nothing special.  It is out of the box configuration with trivial implementations based on the standard servlet API.  More efficient internal Jetty API  have not been used and there has been no fine tuning of the configuration for these tests.  The full source is available, but is presented in summary below:

    public class JsonServlet extends GenericServlet
    {
      private JSON json = new JSON();
      public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
      {
        HttpServletResponse response= (HttpServletResponse)res;
        response.setContentType("application/json");
        Map<String,String> map =
          Collections.singletonMap("message","Hello, World!");
        json.append(response.getWriter(),map);
      }
    }

    The JsonServlet uses the Jetty JSON mapper to convert the trivial instantiated map required of the tests.  Many of the other frameworks tested use Jackson which is now marginally faster than Jetty’s JSON, but we wanted to have our first round with entirely Jetty code.

    public class PlaintextServlet extends GenericServlet
    {
      byte[] helloWorld = "Hello, World!".getBytes(StandardCharsets.ISO_8859_1);
      public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
      {
        HttpServletResponse response= (HttpServletResponse)res;
        response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString());
        response.getOutputStream().write(helloWorld);
      }
    }

    The PlaintextServlet makes a concession to performance by pre converting the string array to bytes, which is then simply written out the output stream for each response.

    public final class HelloWebServer
    {
      public static void main(String[] args) throws Exception
      {
        Server server = new Server(8080);
        ServerConnector connector = server.getBean(ServerConnector.class);
        HttpConfiguration config = connector.getBean(HttpConnectionFactory.class).getHttpConfiguration();
        config.setSendDateHeader(true);
        config.setSendServerVersion(true);
        ServletContextHandler context =
          new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS);
        context.setContextPath("/");
        server.setHandler(context);
        context.addServlet(org.eclipse.jetty.servlet.DefaultServlet.class,"/");
        context.addServlet(JsonServlet.class,"/json");
        context.addServlet(PlaintextServlet.class,"/plaintext");
        server.start();
        server.join();
      }
    }

    The servlets are run by an embedded server.  The only configuration done to the server is to enable the headers required by the test and all other settings are the out-of-the-box defaults.

    What’s wrong with the Techempower Benchmarks?

    While Jetty has been kick-arse in these benchmarks, let’s not get carried away with ourselves because the tests are far from perfect, specially for  these two tests which are not testing framework performance (the primary goal of the techempower benchmarks) :

    • Both have simple requests that have no information in them that needs to be parsed other than a simple URL.  Realistic web loads often have session and security cookies as well as request parameters that need to be decoded.
    • Both have trivial responses that are just the string “Hello World” with minimal encoding. Realistic web load would have larger more complex responses.
    • The JSON test has a maximum concurrency of 256 connections with zero delay turn around between a response and the next request.  Realistic scalable web frameworks must deal with many more mostly idle connections.
    • The plaintext test has a maximum concurrency of 16,384 (which is a more realistic challenge), but uses pipelining to run these connections at what can only be described as a pathological work load! Pipelining is rarely used in real deployments.
    • The tests appear to run only for 15s. This is insufficient time to reach steady state and it is no good your framework performing well for 15s if it is immediately hit with a 10s garbage collection starting on the 16th second.

    But let me get off my benchmarking hobby-horse, as I’ve said it all before:  Truth in Benchmarking,  Lies, Damned Lies and Benchmarks.

    What’s good about the Techempower Benchmarks?

    • There are many frameworks and servers in the comparison and whatever the flaws are, then are the same for all.
    • The test appear to be well run on suitable hardware within a controlled, open and repeatable process.
    • Their primary goal is to test core mechanism of web frameworks, such as object persistence.  However, jetty does not provide direct support for such mechanisms so we have initially not entered all the benchmarks.

    Conclusion

    Both the JSON and plaintext tests are busy connection tests and the JSON test has only a few connections.  Jetty has always prioritized performance for the more realistic scenario of many mostly idle connections and this has shown that even under pathological loads, jetty is able to fairly and efficiently share resources between all connections.

    Thus it is an impressive result that even when tested far outside of it’s comfort zone, Jetty-9.1.0 has performed at the top end of this league table and provided results that if you look beyond the headline throughput figures, presents the best scalability results.   While the tested loads are far from realistic, the results do indicate that jetty has very good concurrency and low contention.

    Finally remember that this is a .0 release aimed at delivering the new features of Servlet 3.1 and we’ve hardly even started optimizing jetty 9.1.x

  • Jetty 9 – Features

    Jetty 9 milestone 0 has landed! We are very excited about getting this release of jetty out and into the hands of everyone. A lot of work as gone into reworking fundamentals and this is going to be the best version of jetty yet!

    Anyway, as promised a few weeks back, here is a list of some of the big features in jetty-9. By no means an authoritative list of things that have changed, these are many of the high points we think are worthy of a bit of initial focus in jetty-9. One of the features will land in a subsequent milestone releases (pluggable modules) as that is still being refined somewhat, but the rest of them are largely in place and working in our initial testing.
    We’ll blog in depth on some of these features over the course of the next couple of months. We are targeting a November official release of Jetty 9.0.0 so keep an eye out. The improved documentation is coming along well and we’ll introduce that shortly. In the meantime, give the initial milestones a whirl and give us feedback on the mailing lists, on twitter (#jettyserver hashtag pls) or directly at some of the conferences we’ll be attending over the next couple of months.
    Next Generation Protocols – SPDY, WebSockets, MUX and HTTP/2.0 are actively replacing the venerable HTTP/1.1 protocol. Jetty directly supports these protocols as equals and first class siblings to HTTP/1.1. This means a lighter faster container that is simpler and more flexible to deal with the rapidly changing mix of protocols currently being experienced as HTTP/1.1 is replaced.
    Content Push – SPDY v3 supporting including content push within both the client and server. This is a potentially huge optimization for websites that know what a browser will need in terms of javascript files or images, instead of waiting for a browser to ask first.
    Improved WebSocket Server and Client

    • Fast websocket implementation
    • Supporting classic Listener approach and @WebSocket annotations
    • Fully compliant to RFC6455 spec (validated via autobahn test suite http://autobahn.ws/testsuite)
    • Support for latest versions of Draft WebSocket extensions (permessage-compression, and fragment)

    Java 7 – We have removed some areas of abstraction within jetty in order to take advantage of improved APIs in the JVM regarding concurrency and nio, this leads to a leaner implementation and improved performance.
    Servlet 3.1 ready – We actively track this developing spec and will be with support, in fact much of the support is already in place.
    Asynchronous HTTP client – refactored to simplify API, while retaining the ability to run many thousands of simultaneous requests, used as a basis for much of our own testing and http client needs.
    Pluggable Modules – one distribution with integration with libraries, third party technologies, and web applications available for download through a simple command line interface
    Improved SSL Support – the proliferation of mobile devices that use SSL has manifested in many atypical client implementations, support for these edge cases in SSL has been thoroughly refactored such that support is now understandable and maintainable by humans
    Lightweight – Jetty continues its history of having a very small memory footprint while still being able to scale to many ten’s of thousands of connections on commodity hardware.
    Eminently Embeddable – Years of embedding support pays off in your own application, webapp, or testing. Use embedded jetty to unit test your web projects. Add a web server to your existing application. Bundle your web app as a standalone application.

  • Jetty 9 – it's coming!

    Development on Jetty-9 has been chugging along for quite some time now and it looks like we’ll start releasing milestones in around the end of September.  This is exciting because we have a lot of cool improvements and features coming that I’ll leave to others to blog about in specific on over the next couple of months and things come closer to release.
    What I wanted to highlight in this blog post are the plans moving forward for jetty version wise pinpointed with a bit of context where appropriate.

    • Jetty-9 will require java 1.7

    While Oracle has relented a couple of times now about when the EOL is of java 1.6, it looks like it will be over within the next few months and since native support for SPDY (more below) is one of the really big deals about jetty-9 and SPDY requires java 1.7 that is going to be the requirement.

    • Jetty-9 will be servlet-api 3.0

    We had planned on jetty-9 being servlet-api 3.1 but since that api release doesn’t appear to be coming anytime soon, the current plan is to just make jetty-9 support servlet 3.0 and once servlet-api 3.1 is released we’ll make a minor release update of jetty-9 to support it.  Most of the work for supporting servlet-api 3.1 already exists in the current versions of jetty anyway so it shouldn’t be a huge deal.

    • Jetty-7 and Jetty-8 will still be supported as ‘mature’ production releases

    Jetty-9 has some extremely important changes in the IO layers that make supporting it moving forward far easier then jetty 7 and 8.  For much of the life of Java 1.6 and Java 1.7 there have been annoying ‘issues’ in the jvm NIO implementation that we (well greg to be honest) have piled on work around after work around until some of the work arounds would start to act up once the underlying jvm issue were resolved.  Most of this has been addressed in jetty-7.6.x and jetty-8.1.x releases assuming the latest jvm’s are being used (basically make sure you avoid anything in the 1.6u20-29 range).  Anyway, jetty-9 contains a heavily refactored IO layer which should make it easier to respond to these situations in the future should they arise in a more…well…deterministic fashion. 🙂

    • Jetty-9 IO is a major overhaul

    This deserves it’s own blog entry which it will get eventually I am sure, however it can’t be overstated how much the inner workings of jetty have evolved with jetty-9. Since its inception jetty has always been a very modular or component oriented http server. The key being ‘http’ server, and with Jetty-9 that is changing. Jetty-9 has been rearchitected from the IO layer up to directly support the separation of wire protocol from semantic, so it is now possible to support HTTP over HTTP, HTTP over SPDY, Websocket over SPDY, multiplexing etc with all protocols being first class citizens and no need to mock out
    inappropriate interfaces. While these are mostly internal changes, they ripple out to give many benefits to users in the form of better performance, smaller software and simpler and more appropriate configuration. For example instead of having multiples of differennt connector types, each with unique SSL and/or SPDY variants, there is now a single connector into which various connections factories are configured to support SSL, HTTP, SPDY, Websocket etc. This means moving forward jetty will be able to adapt easily and quickly to new protocols as they come onto the scene.

    • Jetty-6…for the love of god, please update

    Jetty-5 used to hold the title for ‘venerable’ but that title is really shifting to jetty-6 at this point.  I am constantly amazed with folks on places like stack overflow starting a project using jetty-6.  The linux distributions really need to update, so if you work on those and need help, please ping us.  Many other projects that embed jetty really need to update as well, looking at you Google App Engine and GWT!  If you are a company and would like help updating your jetty version or are interested in taking advantage of the newer protocols, feel free to contact webtide and we can help you make it easier.  If you’re an open source project, reach out to us on the mailing lists and we can assist there as much as time allows.  But please…add migrating to 7, 8 or 9 to your TODO list!

    • No more split production versions

    One of our more confusing situations has been releasing both jetty 7 and jetty 8 as stable production versions.  The reasons for our doing this were many and varied but with servlet 3.0 being ‘live’ for a while now we are going to shift back to the singular supported production version moving forward.  The Servlet API is backwards compatible anyway so we’ll be hopefully reducing some of the confusion on which version of jetty to use moving forward.

    • Documentation

    Finally, our goal starting with jetty-9 moving forward will be to release versioned documentation (generated with docbook)  to a common url under the eclipse.org domain as well as bundling the html and pdf to fit in the new plugin architecture we are working with.  So the days of floundering around for documentation on jetty should be coming to an end soon.
    Lots of exciting things coming in Jetty-9 that you’ll hear about in the coming weeks! Feel free to follow @jmcconnell on twitter for release updates!

  • SPDY – we push!

    SPDY, Google’s web protocol, is gaining momentum. Intending to improve the user’s web experience it aims at severely reducing page load times.
    We’ve blogged about the protocol and jetty’s straight forward SPDY support already: Jetty-SPDY is joining the revolution! and SPDY support in Jetty.
    No we’re taking this a step further and we push!
    SPDY push is one of the coolest features in the SPDY protocol portfolio.
    In the traditional http approach the browser will have to request a html resource (the main resource) and do subsequent requests for each sub resource. Every request/response roundtrip will add latency.
    E.g.:
    GET /index.html – wait for response before before browser can request sub resources
    GET /img.jpg
    GET /style.css – wait for response before we can request sub resources of the css
    GET /style_image.css (referenced in style.css)
    This means a single request – response roundtrip for each resource (main and sub resources). Worse some of them have to be done sequentially. For a page with lots of sub resources, the amount of connections to the server (traditionally browsers tend to open 6 connections) will also limit the amount of sub resources that can be fetched in parallel.
    Now SPDY will reduce the need to open multiple connections by multiplexing requests over a single connection and does more improvements to reduce latency as described in previous blog posts and the SPDY spec.
    SPDY push will enable the server to push resources to the browser/client without having a request for that resource. For example if the server knows that index.html contains a reference to img.jpg, style.css and that style.css contains a reference to style_image.css, the server can push those resources to the client.
    To take the previous example:
    GET /index.html
    PUSH /img.jpg
    PUSH /style.css
    PUSH /style_image.css
    That means only a single request/response roundtrip for the main resource. And the server immediately sends out the responses for all sub resources. This heavily reduces overall latency, especially for pages with high roundtrip delays (bad/busy network connections, etc.).
    We’ve written a unit test to benchmark the differences between plain http, SPDY and SPDY + push. Note that this is not a real benchmark and the roundtrip delay is emulated! Proper benchmarks are already in our task queue, so stay tuned. However, here’s the results:
    HTTP: roundtrip delay 100 ms, average = 414
    SPDY(None): roundtrip delay 100 ms, average = 213
    SPDY(ReferrerPushStrategy): roundtrip delay 100 ms, average = 160
    Sounds cool? Yes, I guess that sounds cool! 🙂
    Even better in jetty this means only exchanging a Connector with another, provide our implementation of the push strategy – done. Yes, that’s it. Only by changing some lines of jetty config you’ll get SPDY and SPDY + push without touching your application.
    Have a look at the Jetty Docs to enable SPDY. (will be updated soon on how to add a push strategy to a SPDY connector.)
    Here’s the only thing you need to configure in jetty to get your application served with SPDY + push transparently:
    <New id=”pushStrategy”>
    <Arg type=”List”>
    <Array type=”String”>
    <Item>.*.css</Item>
    <Item>.*.js</Item>
    <Item>.*.png</Item>
    <Item>.*.jpg</Item>
    <Item>.*.gif</Item>
    </Array>
    </Arg>
    <Set name=”referrerPushPeriod”>15000</Set>
    </New>
    <Call name=”addConnector”>
    <Arg>
    <New>
    <Arg>
    <Ref id=”sslContextFactory” />
    </Arg>
    <Arg>
    <Ref id=”pushStrategy” />
    </Arg>
    <Set name=”Port”>11081</Set>
    <Set name=”maxIdleTime”>30000</Set>
    <Set name=”Acceptors”>2</Set>
    <Set name=”AcceptQueueSize”>100</Set>
    <Set name=”initialWindowSize”>131072</Set>
    </New>
    </Arg>
    </Call>
    So how do we push?
    We’ve implemented a pluggable mechanism to add a push strategy to a SPDY connector. Our default strategy, called ReferrerPushStrategy is using the “referer” header to identify push resources on the first time a page is requested.
    The browser will request the main resource and quickly afterwards it usually requests all sub resources needed for that page. ReferrerPushStrategy will use the referer header used in the sub requests to identify sub resources for the main resource defined in the referer header. It will remember those sub resources and on the next request of the main resource, it’ll push all sub resources it knows about to the client.
    Now if the user will click on a link on the main resource, it’ll also contain a referer header for the main resource. However linked resources should not be pushed to the client in advance! To avoid that ReferrerPushStrategy has a configurable push period. The push strategy will only remember sub resources if they’ve been requested within that period from the very first request of the main resource since application start.
    So this is some kind of best effort strategy. It does not know which resources to push at startup, but it’ll learn on a best effort basis.
    What does best effort mean? It means that if the browser doesn’t request the sub resources fast enough (within the push period timeframe) after the initial request of the main resource it’ll never learn those sub resources. Or if the user is fast enough clicking links, it might push resources which should not be pushed.
    Now you might be wondering what happens if the browser has the resources already cached? Aren’t we sending data over the wire which the browser actually already has? Well, usually we don’t. First we use the if-modified-since header to identify if we should push sub resources or not and second the browser can refuse push streams. If the browser gets a syn for a sub resource it already has, then it can simply reset the push stream. Then the only thing that has been send is the syn frame for the push stream. Not a big drawback considering the advantages this has.
    There has to be more drawbacks?!
    Yes, there are. SPDY implementation in jetty is still experimental. The whole protocol is bleeding edge and implementations in browsers as well as the server still have some rough edges. There is already broad support amoung browsers for the SPDY protocol. Stable releases of firefox and chromium/chrome support SPDY draft2 out of the box and it already works really well. SPDY draft 3 however is only supported with more recent builds of the current browsers. SPDY push seems to work properly only with SPDY draft 3 and the latest chrome/chromium browsers. However we’re all working hard on getting the rough edges smooth and I presume SPDY draft 3 and push will be working in all stable browsers soon.
    We also had to disable push for draft 2 as this seemed to have negative effects on chromium up to regular browser crashes.
    Try it!
    As we keep eating our own dog-food, https://www.webtide.com is already updated with the latest code and has push enabled. If you want to test the push functionality get a chrome canary or a chromium nightly build and access our company’s website.
    This is how it’ll look in the developer tools and on chrome://net-internals page.
    developer-tools (note that the request has been done with an empty cache and the pushed resources are being marked as read from cache):

    net-internals (note the pushed and claimed resource count):

    Pretty exciting! We keep “pushing” for more and better SPDY support. Improve our push strategy and support getting SPDY a better protocol. Stay tuned for more stuff to come.
    Note that SPDY stuff is not in any official jetty release, yet. But most probably will be in the next release. Documentation for jetty will be updated soon as well.

  • NoSql Sessions with Jetty7 and Jetty8

    When Jetty 7.5.0 is released we will have officially started to dabble in the area of distributed session handling and storage. To start this out we have created a set of abstract classes around the general concept of NoSQL support, and have prepared an initial implementation using MongoDB. We will also be working on Ehcache and perhaps Cassandra implementations over time to round out the offering, but it is overall a pretty exciting time for these sorts of things.

    NoSQL sessions are a good idea for a number of usage scenarios, but as with NoSQL solutions in general, it is not a one-size-fits-all technology. The Jetty NoSQL session implementation should be good for scenarios that require decentralization, highly parallel work loads, and scalability, while also supporting session migration from one machine to the next for load balancing purposes. While we are initially releasing with just the MongoDB session manager, it is important to make clear that all the different distributed NoSQLish solutions out there have there own positives and negatives that you need to balance when choosing a storage medium. This is an interesting and diverse area of development, and since there is little standardization at the moment it is not a simple matter of exporting data from one system to the next if you want to change back ends.

    Before jumping in and embracing this solution for your session management, ask yourself some questions:

    • Do I require a lot of write behavior on my session objects?

    When you’re dealing with anything that touches the network to perform an action, you have an entirely different set of issues than if you can keep all your logic on one machine.  The hash session manager is the fastest solution for this use profile, but the JDBC session manager is not a bad solution if you need to operate with the network.  That in mind, there is an optimization in the NoSQL session managers where tight write loops should queue up a bit before an actual write to the back end MongoDB server occurs.  In general, if you have a session profile that involves a lot of writes all the time, you might want to shy away from this approach.

    • Am I bouncing sessions across lots of machines all the time?

    If you are, then you might be better off to get rid of sessions entirely and be more RESTful, but a networked session manager is going to be difficult to scale to this approach and be consistent.  By consistent I mean writing data into your session on one node and having that same data present within a session on another node.  If you’re looking at using MongoDB to increase the number of sessions you’re able to support, it is vitally important to remember that the network is not an inexhaustable resource, and keeping sessions localized is good practice, especially if you want consistent behavior. But if you want non-sticky sessions or mostly sticky sessions that can scale, this sort of NoSQL session manager is certainly an option, especially for lightweight, mostly read sessions.

    • Do I want to scale to crazy amounts of sessions that are relatively small and largely contain write-once read-often data?

    Great! Use this!  You are the people we had in mind when we developed the distributed session handling.

    On the topic of configuring the new session managers, it is much like other traditional ones: add them to the context.xml or set up with the regular jetty.xml route. There are, however, a couple of important options to keep in mind for the session ID manager.

    • scavengeDelay–How often will a scavenge operation occur looking for sessions to invalidate?
    • scavengePeriod–How much time after a scavenge has completed should you wait before doing it again?
    • purge (Boolean)–Do you want to purge (delete) sessions that are invalid from the session store completely?
    • purgeDelay–How often do you want to perform this purge operation?
    • purgeInvalidAge–How old should an invalid session be before it is eligible to be purged?
    • purgeValidAge–How old should a valid session be before it is eligible to be marked invalid and purged? Should this occur at all?

    A guide for detailed configuration can be found on our wiki at on the Session Clustering with MongoDB page.

    The new MongoDB session manager and session ID manager are located in the jetty-nosql module.  Since we plan to have multiple offerings we have made the mongodb dependency optional, so if you’re planning to use embedded Jetty, make sure you declare a hard dependency in Maven. You can also download the mongodb jar file and place it into a lib/mongodb directory within the jetty distribution itself; then you must add mongodb to the OPTIONS  on the cli or in the start.ini file you’re starting Jetty with.

    There were a number of different ways to go in implementing session ID management. While we are wholly tolerant of a user request being moved from one server to another, we chose to keep normal session operations localized to the machine where the session originates.  If the request bounces from one machine to another, the latest known session is loaded. If it is saved and then bounces back, Jetty notices the change in the version of the session and reloads, but these operations are heavy weight: they require pulling back all data of a session across the network, as opposed to a field or two of MongoDB goodness.  One side effect of this approach is the scavenge operation executes only on the known session IDs of a given node. In this scenario, if your happy cluster of Jetty instances has a problem and one of them crashes (not our fault!), there is potential for previously valid session IDs to remain in your MongoDB session store, never to be seen again, but also never cleaned up. That is where purge comes in: the purge process can perform a passive sweep through the MongoDB cluster to delete really old, valid sessions.  You can also delete the invalid sessions that are over a week old, or a month old, or whatever you like. If you have hoarding instincts, you can turn purge off (it’s true by default), and your MongoDB cluster will grow… and grow.

    We have also added some additional JMX support to the MongoDB session manager. When you enable JMX, you can access all the normal session statistics, but you also have the option to force execution of the purge and scavenge operations on a single node, or purge fully, which executes the purge logic for everything in the MongoDB store.  In this mode you can disable purge on your nodes and schedule the actions for when you are comfortable they will not cause issues on the network.  For tips on configuring JMX support for jetty see our tutorial on JMX.

    Lastly I’ll just mention that MongoDB is really a treat to work with. I love how easy it is to print the data being returned from MongoDB, and it’s in happy JSON.  It has a rich query language that allowed us to easily craft queries for the exact information we were looking for, reducing the footprint on the network the session work imposes.

     

  • Websocket Example: Server, Client and LoadTest

    The websocket protocol specification is approaching final and the Jetty implementation and API have been tracking the draft and is ready when the spec and browsers are available.   More over, Jetty release 7.5.0 now includes a capable websocket java client that can be used for non browser applications or load testing. It is fully asynchronous and can create thousands of connections simultaneously.

    This blog uses the classic chat example to introduce a websocket server, client and load test.

    The project

    The websocket example has been created as a maven project with groupid com.example.  The entire project can be downloaded from here.   The pom.xml defines a dependency on org.eclipse.jetty:jetty-websocket-7.5.0.RC1 (you should update to 7.5.0 when the final release is available), which provides the websocket API and transitively the jetty implementation.  There is also a dependency on org.eclipse.jetty:jetty-servlet which provides the ability to create an embedded servlet container to run the server example.

    While the project implements a Servlet, it is not in a typical webapp layout, as I wanted to provide both client and server in the same project.    Instead of a webapp, this project uses embedded jetty in a simple Main class to provide the server and the static content is served from the classpath from src/resources/com/example/docroot.

    Typically developers will want to build a war file containing a webapp, but I leave it as an exercise for the reader to put the servlet and static content described here into a webapp format.

    The Servlet

    The Websocket connection starts with a HTTP handshake.  Thus the websocket API in jetty also initiated by the handling of a HTTP request (typically) by a Servlet.  The advantage of this approach is that it means that websocket connections are terminated in the same rich application space provided by HTTP servers, thus a websocket enabled web application can be developed in a single environment rather than by collaboration between a HTTP server and a separate websocket server.

    We create the ChatServlet with an init() method that instantiates and configures a WebSocketFactory instance:

    public class ChatServlet extends HttpServlet
    {
      private WebSocketFactory _wsFactory;
      private final Set _members = new CopyOnWriteArraySet();
      @Override
      public void init() throws ServletException
      {
        // Create and configure WS factory
        _wsFactory=new WebSocketFactory(new WebSocketFactory.Acceptor()
        {
          public boolean checkOrigin(HttpServletRequest request, String origin)
          {
            // Allow all origins
            return true;
          }
          public WebSocket doWebSocketConnect(HttpServletRequest request, String protocol)
          {
             if ("chat".equals(protocol))
               return new ChatWebSocket();
             return null;
          }
        });
        _wsFactory.setBufferSize(4096);
        _wsFactory.setMaxIdleTime(60000);
      }
      ...

    The WebSocketFactory is instantiated by passing it an Acceptor instance, which in this case is an anonymous instance. The Acceptor must implement two methods: checkOrigin, which in this case accepts all; and doWebSocketConnect, which must accept a WebSocket connection by creating and returning an instance of the WebSocket interface to handle incoming messages.  In this case, an instance of the nested ChatWebSocket class is created if the protocol is “chat”.   The other WebSocketFactory fields have been initialised with hard coded buffers size and timeout, but typically these would be configurable from servlet init parameters.

    The servlet handles get requests by passing them to the WebSocketFactory to be accepted or not:

      ...
      protected void doGet(HttpServletRequest request,
                           HttpServletResponse response)
        throws IOException
      {
        if (_wsFactory.acceptWebSocket(request,response))
          return;
        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           "Websocket only");
      }
      ...

    All that is left for the Servlet, is the ChatWebSocket itself.   This is just a POJO that receives callbacks for events.  For this example we have implemented the WebSocket.OnTextMessage interface to restrict the call backs to only connection management and full messages:

      private class ChatWebSocket implements WebSocket.OnTextMessage
      {
        Connection _connection;
        public void onOpen(Connection connection)
        {
          _connection=connection;
          _members.add(this);
        }
        public void onClose(int closeCode, String message)
        {
          _members.remove(this);
        }
        public void onMessage(String data)
        {
          for (ChatWebSocket member : _members)
          {
            try
            {
              member._connection.sendMessage(data);
            }
            catch(IOException e)
            {
              e.printStackTrace();
            }
          }
        }
      }

    The handling of the onOpen callback is to add the ChatWebSocket to the set of all members (and remembering the Connection object for subsequent sends).  The onClose handling simply removes the member from the set.   The onMessage handling iterates through all the members and sends the received message to them (and prints any resulting exceptions).

     

    The Server

    To run the servlet, there is a simple Main method that creates an embedded Jetty server with a ServletHandler for the chat servlet, as ResourceHandler for the static content needed by the browser client and a DefaultHandler to generate errors for all other requests:

    public class Main
    {
      public static void main(String[] arg) throws Exception
      {
        int port=arg.length>1?Integer.parseInt(arg[1]):8080;
        Server server = new Server(port);
        ServletHandler servletHandler = new ServletHandler();
        servletHandler.addServletWithMapping(ChatServlet.class,"/chat/*");
        ResourceHandler resourceHandler = new ResourceHandler();
        resourceHandler.setBaseResource(Resource.newClassPathResource("com/example/docroot/"));
        DefaultHandler defaultHandler = new DefaultHandler();
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[] {servletHandler,resourceHandler,defaultHandler});
        server.setHandler(handlers);
        server.start();
        server.join();
      }
    }

    The server can be run from an IDE or via maven using the following command line:

    mvn
    mvn -Pserver exec:exec

    The Browser Client

    The HTML for the chat room simply imports some CSS and the javascript before creating a few simple divs to contain the chat text, the join dialog and the joined dialog:

    <html>
     <head>
     <title>WebSocket Chat Example</title>
     <script type='text/javascript' src="chat.js"></script>
     <link rel="stylesheet" type="text/css" href="chat.css" />
     </head>
     <body>
      <div id='chat'></div>
      <div id='input'>
       <div id='join' >
        Username:&nbsp;<input id='username' type='text'/>
        <input id='joinB' class='button' type='submit' name='join' value='Join'/>
       </div>
       <div id='joined' class='hidden'>
        Chat:&nbsp;<input id='phrase' type='text'/>
        <input id='sendB' class='button' type='submit' name='join' value='Send'/>
       </div>
      </div>
      <script type='text/javascript'>init();</script>
     </body>
    </html>

    The javascript create a room object with methods to handle the various operations of a chat room.  The first operation is to join the chat room, which is triggered by entering a user name.  This creates a new WebSocket object pointing to the /chat URL path on the same server the HTML was loaded from:

    var room = {
      join : function(name) {
        this._username = name;
        var location = document.location.toString()
          .replace('http://', 'ws://')
          .replace('https://', 'wss://')+ "chat";
        this._ws = new WebSocket(location, "chat");
        this._ws.onopen = this.onopen;
        this._ws.onmessage = this.onmessage;
        this._ws.onclose = this.onclose;
      },
      onopen : function() {
        $('join').className = 'hidden';
        $('joined').className = '';
        $('phrase').focus();
        room.send(room._username, 'has joined!');
      },
      ...

    The javascript websocket object is initialised with call backs for onopen, onclose and onmessage. The onopen callback is handled above by switching the join div to the joined div and sending a “has joined” message.

    Sending is implemented by creating a string of username:message and sending that via the WebSocket instance:

      ...
      send : function(user, message) {
        user = user.replace(':', '_');
        if (this._ws)
          this._ws.send(user + ':' + message);
      },
      ...

    If the chat room receives a message, the onmessage callback is called, which sanitises the message, parses out the username and appends the text to the chat div:

      ...
      onmessage : function(m) {
        if (m.data) {
          var c = m.data.indexOf(':');
          var from = m.data.substring(0, c)
            .replace('<','<')
            .replace('>','>');
          var text = m.data.substring(c + 1)
            .replace('<', '<')
            .replace('>', '>');
          var chat = $('chat');
          var spanFrom = document.createElement('span');
          spanFrom.className = 'from';
          spanFrom.innerHTML = from + ': ';
          var spanText = document.createElement('span');
          spanText.className = 'text';
          spanText.innerHTML = text;
          var lineBreak = document.createElement('br');
          chat.appendChild(spanFrom);
          chat.appendChild(spanText);
          chat.appendChild(lineBreak);
          chat.scrollTop = chat.scrollHeight - chat.clientHeight;
        }
      },
      ...

    Finally, the onclose handling empties the chat div and switches back to the join div so that a new username may be entered:

      ...
      onclose : function(m) {
        this._ws = null;
        $('join').className = '';
        $('joined').className = 'hidden';
        $('username').focus();
        $('chat').innerHTML = '';
      }
    };

    With this simple client being served from the server, you can now point your websocket capable browsers at http://localhost:8080 and interact with the chat room. Of course this example glosses over a lot of detail and complications a real chat application would need, so I suggest you read my blog is websocket chat simpler to learn what else needs to be handled.

    The Load Test Client

    The jetty websocket java client is an excellent tool for both functional and load testing of a websocket based service.  It  uses the same endpoint API as the server side and for this example we create a simple implementation of the OnTextMessage interface that keeps track of the all the open connection and counts the number of messages sent and received:

    public class ChatLoadClient implements WebSocket.OnTextMessage
    {
      private static final AtomicLong sent = new AtomicLong(0);
      private static final AtomicLong received = new AtomicLong(0);
      private static final Set<ChatLoadClient> members = new CopyOnWriteArraySet<ChatLoadClient>();
      private final String name;
      private final Connection connection;
      public ChatLoadClient(String username,WebSocketClient client,String host, int port)
      throws Exception
      {
        name=username;
        connection=client.open(new URI("ws://"+host+":"+port+"/chat"),this).get();
      }
      public void send(String message) throws IOException
      {
        connection.sendMessage(name+":"+message);
      }
      public void onOpen(Connection connection)
      {
        members.add(this);
      }
      public void onClose(int closeCode, String message)
      {
        members.remove(this);
      }
      public void onMessage(String data)
      {
        received.incrementAndGet();
      }
      public void disconnect() throws IOException
      {
        connection.disconnect();
      }

    The Websocket is initialized by calling open on the WebSocketClient instance passed to the constructor.  The WebSocketClient instance is shared by multiple connections and contains the thread pool and other common resources for the client.

    This load test example comes with a main method that creates a WebSocketClient from command line options and then creates a number of ChatLoadClient instances:

    public static void main(String... arg) throws Exception
    {
      String host=arg.length>0?arg[0]:"localhost";
      int port=arg.length>1?Integer.parseInt(arg[1]):8080;
      int clients=arg.length>2?Integer.parseInt(arg[2]):1000;
      int mesgs=arg.length>3?Integer.parseInt(arg[3]):1000;
      WebSocketClient client = new WebSocketClient();
      client.setBufferSize(4096);
      client.setMaxIdleTime(30000);
      client.setProtocol("chat");
      client.start();
      // Create client serially
      ChatLoadClient[] chat = new ChatLoadClient[clients];
      for (int i=0;i<chat.length;i++)
        chat[i]=new ChatLoadClient("user"+i,client,host,port);
      ...

    Once the connections are opened, the main method loops around picking a random client to speak in the chat room

      ...
      // Send messages
      Random random = new Random();
      for (int i=0;i<mesgs;i++)
      {
        ChatLoadClient c = chat[random.nextInt(chat.length)];
        String msg = "Hello random "+random.nextLong();
        c.send(msg);
      }
      ...

    Once all the messages have been sent and all the replies have been received, the connections are closed:

      ...
      // close all connections
      for (int i=0;i<chat.length;i++)
        chat[i].disconnect();

    The project is setup so that the load client can be run with the following maven command:

    mvn -Pclient exec:exec

    And the resulting output should look something like:

    Opened 1000 of 1000 connections to localhost:8080 in 1109ms
    Sent/Received 10000/10000000 messages in 15394ms: 649603msg/s
    Closed 1000 connections to localhost:8080 in 45ms

    Yes that is 649603 messages per second!!!!!!!!!!! This is a pretty simple easy test, but it is still scheduling 1000 local sockets plus generating and parsing all the websocket frames. Real applications on real networks are unlikely to achieve close to this level, but the indications are good for the capability of high throughput and stand by for more rigorous bench marks shortly.

     

     

     

  • Jetty @ Eclipse Summit Europe

    I have presented the advanced features of Jetty at the Eclipse Summit Europe 2010.

    Highlights of the features that I presented are:

    • Jetty & OSGi, showing the full integration of Jetty with the OSGi world
    • Jetty’s asynchronous HTTP client
    • Jetty’s WebSocket protocol support
    • Jetty’s WTP integration
    • Jetty’s Cloudtide, for multi-tenanted deployments of web applications

    Here you can download the slides of the presentation.