293222 improved statistics collection for async

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1022 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2009-10-31 12:10:54 +00:00
parent a804e3ee7a
commit b170e765f3
10 changed files with 784 additions and 331 deletions

View File

@ -12,6 +12,7 @@ jetty-7.0.1-SNAPSHOT
+ 292642 Fix errors in embedded Jetty examples
+ 292825 Continuations ISE rather than ignore bad transitions
+ 292546 Proactively enforce HttpClient idle timeout
+ 293222 Improved StatisticsHandler for async
+ 293506 Unable to use jconsole with Jetty when running with security manager
+ 293557 Add "jad" mime mapping
+ JETTY-937 More JVM bug work arounds. Insert pause if all else fails

View File

@ -29,6 +29,7 @@
</execution>
</executions>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
@ -53,6 +54,7 @@
</execution>
</executions>
</plugin>
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-remote-resources-plugin</artifactId>

View File

@ -2,14 +2,22 @@ StatisticsHandler: Request Statistics gathering
statsOnMs: Time in milliseconds stats have been collected for.
statsReset(): Resets statistics.
requests: Number of requests since statsReset() called.
requestsExpired: Number of requests expired since statsReset() called.
requestsResumed: Number of requests resumed since statsReset() called.
requestTimeAverage: Average time in milliseconds of request handling since statsReset() called.
requestTimeMin: Minimum time in milliseconds of request handling since statsReset() called.
requestTimeMax: Maximum time in milliseconds of request handling since statsReset() called.
requestTimeTotal: Total time in milliseconds of all request handling since statsReset() called.
requestsActive: Number of requests currently active since statsReset() called.
requestsActiveMax: Maximum number of active requests since statsReset() called.
requestTimeMax: Maximum time in milliseconds of request handling since statsReset() called.
requestTimeTotal: Total time in milliseconds of all request handling since statsReset() called.
dispatched: Number of dispatches since statsReset() called.
dispatchedActive: Number of dispatches currently active since statsReset() called.
dispatchedActiveMax: Maximum number of active dispatches since statsReset() called.
dispatchedTimeMax: Maximum time in milliseconds of dispatched handling since statsReset() called.
dispatchedTimeTotal: Total time in milliseconds of all dispatched handling since statsReset() called.
suspends: Number of requests suspended since statsReset() called.
suspendsActive: Number of dispatches currently active since statsReset() called.
suspendsActiveMax: Maximum number of active dispatches since statsReset() called.
resumes: Number of requests resumed since statsReset() called.
expires: Number of requests expired since statsReset() called.
requestTimeAverage: Average time in milliseconds of request handling since statsReset() called.
dispatchedTimeAverage: Average time in milliseconds of dispatch handling since statsReset() called.
responses1xx: Number of responses with a 1xx status since statsReset() called.
responses2xx: Number of responses with a 2xx status since statsReset() called.
responses3xx: Number of responses with a 3xx status since statsReset() called.

View File

