jetty-9 - HTTP client: load test.

This commit is contained in:
Simone Bordet 2012-09-17 11:45:44 +02:00
parent fdd65fb2ea
commit e7db1661c6
1 changed files with 198 additions and 0 deletions

View File

@ -0,0 +1,198 @@
//
// ========================================================================
// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
import org.eclipse.jetty.toolchain.test.annotation.Stress;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.junit.Assert;
import org.junit.Test;
public class HttpClientLoadTest extends AbstractHttpClientServerTest
{
private final Logger logger = Log.getLogger(HttpClientLoadTest.class);
public HttpClientLoadTest(SslContextFactory sslContextFactory)
{
super(sslContextFactory);
}
@Stress("High I/O, High CPU")
@Slow
@Test
public void testIterative() throws Exception
{
start(new LoadHandler());
client.setMaxConnectionsPerAddress(32768);
client.setMaxQueueSizePerAddress(1024 * 1024);
Random random = new Random();
int iterations = 1000;
CountDownLatch latch = new CountDownLatch(iterations);
List<String> failures = new ArrayList<>();
// Dumps the state of the client if the test takes too long
final Thread testThread = Thread.currentThread();
client.getScheduler().schedule(new Runnable()
{
@Override
public void run()
{
for (String host : Arrays.asList("localhost", "127.0.0.1"))
{
HttpDestination destination = (HttpDestination)client.getDestination(scheme, host, connector.getLocalPort());
for (Connection connection : new ArrayList<>(destination.getActiveConnections()))
{
HttpConnection active = (HttpConnection)connection;
logger.warn(active.getEndPoint() + " exchange " + active.getExchange());
}
}
testThread.interrupt();
}
}, iterations * ("http".equalsIgnoreCase(scheme) ? 10 : 100), TimeUnit.MILLISECONDS);
long begin = System.nanoTime();
for (int i = 0; i < iterations; ++i)
{
test(random, latch, failures);
}
Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS));
long end = System.nanoTime();
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
logger.info("{} requests in {} ms, {} req/s", iterations, elapsed, elapsed > 0 ? iterations * 1000 / elapsed : -1);
Assert.assertTrue(failures.toString(), failures.isEmpty());
}
private void test(Random random, final CountDownLatch latch, final List<String> failures)
{
int maxContentLength = 64 * 1024;
// Choose a random destination
String host = random.nextBoolean() ? "localhost" : "127.0.0.1";
Request request = client.newRequest(host, connector.getLocalPort()).scheme(scheme);
// Choose a random method
HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST;
request.method(method);
// Choose randomly whether to close the connection on the client or on the server
if (random.nextBoolean())
request.header("Connection", "close");
else if (random.nextBoolean())
request.header("X-Close", "true");
switch (method)
{
case GET:
// Randomly ask the server to download data upon this GET request
if (random.nextBoolean())
request.header("X-Download", String.valueOf(random.nextInt(maxContentLength) + 1));
break;
case POST:
int contentLength = random.nextInt(maxContentLength) + 1;
request.header("X-Upload", String.valueOf(contentLength));
request.content(new BytesContentProvider(new byte[contentLength]));
break;
}
request.send(new Response.Listener.Empty()
{
private final AtomicInteger contentLength = new AtomicInteger();
@Override
public void onHeaders(Response response)
{
String content = response.headers().get("X-Content");
if (content != null)
contentLength.set(Integer.parseInt(content));
}
@Override
public void onContent(Response response, ByteBuffer content)
{
contentLength.addAndGet(-content.remaining());
}
@Override
public void onComplete(Result result)
{
if (result.isFailed())
failures.add("Result failed " + result);
if (contentLength.get() != 0)
failures.add("Content length mismatch " + contentLength);
latch.countDown();
}
});
}
private class LoadHandler extends AbstractHandler
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
String method = request.getMethod().toUpperCase();
switch (method)
{
case "GET":
int contentLength = request.getIntHeader("X-Download");
if (contentLength > 0)
{
response.setHeader("X-Content", String.valueOf(contentLength));
response.getOutputStream().write(new byte[contentLength]);
}
break;
case "POST":
response.setHeader("X-Content", request.getHeader("X-Upload"));
IO.copy(request.getInputStream(), response.getOutputStream());
break;
}
if (Boolean.parseBoolean(request.getHeader("X-Close")))
response.setHeader("Connection", "close");
baseRequest.setHandled(true);
}
}
}