Jetty Optimization Guide

Introduction

This guide describes techniques for optimizing a deployment of the Jetty HTTP server and servlet container. While some of the techniques described here are particular to the Jetty server, many are generally applicable to any similar servlet server. Note that for a J2EE application server, often it is the web tier that controls the vast majority of requests entering the server. Thus optimization of the web tier is key to the optimization of the entire container.

Optimization is more of an art than a science, so this document does not present a specific solution. Instead the issues and parameters that need to be considered are discussed and "rules of thumb" are given where appropriate.

Optimization Overview

HTTP Traffic Profile

In order to optimize a servlet container it is important to understand how requests are delivered to the container and what resources are used to handle it.

Browser Connection Handling

Each user connecting to the webapp container will be using a browser or other HTTP client application. How that client connects to the server greatly effects the optimization process. Historically browsers would only send a single HTTP request over a TCP connection, which meant that each HTTP request incurred the latency and resource costs of establishing a connection to the server. In order to quickly render a page with many images, each requiring a request, browsers could open up to 8 connections to the server so that multiple requests could be outstanding at once. In some specific circumstances with HTTP/1.0 browsers multiple requests could be sent over a single connection.

Modern browsers are now mostly using HTTP/1.1 persistent connections that allow multiple requests per connection in almost all circumstances. Thus browsers now typically open only 1 or 2 connections to each server and send many requests over those connections. Browsers are increasingly using request pipelining so that multiple requests may be outstanding on a single connection, thus decreasing request latency and reducing the need for multiple connections.

This situation results in a near linear relationship between the number of server connections and the number of simultaneous users off the server:

SimultaneousUser * NconnectionPerClient == SimultaneousConnections

Server Connection Handling

For Jetty and almost all java HTTP servers, each connection accepted by the server is allocated a thread to listen for requests and to handle those requests. While non-blocking solutions are available to avoid this allocation of a thread per connection, the blocking nature of the servlet API prevents these being efficiently used with a servlet container.

SimultaneousConnections <= Threads

Persistent Connections

Persistent connections are supported by the HTTP/1.1 protocol and to a lesser extent by the HTTP/1.0 protocol. The duration of these connections and how they interact with a webapp can greatly effect the optimization of the server and webapp.

A typical webapp will be comprised of a dynamically generated page with many static components such as style sheets and/or images. Thus to display a page a cluster of requests are sent for the main page and for the resources that it uses. It is highly desirable for persistent connections to be held at least long enough for all the requests of a single page view to be completed.

After a page is served to a user, there is typically a delay while the user reads or interacts with the page. After which another request cluster is sent in order to obtain the next page of the webapp. The delay between request clusters can be anything from seconds to minutes. It is desirable for the persistent connections to be held longer than this delay in order to improve the responsiveness of the webapp and to reduce the costs of new connections. However the cost of this may be many idle connections on the server which are consuming resources for no server throughput.

The duration that persistent connections are held is under the control of both the client and the server, either of which can close a connection at any time. The browsers cache settings may also greatly effect the use of persistent connections, as many requests for resources on a page may not be issued or may be handled with a simple 304-NotModified response.

Optimization Objectives

There are several key objectives when optimizing a webapp container, unfortunately not all of them are compatible and you are often faced with a trade off between two or more objectives.

Maximize Throughput

Throughput is the primary measure used to rate the performance of a web container and it is mostly measured in requests per second. Your efforts in optimizing the container will mainly be aimed at maximizing the request rate or at least ensuring a minimal rate is achievable. However you must remember that request rate is an imperfect measure as not all requests are the same and that it is simple to measure a request rate for load that is unlike a real load. Specifically:

Minimize Latency

Latency is a delay in the processing of requests and it is desirable to reduce latency so that web applications appear responsive to the users. There are two key sources of latency to consider:

While latency is not directly related to throughput, there is often a trade off to be made between reducing latency and increasing throughput. Server resources that are allocated to idle connections may be better deployed handing actual requests.

Minimize Resources

The processing of each request consumes server resources in the form of memory, CPU and time. Memory is used for buffers, program stack space and application objects. Keeping memory usage within a servers physical available memory is important for maximum throughput. Conversely using a servers virtual memory may allow increased simultaneous users and can also decrease latency.

Servers will have 1 or more CPUs available to process requests. It is important that the scheduling of these processors is done in such a way that they spend more time handling requests and less time organizing and switching between tasks.