@ -675,6 +675,12 @@ public class AsyncContinuation implements AsyncContext, Continuation
dispatch();
}
/* ------------------------------------------------------------ */
public Request getBaseRequest()
{
return _connection.getRequest();
}
/* ------------------------------------------------------------ */
public ServletRequest getRequest()
{

View File

@ -21,46 +21,87 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.server.AsyncContinuation;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
public class StatisticsHandler extends HandlerWrapper
{
private transient final AtomicLong _statsStartedAt = new AtomicLong();
private transient final AtomicInteger _requests = new AtomicInteger();
private transient final AtomicInteger _resumedRequests = new AtomicInteger();
private transient final AtomicInteger _expiredRequests = new AtomicInteger();
private transient final AtomicLong _requestMinTime = new AtomicLong();
private transient final AtomicLong _requestMaxTime = new AtomicLong();
private transient final AtomicLong _requestTotalTime = new AtomicLong();
private transient final AtomicLong _suspendMinTime = new AtomicLong();
private transient final AtomicLong _suspendTotalTime = new AtomicLong();
private transient final AtomicInteger _requestsActive = new AtomicInteger();
private transient final AtomicInteger _requestsMaxActive = new AtomicInteger();
private transient final AtomicInteger _responses1xx = new AtomicInteger();
private transient final AtomicInteger _responses2xx = new AtomicInteger();
private transient final AtomicInteger _responses3xx = new AtomicInteger();
private transient final AtomicInteger _responses4xx = new AtomicInteger();
private transient final AtomicInteger _responses5xx = new AtomicInteger();
private transient final AtomicLong _responsesTotalBytes = new AtomicLong();
private final AtomicLong _statsStartedAt = new AtomicLong();
private final AtomicInteger _requests = new AtomicInteger();
private final AtomicInteger _requestsActive = new AtomicInteger();
private final AtomicInteger _requestsActiveMax = new AtomicInteger();
private final AtomicLong _requestTimeMax = new AtomicLong();
private final AtomicLong _requestTimeTotal = new AtomicLong();
private final AtomicInteger _dispatched = new AtomicInteger();
private final AtomicInteger _dispatchedActive = new AtomicInteger();
private final AtomicInteger _dispatchedActiveMax = new AtomicInteger();
private final AtomicLong _dispatchedTimeMax = new AtomicLong();
private final AtomicLong _dispatchedTimeTotal = new AtomicLong();
private final AtomicInteger _suspends = new AtomicInteger();
private final AtomicInteger _suspendsActive = new AtomicInteger();
private final AtomicInteger _suspendsActiveMax = new AtomicInteger();
private final AtomicInteger _resumes = new AtomicInteger();
private final AtomicInteger _expires = new AtomicInteger();
private final AtomicInteger _responses1xx = new AtomicInteger();
private final AtomicInteger _responses2xx = new AtomicInteger();
private final AtomicInteger _responses3xx = new AtomicInteger();
private final AtomicInteger _responses4xx = new AtomicInteger();
private final AtomicInteger _responses5xx = new AtomicInteger();
private final AtomicLong _responsesTotalBytes = new AtomicLong();
private final ContinuationListener _onCompletion = new ContinuationListener()
{
public void onComplete(Continuation continuation)
{
final Request request = ((AsyncContinuation)continuation).getBaseRequest();
final long elapsed = System.currentTimeMillis()-request.getTimeStamp();
_requestsActive.decrementAndGet();
_requests.incrementAndGet();
updateMax(_requestTimeMax, elapsed);
_requestTimeTotal.addAndGet(elapsed);
updateResponse(request);
if (!continuation.isResumed())
_suspendsActive.decrementAndGet();
}
public void onTimeout(Continuation continuation)
{
_expires.incrementAndGet();
}
};
/**
* Resets the current request statistics.
*/
public void statsReset()
{
_statsStartedAt.set(System.currentTimeMillis());
_requests.set(0);
_resumedRequests.set(0);
_expiredRequests.set(0);
_requestMinTime.set(Long.MAX_VALUE);
_requestMaxTime.set(0L);
_requestTotalTime.set(0L);
_suspendMinTime.set(Long.MAX_VALUE);
_suspendTotalTime.set(0L);
_requestsActive.set(0);
_requestsMaxActive.set(0);
_requestsActiveMax.set(0);
_requestTimeMax.set(0L);
_requestTimeTotal.set(0L);
_dispatched.set(0);
_dispatchedActive.set(0);
_dispatchedActiveMax.set(0);
_dispatchedTimeMax.set(0L);
_dispatchedTimeTotal.set(0L);
_suspends.set(0);
_suspendsActive.set(0);
_suspendsActiveMax.set(0);
_resumes.set(0);
_expires.set(0);
_responses1xx.set(0);
_responses2xx.set(0);
_responses3xx.set(0);
@ -91,40 +132,26 @@ public class StatisticsHandler extends HandlerWrapper
}
}
private void updateMin(AtomicLong valueHolder, long value)
{
long oldValue = valueHolder.get();
while (value < oldValue)
{
if (valueHolder.compareAndSet(oldValue, value))
break;
oldValue = valueHolder.get();
}
}
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
_requests.incrementAndGet();
updateMax(_dispatchedActiveMax, _dispatchedActive.incrementAndGet());
int activeRequests = _requestsActive.incrementAndGet();
updateMax(_requestsMaxActive, activeRequests);
// The order of the ifs is important, as a continuation can be resumed and expired
// We test first if it's expired, and then if it's resumed
final long start;
AsyncContinuation continuation = request.getAsyncContinuation();
if (continuation.isExpired())
if (continuation.isInitial())
{
_expiredRequests.incrementAndGet();
// new request
updateMax(_requestsActiveMax, _requestsActive.incrementAndGet());
start = request.getTimeStamp();
}
else if (continuation.isResumed())
else
{
_resumedRequests.incrementAndGet();
long initialTime = request.getTimeStamp();
long suspendTime = System.currentTimeMillis() - initialTime;
updateMin(_suspendMinTime, suspendTime);
_suspendTotalTime.addAndGet(suspendTime);
// resumed request
start = System.currentTimeMillis();
_suspendsActive.decrementAndGet();
if (continuation.isResumed())
_resumes.incrementAndGet();
}
try
@ -133,23 +160,37 @@ public class StatisticsHandler extends HandlerWrapper
}
finally
{
_requestsActive.decrementAndGet();
if (!continuation.isSuspended())
final long now = System.currentTimeMillis();
final long dispatched=now-start;
_dispatchedActive.decrementAndGet();
_dispatched.incrementAndGet();
_dispatchedTimeTotal.addAndGet(dispatched);
updateMax(_dispatchedTimeMax, dispatched);
if (continuation.isSuspended())
{
if (continuation.isInitial())
continuation.addContinuationListener(_onCompletion);
_suspends.incrementAndGet();
updateMax(_suspendsActiveMax, _suspendsActive.incrementAndGet());
}
else if (continuation.isInitial())
{
_requestsActive.decrementAndGet();
_requests.incrementAndGet();
updateMax(_requestTimeMax, dispatched);
_requestTimeTotal.addAndGet(dispatched);
updateResponse(request);
}
// else onCompletion will handle it.
}
}
private void updateResponse(Request request)
{
long elapsed = System.currentTimeMillis() - request.getTimeStamp();
updateMin(_requestMinTime, elapsed);
updateMax(_requestMaxTime, elapsed);
_requestTotalTime.addAndGet(elapsed);
Response response = request.getResponse();
switch (response.getStatus() / 100)
{
@ -171,7 +212,6 @@ public class StatisticsHandler extends HandlerWrapper
default:
break;
}
_responsesTotalBytes.addAndGet(response.getContentCount());
}
@ -184,9 +224,9 @@ public class StatisticsHandler extends HandlerWrapper
/**
* @return the number of requests handled by this handler
* since {@link #statsReset()} was last called, including
* resumed requests
* @see #getRequestsResumed()
* since {@link #statsReset()} was last called, excluding
* active requests
* @see #getResumes()
*/
public int getRequests()
{
@ -208,25 +248,145 @@ public class StatisticsHandler extends HandlerWrapper
*/
public int getRequestsActiveMax()
{
return _requestsMaxActive.get();
return _requestsActiveMax.get();
}
/**
* @return the number of requests that have been resumed
* @see #getRequestsExpired()
* @return the maximum time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
*/
public int getRequestsResumed()
public long getRequestTimeMax()
{
return _resumedRequests.get();
return _requestTimeMax.get();
}
/**
* @return the total time (in milliseconds) of requests handling
* since {@link #statsReset()} was last called.
*/
public long getRequestTimeTotal()
{
return _requestTimeTotal.get();
}
/**
* @return the average time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
* @see #getRequestTimeTotal()
* @see #getRequests()
*/
public long getRequestTimeAverage()
{
int requests = getRequests();
return requests == 0 ? 0 : getRequestTimeTotal() / requests;
}
/**
* @return the number of dispatches seen by this handler
* since {@link #statsReset()} was last called, excluding
* active dispatches
*/
public int getDispatched()
{
return _dispatched.get();
}
/**
* @return the number of dispatches currently in this handler
* since {@link #statsReset()} was last called, including
* resumed requests
*/
public int getDispatchedActive()
{
return _dispatchedActive.get();
}
/**
* @return the max number of dispatches currently in this handler
* since {@link #statsReset()} was last called, including
* resumed requests
*/
public int getDispatchedActiveMax()
{
return _dispatchedActiveMax.get();
}
/**
* @return the maximum time (in milliseconds) of request dispatch
* since {@link #statsReset()} was last called.
*/
public long getDispatchedTimeMax()
{
return _dispatchedTimeMax.get();
}
/**
* @return the total time (in milliseconds) of requests handling
* since {@link #statsReset()} was last called.
*/
public long getDispatchedTimeTotal()
{
return _dispatchedTimeTotal.get();
}
/**
* @return the average time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
* @see #getRequestTimeTotal()
* @see #getRequests()
*/
public long getDispatchedTimeAverage()
{
int requests = getDispatched();
return requests == 0 ? 0 : getDispatchedTimeTotal() / requests;
}
/**
* @return the number of requests handled by this handler
* since {@link #statsReset()} was last called, including
* resumed requests
* @see #getResumes()
*/
public int getSuspends()
{
return _suspends.get();
}
/**
* @return the number of requests currently suspended.
* since {@link #statsReset()} was last called.
*/
public int getSuspendsActive()
{
return _suspendsActive.get();
}
/**
* @return the maximum number of current suspended requests
* since {@link #statsReset()} was last called.
*/
public int getSuspendsActiveMax()
{
return _suspendsActiveMax.get();
}
/**
* @return the number of requests that have been resumed
* @see #getExpires()
*/
public int getResumes()
{
return _resumes.get();
}
/**
* @return the number of requests that expired while suspended.
* @see #getRequestsResumed()
* @see #getResumes()
*/
public int getRequestsExpired()
public int getExpires()
{
return _expiredRequests.get();
return _expires.get();
}
/**
@ -281,46 +441,7 @@ public class StatisticsHandler extends HandlerWrapper
{
return System.currentTimeMillis() - _statsStartedAt.get();
}
/**
* @return the minimum time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
*/
public long getRequestTimeMin()
{
return _requestMinTime.get();
}
/**
* @return the maximum time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
*/
public long getRequestTimeMax()
{
return _requestMaxTime.get();
}
/**
* @return the total time (in milliseconds) of requests handling
* since {@link #statsReset()} was last called.
*/
public long getRequestTimeTotal()
{
return _requestTotalTime.get();
}
/**
* @return the average time (in milliseconds) of request handling
* since {@link #statsReset()} was last called.
* @see #getRequestTimeTotal()
* @see #getRequests()
*/
public long getRequestTimeAverage()
{
int requests = getRequests();
return requests == 0 ? 0 : getRequestTimeTotal() / requests;
}
/**
* @return the total bytes of content sent in responses
*/
@ -328,22 +449,45 @@ public class StatisticsHandler extends HandlerWrapper
{
return _responsesTotalBytes.get();
}
public String toStatsHTML()
{
StringBuilder sb = new StringBuilder();
/**
* @return the minimum time (in milliseconds) of request suspension
* since {@link #statsReset()} was last called.
*/
public long getSuspendedTimeMin()
{
return _suspendMinTime.get();
}
sb.append("<h1>Statistics:</h1>\n");
sb.append("Statistics gathering started ").append(getStatsOnMs()).append("ms ago").append("<br />\n");
sb.append("<h2>Requests:</h2>\n");
sb.append("Total requests: ").append(getRequests()).append("<br />\n");
sb.append("Active requests: ").append(getRequestsActive()).append("<br />\n");
sb.append("Max active requests: ").append(getRequestsActiveMax()).append("<br />\n");
sb.append("Total requests time: ").append(getRequestTimeTotal()).append("<br />\n");
sb.append("Average request time: ").append(getRequestTimeAverage()).append("<br />\n");
sb.append("Max request time: ").append(getRequestTimeMax()).append("<br />\n");
sb.append("<h2>Dispatches:</h2>\n");
sb.append("Total dispatched: ").append(getDispatched()).append("<br />\n");
sb.append("Active dispatched: ").append(getDispatchedActive()).append("<br />\n");
sb.append("Max active dispatched: ").append(getDispatchedActiveMax()).append("<br />\n");
sb.append("Total dispatched time: ").append(getDispatchedTimeTotal()).append("<br />\n");
sb.append("Average dispatched time: ").append(getDispatchedTimeAverage()).append("<br />\n");
sb.append("Max dispatched time: ").append(getDispatchedTimeMax()).append("<br />\n");
sb.append("Total requests suspended: ").append(getSuspends()).append("<br />\n");
sb.append("Total requests expired: ").append(getExpires()).append("<br />\n");
sb.append("Total requests resumed: ").append(getResumes()).append("<br />\n");
sb.append("<h2>Responses:</h2>\n");
sb.append("1xx responses: ").append(getResponses1xx()).append("<br />\n");
sb.append("2xx responses: ").append(getResponses2xx()).append("<br />\n");
sb.append("3xx responses: ").append(getResponses3xx()).append("<br />\n");
sb.append("4xx responses: ").append(getResponses4xx()).append("<br />\n");
sb.append("5xx responses: ").append(getResponses5xx()).append("<br />\n");
sb.append("Bytes sent total: ").append(getResponsesBytesTotal()).append("<br />\n");
return sb.toString();
/**
* @return the total time (in milliseconds) of request suspension
* since {@link #statsReset()} was last called.
*/
public long getSuspendedTimeTotal()
{
return _suspendTotalTime.get();
}
}

View File

@ -15,6 +15,7 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@ -25,6 +26,7 @@ import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
@ -59,139 +61,10 @@ public class StatisticsHandlerTest extends TestCase
_server.join();
}
public void testSuspendResume() throws Exception
public void testRequest() throws Exception
{
final long sleep = 500;
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
if (continuationHandle.get() == null)
{
continuation.suspend();
continuationHandle.set(continuation);
try
{
Thread.sleep(sleep);
}
catch (InterruptedException x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
boolean passed = _latchHandler.await(1000);
assertTrue(passed);
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
continuationHandle.get().resume();
passed = _latchHandler.await(1000);
assertTrue(passed);
assertEquals(2, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsResumed());
assertEquals(0, _statsHandler.getRequestsExpired());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(sleep <= _statsHandler.getSuspendedTimeMin());
assertEquals(_statsHandler.getSuspendedTimeMin(), _statsHandler.getSuspendedTimeTotal());
}
public void testSuspendExpire() throws Exception
{
final long timeout = 1000;
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
System.out.println("continuation = " + continuation);
if (continuationHandle.get() == null)
{
continuation.setTimeout(timeout);
continuation.suspend();
continuationHandle.set(continuation);
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
boolean passed = _latchHandler.await(1000);
assertTrue(passed);
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
Thread.sleep(timeout);
passed = _latchHandler.await(1000);
assertTrue(passed);
assertEquals(2, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsResumed());
assertEquals(1, _statsHandler.getRequestsExpired());
assertEquals(1, _statsHandler.getResponses2xx());
}
public void testSuspendComplete() throws Exception
{
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
if (continuationHandle.get() == null)
{
continuation.suspend();
continuationHandle.set(continuation);
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
boolean passed = _latchHandler.await(1000);
assertTrue(passed);
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
continuationHandle.get().complete();
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsResumed());
assertEquals(0, _statsHandler.getRequestsExpired());
// TODO: complete callback not implemented
// Commented to pass the tests
// assertEquals(1, _statsHandler.getResponses2xx());
}
public void testRequestTimes() throws Exception
{
final long sleep = 1000;
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
@ -199,9 +72,11 @@ public class StatisticsHandlerTest extends TestCase
request.setHandled(true);
try
{
Thread.sleep(sleep);
barrier[0].await();
barrier[1].await();
}
catch (InterruptedException x)
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
@ -213,24 +88,452 @@ public class StatisticsHandlerTest extends TestCase
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.getResponses(request);
_connector.executeRequest(request);
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getRequestsActiveMax());
assertEquals(0, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getDispatchedActiveMax());
barrier[1].await();
boolean passed = _latchHandler.await(1000);
assertTrue(passed);
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getRequestsActiveMax());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getDispatchedActiveMax());
assertEquals(0, _statsHandler.getSuspends());
assertEquals(0, _statsHandler.getResumes());
assertEquals(0, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(sleep <= _statsHandler.getRequestTimeMin());
assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeTotal());
assertEquals(_statsHandler.getRequestTimeMin(), _statsHandler.getRequestTimeAverage());
_latchHandler.reset();
barrier[0].reset();
barrier[1].reset();
_connector.executeRequest(request);
_connector.getResponses(request);
barrier[0].await();
assertEquals(1, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getRequestsActiveMax());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getDispatchedActiveMax());
barrier[1].await();
passed = _latchHandler.await(1000);
assertTrue(passed);
assertEquals(2, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getRequestsActiveMax());
assertEquals(2, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getDispatchedActiveMax());
assertEquals(0, _statsHandler.getSuspends());
assertEquals(0, _statsHandler.getResumes());
assertEquals(0, _statsHandler.getExpires());
assertEquals(2, _statsHandler.getResponses2xx());
assertTrue(sleep <= _statsHandler.getRequestTimeMin());
assertTrue(sleep <= _statsHandler.getRequestTimeAverage());
assertTrue(_statsHandler.getRequestTimeTotal() >= 2 * sleep);
_latchHandler.reset(2);
barrier[0]=new CyclicBarrier(3);
barrier[1]=new CyclicBarrier(3);
_connector.executeRequest(request);
_connector.executeRequest(request);
barrier[0].await();
assertEquals(2, _statsHandler.getRequests());
assertEquals(2, _statsHandler.getRequestsActive());
assertEquals(2, _statsHandler.getRequestsActiveMax());
assertEquals(2, _statsHandler.getDispatched());
assertEquals(2, _statsHandler.getDispatchedActive());
assertEquals(2, _statsHandler.getDispatchedActiveMax());
barrier[1].await();
passed = _latchHandler.await(1000);
assertTrue(passed);
assertEquals(4, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(2, _statsHandler.getRequestsActiveMax());
assertEquals(4, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(2, _statsHandler.getDispatchedActiveMax());
assertEquals(0, _statsHandler.getSuspends());
assertEquals(0, _statsHandler.getResumes());
assertEquals(0, _statsHandler.getExpires());
assertEquals(4, _statsHandler.getResponses2xx());
}
public void testSuspendResume() throws Exception
{
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
if (continuationHandle.get() == null)
{
continuation.suspend();
continuationHandle.set(continuation);
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
finally
{
try
{
barrier[1].await();
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(0, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await(1000));
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
Thread.sleep(10);
_latchHandler.reset();
barrier[0].reset();
barrier[1].reset();
continuationHandle.get().addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation)
{
}
public void onComplete(Continuation continuation)
{
try { barrier[2].await(); } catch(Exception e) {}
}
});
continuationHandle.get().resume();
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await(1000));
barrier[2].await();
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(2, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getSuspends());
assertEquals(1, _statsHandler.getResumes());
assertEquals(0, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(_statsHandler.getRequestTimeTotal()>=30);
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeAverage());
assertTrue(_statsHandler.getDispatchedTimeTotal()>=20);
assertTrue(_statsHandler.getDispatchedTimeAverage()+10<=_statsHandler.getDispatchedTimeTotal());
assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal());
}
public void testSuspendExpire() throws Exception
{
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
if (continuationHandle.get() == null)
{
continuation.setTimeout(10);
continuation.suspend();
continuationHandle.set(continuation);
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
finally
{
try
{
barrier[1].await();
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(0, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await(1000));
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
continuationHandle.get().addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation)
{
}
public void onComplete(Continuation continuation)
{
try { barrier[2].await(); } catch(Exception e) {}
}
});
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
_latchHandler.reset();
barrier[0].reset();
barrier[1].reset();
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await(1000));
barrier[2].await();
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(2, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getSuspends());
assertEquals(1, _statsHandler.getResumes());
assertEquals(1, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(_statsHandler.getRequestTimeTotal()>=30);
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeAverage());
assertTrue(_statsHandler.getDispatchedTimeTotal()>=20);
assertTrue(_statsHandler.getDispatchedTimeAverage()+10<=_statsHandler.getDispatchedTimeTotal());
assertTrue(_statsHandler.getDispatchedTimeMax()+10<=_statsHandler.getDispatchedTimeTotal());
}
public void testSuspendComplete() throws Exception
{
final AtomicReference<Continuation> continuationHandle = new AtomicReference<Continuation>();
final CyclicBarrier barrier[] = { new CyclicBarrier(2), new CyclicBarrier(2), new CyclicBarrier(2)};
_statsHandler.setHandler(new AbstractHandler()
{
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
request.setHandled(true);
try
{
barrier[0].await();
Thread.sleep(10);
Continuation continuation = ContinuationSupport.getContinuation(httpRequest);
if (continuationHandle.get() == null)
{
continuation.setTimeout(1000);
continuation.suspend();
continuationHandle.set(continuation);
}
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
finally
{
try
{
barrier[1].await();
}
catch (Exception x)
{
Thread.currentThread().interrupt();
throw (IOException)new IOException().initCause(x);
}
}
}
});
_server.start();
String request = "GET / HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"\r\n";
_connector.executeRequest(request);
barrier[0].await();
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(0, _statsHandler.getDispatched());
assertEquals(1, _statsHandler.getDispatchedActive());
barrier[1].await();
assertTrue(_latchHandler.await(1000));
assertNotNull(continuationHandle.get());
assertTrue(continuationHandle.get().isSuspended());
continuationHandle.get().addContinuationListener(new ContinuationListener()
{
public void onTimeout(Continuation continuation)
{
}
public void onComplete(Continuation continuation)
{
try { barrier[2].await(); } catch(Exception e) {}
}
});
assertEquals(0, _statsHandler.getRequests());
assertEquals(1, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
Thread.sleep(10);
continuationHandle.get().complete();
barrier[2].await();
assertEquals(1, _statsHandler.getRequests());
assertEquals(0, _statsHandler.getRequestsActive());
assertEquals(1, _statsHandler.getDispatched());
assertEquals(0, _statsHandler.getDispatchedActive());
assertEquals(1, _statsHandler.getSuspends());
assertEquals(0, _statsHandler.getResumes());
assertEquals(0, _statsHandler.getExpires());
assertEquals(1, _statsHandler.getResponses2xx());
assertTrue(_statsHandler.getRequestTimeTotal()>=20);
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeMax());
assertEquals(_statsHandler.getRequestTimeTotal(),_statsHandler.getRequestTimeAverage());
assertTrue(_statsHandler.getDispatchedTimeTotal()>=10);
assertTrue(_statsHandler.getDispatchedTimeTotal()<_statsHandler.getRequestTimeTotal());
assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeMax());
assertEquals(_statsHandler.getDispatchedTimeTotal(),_statsHandler.getDispatchedTimeAverage());
}
/**
* This handler is external to the statistics handler and it is used to ensure that statistics handler's
* handle() is fully executed before asserting its values in the tests, to avoid race conditions with the
@ -238,11 +541,12 @@ public class StatisticsHandlerTest extends TestCase
*/
private static class LatchHandler extends HandlerWrapper
{
private volatile CountDownLatch latch = new CountDownLatch(1);
private volatile CountDownLatch _latch = new CountDownLatch(1);
@Override
public void handle(String path, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
{
final CountDownLatch latch=_latch;
try
{
super.handle(path, request, httpRequest, httpResponse);
@ -253,11 +557,19 @@ public class StatisticsHandlerTest extends TestCase
}
}
private void reset()
{
_latch=new CountDownLatch(1);
}
private void reset(int count)
{
_latch=new CountDownLatch(count);
}
private boolean await(long ms) throws InterruptedException
{
boolean result = latch.await(ms, TimeUnit.MILLISECONDS);
latch = new CountDownLatch(1);
return result;
return _latch.await(ms, TimeUnit.MILLISECONDS);
}
}
}