The servers often allocate resources based on time and it is important to tune timeouts so that those resources have a high probability of being productively used.

Graceful degradation

Much of optimization is focused on providing maximum throughput under average or high offered load rates. However for many systems that wish to offer high availability and high quality of service, it is important to optimize the performance under extreme offered load, either to continue providing reasonable service to some of the offered load or to gracefully degrade service to all of the offered load.

Analyzing Traffic

Before beginning to optimize the configuration of your HTTP server and servlet container, it is fundamental that you analyse the profile of the traffic you expect your server to handle. This can be estimated or measured from an actual live server. The type of information that is useful gather includes:

Attribute Variations Comment
Request rate average, peak The number of requests per second
Connection rate average, peak The number of new connections established with the server per second
Simultaneous Users average, peak The number of users that a simultaneously interacting with the server.
Requests per page average The number of requests that is required to render a page of the webapp. Includes images and style sheets, but may be affected by client caching.
Page view time average The period of time that a typical user will view a page before requesting another from the webapp.
Session duration average The period of time that an average user will remain in contact with the server. This can be used to estimate session and memory requirements

Measuring Traffic

The most accurate way to measure the attributes listed above is to measure them on a live server that is handling real traffic for the webapp that you are trying to optimize. Statistics and log analysis can then be used to derive the information above.

Jetty supports statistics collection at both the server and context level. The following configuration excerpt shows how to turn on statistics for the server and for a particular web application:

<Configure class="org.mortbay.jetty.Server">
  ...
  <Call name="addWebApplication">
    <Arg>/myapp</Arg>
    <Arg>./webapps/myapp</Arg>
    <Set name="statsOn">false</Set>
  </Call>
  ...
  <Set name="statsOn">false</Set>
  ...
</Configure>

While statistics can be enabled as above, it is probably just as convenient to turn them on using a JMX agent to the Jetty MBeans. If Jetty is run with JBoss or within a JMX server, then a JMX agent can be used to configure and view statistics collection

Jetty HttpServer Statistics

The following statistics attributes are available on the org.mortbay.http.HttpServer class or via the associated MBean which is normally named like "org.mortbay:Jetty=0":

Attribute Comment
statsOn True if statistics collection is turned on.
statsOnMs Time in milliseconds stats have been collected for
statsReset() Reset statistics
connections Number of connections accepted by the server since statsReset() called
connectionsOpen Number of connections currently open that were opened since statsReset() called
connectionsOpenMax Maximum number of connections opened simultaneously since statsReset() called
connectionsDurationAve Sliding average duration in milliseconds of open connections since statsReset() called
connectionsDurationMax Maximum duration in milliseconds of an open connection since statsReset() called
connectionsRequestsAve Sliding average number of requests per connection since statsReset() called
connectionsRequestsMax Maximum number of requests per connection since statsReset() called
errors Number of errors since statsReset() called. An error is a request that resulted in an exception being thrown by the handler
requests Number of requests since statsReset() called
requestsActive Number of requests currently active
requestsActiveMax Maximum number of active requests since statsReset() called
requestsDurationAve Average duration of request handling in milliseconds since statsReset() called
requestsDurationMax Get maximum duration in milliseconds of request handling since statsReset() called.

Jetty HttpContext Statistics

The following statistics attributes are available on the org.mortbay.http.HttpContext class or via the associated MBean which is normally named like "org.mortbay:Jetty=0,HttpContext=0,context=/myappp":

Attribute Comment
statsOn True if statistics collection is turned on
statsOnMs Time in Milliseconds that stats have been collected for
statsReset() Reset statistics
requests Number of requests since statsReset() called
requestsActive Number of requests currently active
requestsActiveMax Maximum number of active requests since statsReset() called
responses1xx Number of responses with 1xx status (Informal) since statsReset() called
responses2xx Number of responses with 2xx status (Success) since statsReset() called
responses3xx Number of responses with 3xx status (Redirection) since statsReset() called
responses4xx Number of responses with 4xx status (Client Error) since statsReset() called
responses5xx Number of responses with 5xx status (Server Error) since statsReset() called

Estimating Traffic

It may not be possible to measure actual live traffic of a deployment to be optimized. In this case estimates must be made to obtain a traffic profile on which to base your optimization. The following work sheets give some examples of how this may be done:

Attribute Formula Example Comment
SimultaneousUsers - 1000 Estimated from marketing or other sources.
UserSessionDuration - 180 seconds Time a single user spends interactive with the webapp. Estimated from marketing, usage trials or other sources.
AvePageViewTime - 30 seconds Time between page requests from a single user. Estimated from marketing or usage trials or other sources.
PagesPerUserSession UserSessionDuration/PageViewTime 6
RequestsPerPageNoCache - 12 Calculated from inspection of HTML
RequestsPerPageCache - 3 Calculated from inspection of HTML and usage trials.
RequestsPerUserSession RequestsPerPageNoCache+ (RequestsPerPageCache* (PagesPerUserSession-1)) 27
RequestsPerSecPerUser RequestsPerUserSession/ UserSessionDuration 0.15
RequestsPerSec SimultaneousUsers* RequestsPerSecPerUser 150
ConnectionsPerUser - 2.5 Measured from usage trials with estimated browser mix.
AverageConnections SimultaneousUsers* ConnectionsPerUser 2500
ConnectionsPerSecond ConnectionsPerUser* SimultaneousUsers/ UserSessionDuration 13.88 Assuming persistent connections that will span entire user session. If connections will not span session the multiply by PagesPerUserSession
PeakRequestsPerSecond 2*ConnectionsPerSecond + (RequestsPerPageNoCache- RequestsPerPageCache) * SimultaneousUsers/ UserSessionDuration 77.76 Based on SimultaneousUsers doubling in UserSessionDuration. The formula represents double the normal requests rate, plus the additional load of the new users loading the initial page with no cache.

This work sheet is only indicative of an estimate process that can be used, specially the method for determining the peak request rate. If possible , several estimation techniques should be used and the worse case numbers assumed.

Clustered Traffic

When running a cluster of application servers, it is often desirable to be able to handle the max expected load in the advent of a node failure. Thus once the single node traffic has been estimated or measured, the traffic loads for failure modes can be calculated:

Nodes in Cluster Failed Nodes Load
2 1 200%
3 1 150%
3 2 300%
4 1 133%
4 2 200%

Generating Traffic

Once the expected traffic profile has been analysed, a test client can be used to generate load on the server that reflects realistic load. It is important to make sure that the test client used is generating realistic load:

Optimizing Jetty

Jetty has a few features that have been deprecated or that are particularly resource hungry. Before starting optimizing the more conventional attributes it is worthwhile to make sure that these features are turned off or minimally configured.

Request Log Buffering

The Jetty request log mechanism has the ability to buffer its output in memory before writing this to a file, which was intended to reduce synchronization load on the server. Unfortunately analysis of actual performance shows that the a server with buffering turned on has around 5% maximum throughput. Prior to Jetty release 4.2.9 log buffering was turned on by default. This should be turned off:

<Configure class="org.mortbay.jetty.Server">
  ...
  <Set name="RequestLog">
    <New class="org.mortbay.http.NCSARequestLog">
      <Arg><SystemProperty name="jetty.home" default="."/>/logs/yyyy_mm_dd.request.log</Arg>
      <Set name="retainDays">90</Set>
      <Set name="append">true</Set>
      <Set name="extended">false</Set>
      <Set name="buffered">false</Set>
      <Set name="logTimeZone">GMT</Set>
    </New>
  </Set>
  ...
</Configure>

Statistics

The Jetty server supports statistic collection at the server and at the context level. While stats collection itself does not involve significant work load, it does require synchronization in order to correctly count some statistics. On a multi CPU machine, this extra synchronization could significantly affect the performance of the server, thus statistics should be turned off while optimizing the server. Note that this is somewhat counter productive, as the statistics are very useful for measuring the results of optimization. Thus the recommended use of the server statistics is to measure the profile of real load being handled by the server. This profile can then be used in generating test load, hopefully from a test client which itself can generate statistics which can be used to evaluate optimizations.

NIO SocketChannelListener

Jetty releases from release 4.0.0 to 4.2.9 contained the SocketChannelListener implementation of the HttpListener interface. This implementation used the features of the java 1.4 NIO library to use non-blocking sockets for idle connections. The intent was to avoid allocating a java thread to idle connections. Unfortunately, due to the nature of the servlet API, the sockets had to be returned to blocking mode before control was passed to a servlet. The resulting constant changing of the NIO select sets proved to consume significantly more system resources than was saved by reducing the required number of threads. The SocketChannelListener has been deprecated since 4.2.10 and should not be used for any release unless for experimental purposes.

Max Read Time