View File

@ -111,17 +111,24 @@ public class StatisticsServlet extends HttpServlet
sb.append(" <requests>\n");
sb.append(" <statsOnMs>").append(_statsHandler.getStatsOnMs()).append("</statsOnMs>\n");
sb.append(" <requests>").append(_statsHandler.getRequests()).append("</requests>\n");
sb.append(" <requestsExpired>").append(_statsHandler.getRequestsExpired()).append("</requestsExpired>\n");
sb.append(" <requestsResumed>").append(_statsHandler.getRequestsResumed()).append("</requestsResumed>\n");
sb.append(" <requestsActive>").append(_statsHandler.getRequestsActive()).append("</requestsActive>\n");
sb.append(" <requestsActiveMax>").append(_statsHandler.getRequestsActiveMax()).append("</requestsActiveMax>\n");
sb.append(" <requestsTimeTotal>").append(_statsHandler.getRequestTimeTotal()).append("</requestsTimeTotal>\n");
sb.append(" <requestsTimeAverage>").append(_statsHandler.getRequestTimeAverage()).append("</requestsTimeAverage>\n");
sb.append(" <requestsTimeMin>").append(_statsHandler.getRequestTimeMin()).append("</requestsTimeMin>\n");
sb.append(" <requestsTimeMax>").append(_statsHandler.getRequestTimeMax()).append("</requestsTimeMax>\n");
sb.append(" <suspendTimeMin>").append(_statsHandler.getSuspendedTimeMin()).append("</suspendTimeMin>\n");
sb.append(" <suspendTimeTotal>").append(_statsHandler.getSuspendedTimeTotal()).append("</suspendTimeTotal>\n");
sb.append(" <dispatched>").append(_statsHandler.getDispatched()).append("</dispatched>\n");
sb.append(" <dispatchedActive>").append(_statsHandler.getDispatchedActive()).append("</dispatchedActive>\n");
sb.append(" <dispatchedActiveMax>").append(_statsHandler.getDispatchedActiveMax()).append("</dispatchedActiveMax>\n");
sb.append(" <dispatchedTimeTotal>").append(_statsHandler.getDispatchedTimeTotal()).append("</dispatchedTimeTotal>\n");
sb.append(" <dispatchedTimeAverage>").append(_statsHandler.getDispatchedTimeAverage()).append("</dispatchedTimeAverage>\n");
sb.append(" <dispatchedTimeMax>").append(_statsHandler.getDispatchedTimeMax()).append("</dispatchedTimeMax>\n");
sb.append(" <requestsSuspended>").append(_statsHandler.getSuspends()).append("</requestsSuspended>\n");
sb.append(" <requestsExpired>").append(_statsHandler.getExpires()).append("</requestsExpired>\n");
sb.append(" <requestsResumed>").append(_statsHandler.getResumes()).append("</requestsResumed>\n");
sb.append(" </requests>\n");
sb.append(" <responses>\n");
@ -173,32 +180,8 @@ public class StatisticsServlet extends HttpServlet
private void sendTextResponse(HttpServletResponse response) throws IOException
{
StringBuilder sb = new StringBuilder();
sb.append("<h1>Statistics:</h1>\n");
sb.append("<h2>Requests:</h2>\n");
sb.append("Statistics gathering started ").append(_statsHandler.getStatsOnMs()).append("ms ago").append("<br />\n");
sb.append("Total requests: ").append(_statsHandler.getRequests()).append("<br />\n");
sb.append("Total requests expired: ").append(_statsHandler.getRequestsExpired()).append("<br />\n");
sb.append("Total requests resumed: ").append(_statsHandler.getRequestsResumed()).append("<br />\n");
sb.append("Current requests active: ").append(_statsHandler.getRequestsActive()).append("<br />\n");
sb.append("Max concurrent requests active: ").append(_statsHandler.getRequestsActiveMax()).append("<br />\n");
sb.append("Total requests time: ").append(_statsHandler.getRequestTimeTotal()).append("<br />\n");
sb.append("Average request time: ").append(_statsHandler.getRequestTimeAverage()).append("<br />\n");
sb.append("Min request time: ").append(_statsHandler.getRequestTimeMin()).append("<br />\n");
sb.append("Max request time: ").append(_statsHandler.getRequestTimeMax()).append("<br />\n");
sb.append("Min suspended request time: ").append(_statsHandler.getSuspendedTimeMin()).append("<br />\n");
sb.append("Total suspended requests time: ").append(_statsHandler.getSuspendedTimeTotal()).append("<br />\n");
sb.append("<h2>Responses:</h2>\n");
sb.append("1xx responses: ").append(_statsHandler.getResponses1xx()).append("<br />\n");
sb.append("2xx responses: ").append(_statsHandler.getResponses2xx()).append("<br />\n");
sb.append("3xx responses: ").append(_statsHandler.getResponses3xx()).append("<br />\n");
sb.append("4xx responses: ").append(_statsHandler.getResponses4xx()).append("<br />\n");
sb.append("5xx responses: ").append(_statsHandler.getResponses5xx()).append("<br />\n");
sb.append("Bytes sent total: ").append(_statsHandler.getResponsesBytesTotal()).append("<br />\n");
sb.append(_statsHandler.toStatsHTML());
sb.append("<h2>Connections:</h2>\n");
for (Connector connector : _connectors)

View File

@ -65,11 +65,6 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>

View File

@ -52,6 +52,7 @@ public class Dump extends HttpServlet
{
static boolean fixed;
/* ------------------------------------------------------------ */
@Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
@ -131,26 +132,12 @@ public class Dump extends HttpServlet
{
e.printStackTrace();
}
Continuation continuation = ContinuationSupport.getContinuation(request,response);
Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.resume();
}
}).start();
}
if (request.getParameter("suspend")!=null)
{
try
{
Continuation continuation = ContinuationSupport.getContinuation(request,response);
continuation.setTimeout(Long.parseLong(request.getParameter("suspend")));
continuation.suspend();
}
catch(Exception e)
{
throw new ServletException(e);
}
}
if (request.getParameter("complete")!=null)
{
@ -183,6 +170,21 @@ public class Dump extends HttpServlet
}).start();
}
if (request.getParameter("suspend")!=null && request.getAttribute("SUSPEND")!=Boolean.TRUE)
{
request.setAttribute("SUSPEND",Boolean.TRUE);
try
{
Continuation continuation = ContinuationSupport.getContinuation(request);
continuation.setTimeout(Long.parseLong(request.getParameter("suspend")));
continuation.suspend();
continuation.undispatch();
}
catch(Exception e)
{
throw new ServletException(e);
}
}
request.setAttribute("Dump", this);
getServletContext().setAttribute("Dump",this);

View File

@ -31,7 +31,7 @@ This is a test context that serves:
<li>a <a href="dispatch">Dispatcher Servlet</a></li>
<li>a <a href="rewrite/">Request Rewrite Servlet</a></li>
<li>a <a href="google/">Transparent Proxy</a> (to www.google.com)</li>
<li>a <a href="cgi-bin/hello.sh">CGI script</a> (unix only)</li>
<li>a <a href="chat/">Comet Chat</a></li>
<li>a <a href="auth.html">Authentication</a></li>
</ul>
<p/>