The Jetty HTTP Listeners in versions prior to 4.1.1 had a parameter called maxReadTime, which was used to limit the time a request handler would wait for request content (e.g. on a form POST). This parameter, like maxIdleTime, was used to set the SO timeout value on the underlying connection socket. Unfortunately, if the maxReadTime value was different to the maxIdleTime value, then the SO timeout value was changed twice for every request. This proved to cause a significant reduction of throughput of the server, in the order of 10%. Thus for Jetty versions prior to 4.1.1 it is important to set the maxIdleTime and maxReadTime parameters to the same value:

<Configure class="org.mortbay.jetty.Server">
  <Call name="addListener">
    <Arg>
      <New class="org.mortbay.http.SocketListener">
        <Set name="port">8080</Set
        <Set name="minThreads">25</Set>
        <Set name="maxThreads">255</Set>
        <Set name="maxIdleTimeMs">60000</Set>
        <Set name="maxReadTimeMs">60000</Set>
      </New>
    </Arg>
  </Call>
  ...

For Jetty versions 4.1.1 or later, maxReadTime should not be set as it is ignored and produces a warning.

Optimizing Memory

Memory is a key resource that must be managed in any optimization of a web container. The procedure is to:

  1. Measure the static and dynamic memory requirements of your application.
  2. Configure the JVMs memory limits
  3. Adjust the thread pool to constrain dynamic memory use.
  4. Tune garbage collection.

To tune memory usage, using a profiling tool like optimizeIt of jProbe can be very useful, however it can also be done simply by monitoring the memory allocated to the process by the operating system.

Measuring memory usage

Running a webapp can consume memory for:

Check for memory leaks

Before optimizing your memory, it is important to establish that your webapp does not have any memory leaks. This is to say that no memory allocated when processing requests that cannot be freed when the server returns to idle. This can be determined by running the application with a constant low to medium load and monitoring the memory usage. The memory allocated should increase to a level and then stabilize. If the memory continues to grow and/or a OutOfMemory exception is eventually thrown, then the application has an object/memory leak. Such an application will not be able to run long term and the leak should be fixed before optimizing or deploying the webapp.

Note that application data caches or poor garbage collection (GC) behaviour may appear as a memory leak. If possible disable application caches or configure them to small sized in order to test the applications underlying memory requirements. The JVM may be forced to perform a GC after a fixed number of requests by the requestsPerGC attribute of HttpServer. This can be set to a low value to avoid large fluctuations in memory usage during this measurement phase:

<Configure class="org.mortbay.jetty.Server">
  ... 
  <Set name="requestsPerGC">100</Set>
  ...
</Configure>

Stack space Usage

JVMs allocate a fixed amount of stack space per thread created. The stack space is used for storing parameters and other objects associated with a method call. The more nested method calls that you application requires (deeper stacks), then more stack space is required. Typically the default stack settings for JVMs are rather generous and are allocated per thread, thus significant savings can be made by tuning this allocation.

For many JVMs, the stack space allocation is controlled with the -Xss option and the following command runs Jetty with 96kb allocated per stack:

java -Xss96k -jar start.jar

The simplest way to measure your stack requirements is to reduce the stack allocated until complex requests fail with StackOverflowException. You then need to increase your stack allocation with a good safety margin, the size of which will depend greatly on your application as some may have large variation in stack usage, specially those that use recursion.

Static & Dynamic Memory Usage

An estimate of the static and dynamic heap space usage is needed to optimize the memory allocation. This is best done by measuring memory usage under realistic steady load at several load levels. The Jetty HttpListener should be configured to have a low minimal threads setting, so that idle threads do not effect the measurements.

The following table shows some results for a simple test for memory usage using the unix ps command to determine the resident memory set size:

Active connections/threads Process size in kb kb per connection
0 23076
20 27540 224
40 29352 90
60 31868 125
100 33852 49
150 38264 88

Extrapolating from this table gives the following approximate formula for memory usagage for this webapp:

memoryRequired = 23Mb + threads * 200kb

Ideally this formula should be tested with direct measurement under all load levels.

This formula can now be used to calculate the memory requirements for your system and the JVM parameters should set to ensure that enough memory is available when the maximum number of threads are in use. For the above example, if a maximum of 500 threads are required (see below) and a 128k stack size is used, then 120MB of memory is required and the JVMs memory parameters should be configures as follows:

java -Xss128k -Xms120m -jar start.jar

Alternately, the memory formula can be used in reverse. If a known amount of physical or virtual memory is available and must not be exceeded, then the maximum number of threads can be determined.

Clustered Memory Usage.

Memory usage for a node in a cluster cannot be measured by looking at a single node. If distributed sessions or EJBs are being used, then memory used on one node may be replicated on all nodes. For example, with distributed HTTP sessions, each node must have capacity to store all the sessions for all the nodes in the cluster.

For this reason, it is often desirable to not have large homogenous clusters. Rather a cluster of clusters topology can reduce the memory and failure contingency load on each node.

Optimizing Threads

Once you have determined your traffic profile and your memory profile, it is now possible to tune your server by adjusting the parameters of the thread pool. Each Jetty HttpListener has a pool of threads that is used to allocate threads to accepted connections. The following parameters can be used to configure the thread pool of each listener:

Parameter Comment
maxThreads limit to the number of threads that can be allocated to connections for that HTTP listener. This will effectively limit the number of simultaneous users of the server as well as the maximum memory usage.
minThreads The minimum number of unused threads to keep within the thread pool. A large number of unused threads will allows a server to respond to a sudden increase in load with little latency. More importantly, a HTTP listener is considered to be low on resources once it's pool cannot allocate minThreads unused threads without exceeding max threads.
maxIdleTimeMs The maximum time in milliseconds that a thread can be allocated to a connection without a request being received. This limits the duration of idle persistent connections.
lowResourcePersistTimeMs An alternative value for maxIdleTimeMs to be used when the listener is low on resources (see minThreads).
poolName If multiple HTTP Listeners are used, those with the same pool name will share the same thread pool. This avoid one listener running low on threads while another has idle threads.

Setting maxThreads

The primary objective of the maxThread setting is to protect the server from excess resource utilization from high connection or request rates. Without a limit to the maximum threads, it would be possible for arbitrary high load to be accepted by the server which would eventually lead to one of the following failure modes:

There are two main approaches to setting maxThreads:

  1. If a good estimate or measurement of the maximum load is known, then maxThreads is set high enough to handle this and then system verified to check that none of the failure modes are breached. This approach results in a server that is good enough for the webapp and can leave server resources available for other uses.
  2. Various maxThreads values are tested with a test client generating a load of approximately the same value. The tested maxThreads value is increased until such time as one of the failure modes above is detected or the measured throughput starts to decrease. This approach results in a server that uses all the system resources and requires a dedicated machine.

If with either of these approaches, the estimated, measured or required maximum load requires a maxThread value that exhaust the system memory, CPU, connections or other resources, then the machine is not sufficient for that webapp. In this case, additional server resources (memory, CPU, kernel configuration) is required or a clustering solution can be considered.

Once a server has reached it's maximum number of threads, then any new connections attempted are held by the operating system until either they time out, a thread becomes available to accept the connection or they are refused when the operating system queue becomes full.

Setting minThreads

The minThreads value is used to control how a server degrades under extreme load. Once there are less than minThreads available in the thread pool, then the lowResourcePersisteTimeMs parameter can be used to free up other idle threads.

If a good estimate or measure of average and maximum load are known, then the minThreads value can be set to half the difference between the average and maximum.

minThreads == (maxThreads - averageConnections) / 2

Thus if maxThreads is 3000 and averageConnections is 2500, then minThreads could be set at 250, so that low resource timeouts will be applied once the actual connections exceeds 2750.

Alternately, minThreads may be set to protect excess memory usage. If maxThreads requires more memory than is physically available, then minThreads can be set to free resources once physical memory is exceeded. Using the memory formula example from above and if 47Mb of physical memory is available on the system (when running the OS), then for maxThreads == 200:

minThreads == maxThreads - ( ( 47Mb - 23Mb ) / 200kb ) == 80

Setting maxIdleTimeMs

The idle time of a thread is used to limit the time that an persistent connection can be idle. Higher values are desirable to reduce latency for a user and avoid the expense of recreating TCP/IP connections. However, if the value is set too high, it wil result in many connections being left open when the user is no longer browsing the webapp and the resources allocated to it are effectively wasted for a long period of time.

A good value to use for the maxIdleTimeMs is slightly longer than the average page view time for the application, so that persistent connections are held long enough to span the time between page requests for an average user.

Setting lowResourcesPersistTimeMs

A HTTP Listener is considered low on resources if there are less than minThreads available in the thread pool and a lowResourcePersistTimeMs can be set to replace maxIdleTimeMs so that idle connections can be freed for other connections. The reasoning for this is that once a server is low on resources, there is little benefit keeping resources allocated to idle connections in the hope that new requests will come from them.

With a low lowResourcesPersistTimeMs value set, performance will degrade more gracefully as maxThreads is approached.

The value of lowResourcePersistTimeMs should be long enough to ensure that all requests in the cluster for a page view can be served by a persistent connection. This is typcially governed by the network latency and should not be more than a few seconds and can be as low as a few hundred milliseconds for a good network.

Setting poolName

If a server has multiple HTTP listeners configured, it may be desirable to share the thread pool between listeners, so that one listener is not starved or resources if the other has free threads. If you wish to reserve capacity for a particular listener, then a shared thread pool should not be used:

<Configure class="org.mortbay.jetty.Server">
  ...
 <Call name="addListener">
    <Arg>
      <New class="org.mortbay.http.SocketListener">
        <Set name="port">8080</Set>
        <Set name="minThreads">80</Set>
        <Set name="maxThreads">200</Set>
        <Set name="maxIdleTimeMs">30000</Set>
        <Set name="lowResourcePersistTimeMs">2500</Set>
        <Set name="poolName">Listener</Set>
      </New>
    </Arg>
  </Call>

  <Call name="addListener">
    <Arg>
      <New class="org.mortbay.http.SunJsseListener">
        <Set name="port">443</Set>
        <Set name="poolName">Listener</Set>
        <Set name="keystore">./etc/demokeystore</Set>
        <Set name="password">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
        <Set name="keyPassword">OBF:1u2u1wml1z7s1z7a1wnl1u2g</Set>
      </New>
    </Arg>
  </Call>
  ...
</Configure>

Other Optimizations

Buffering

Providing larger buffers for the HTTP Listeners allows more efficient processing and generation of content, with less blocking and content switching. It also allows the TCP/IP protocol to more efficiently run it's sliding window protocol and avoid network latencies. Prior to Jetty release 4.2.10, the default buffer size was 4096 bytes. This has now been increased to 8192 bytes. The buffer size can be set as follows:

<Configure class="org.mortbay.jetty.Server">
  ...
 <Call name="addListener">
    <Arg>
      <New class="org.mortbay.http.SocketListener">
        <Set name="port">8080</Set>
        <Set name="minThreads">80</Set>
        <Set name="maxThreads">200</Set>
        <Set name="maxIdleTimeMs">30000</Set>
        <Set name="lowResourcePersistTimeMs">2500</Set>
        <Set name="poolName">Listener</Set>
        <Set name="bufferSize">8192</Set>
      </New>
    </Arg>
  </Call>
  ...
</Configure>

Security

Authenticated security constraints on a webapp can be expensive to check as often a realm is implemented using crypto algorithms or with a remote AAA server or database involved.

Frequently a webapp page is constructed with many images that are not sensitive and do not need to be protected with an authenticated security constraint. Significant performance gains can be obtained by excluding such static resources from a security constraint.

For example consider a webapp that protects the directory /private with an authenticated constraint, but has a number of non-sensitive images in the /private/images directory, then the following web.xml excerp can be used to protect the private directory without the expense of protecting the images directory.

  ...
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Authed User Required</web-resource-name>
      <url-pattern>/private/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <role-name>*</role-name>
    </auth-constraint>
  </security-constraint>
  
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Images Not Protected</web-resource-name>
      <url-pattern>/private/images/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>HEAD</http-method>
    </web-resource-collection>
  </security-constraint>

Logging

Logging of requests can add extra CPU load per request and an extra synchronization point. The following points should be considered to optimize the logging configuration:

The following request log configuration applies the points above.

<Configure class="org.mortbay.jetty.Server">
  ...
  <Set name="RequestLog">
    <New class="org.mortbay.http.NCSARequestLog">
      <Set name="filename">./logs/yyyy_mm_dd.request.log</Set>
      <Set name="buffered">false</Set>
      <Set name="retainDays">90</Set>
      <Set name="append">true</Set>
      <Set name="extended">false</Set>
      <Set name="logTimeZone">GMT</Set>
      <Set name="ignorePaths">
        <Array type="String">
          <Item>/images/*</Item>
          <Item>*.css</Item>
        </Array>
      </Set>
    </New>
  </Set>
  ...
</Configure>

Application

The way a web application is written can greatly effect the efficiency of the service. The following points should be considered when writing or reviewing your webapplication: