Skip to content

Instantly share code, notes, and snippets.

@isopov
Created July 17, 2011 09:48
Show Gist options
  • Save isopov/1087405 to your computer and use it in GitHub Desktop.
Save isopov/1087405 to your computer and use it in GitHub Desktop.
Simple bench comparing creating new connection per each HttpRequest and using one keep-alive (using two different computers)
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.Charset;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpVersion;
public class KeepAliveTestClient {
private static final int KEEP_ALIVE_PORT = 10000;
private static final int NO_KEEP_ALIVE_PORT = 10001;
private static final int NUM_MESSAGES = 1000;
private static final int NUM_ITERATIONS = 100;
public static void main(String[] args) {
if (args.length != 1) {
throw new RuntimeException("specify IP for server");
}
String address = args[0];
final SocketAddress keepAliveAddress = new InetSocketAddress(address, KEEP_ALIVE_PORT);
final SocketAddress noKeepAliveAddress = new InetSocketAddress(address, NO_KEEP_ALIVE_PORT);
//Clients
ClientBootstrap keepAliveClient = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
AtomicBoolean keepAliveResponceRecieved = new AtomicBoolean(false);
keepAliveClient.setPipelineFactory(new KeepAliveClientPipelineFactory(keepAliveResponceRecieved));
ClientBootstrap noKeepAliveClient = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
AtomicBoolean noKeepAliveResponceRecieved = new AtomicBoolean(false);
noKeepAliveClient.setPipelineFactory(new NoKeepAliveClientPipelineFactory(noKeepAliveResponceRecieved));
//Benching
long keepAliveTime = 0L, noKeepAliveTime = 0L;
Random dice = new Random();
for (int i = 0; i < NUM_ITERATIONS; i++) {
if (dice.nextBoolean()) {
noKeepAliveTime += testCommunication(noKeepAliveClient, noKeepAliveResponceRecieved, false,
NUM_MESSAGES, noKeepAliveAddress);
keepAliveTime += testCommunication(keepAliveClient, keepAliveResponceRecieved, true, NUM_MESSAGES,
keepAliveAddress);
} else {
keepAliveTime += testCommunication(keepAliveClient, keepAliveResponceRecieved, true, NUM_MESSAGES,
keepAliveAddress);
noKeepAliveTime += testCommunication(noKeepAliveClient, noKeepAliveResponceRecieved, false,
NUM_MESSAGES, noKeepAliveAddress);
}
}
System.out.println("keep alive - " + TimeUnit.MICROSECONDS.convert(keepAliveTime, TimeUnit.NANOSECONDS)
/ NUM_ITERATIONS / NUM_MESSAGES);
System.out.println("no keep alive - " + TimeUnit.MICROSECONDS.convert(noKeepAliveTime, TimeUnit.NANOSECONDS)
/ NUM_ITERATIONS / NUM_MESSAGES);
//closing clients
keepAliveClient.releaseExternalResources();
noKeepAliveClient.releaseExternalResources();
}
private static long testCommunication(ClientBootstrap client, AtomicBoolean responceRecieved, boolean keepAlive,
int numMessages, SocketAddress address) {
long start = System.nanoTime();
Channel channel = null;
if (keepAlive) {
channel = client.connect(address).awaitUninterruptibly().getChannel();
}
for (int i = 0; i < numMessages; i++) {
if (!keepAlive) {
channel = client.connect(address).awaitUninterruptibly().getChannel();
}
responceRecieved.set(false);
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "test");
ChannelBuffer buffer = ChannelBuffers.copiedBuffer("ping", Charset.defaultCharset());
request.addHeader(HttpHeaders.Names.CONTENT_LENGTH, buffer.readableBytes());
request.setContent(buffer);
if (keepAlive) {
HttpHeaders.setKeepAlive(request, true);
}
channel.write(request);
while (!responceRecieved.get()) {
//wait
}
}
if (keepAlive) {
channel.close().awaitUninterruptibly();
}
return System.nanoTime() - start;
}
private static class NoKeepAliveClientPipelineFactory implements ChannelPipelineFactory {
private final AtomicBoolean responseRecieved;
public NoKeepAliveClientPipelineFactory(AtomicBoolean responseRecieved) {
this.responseRecieved = responseRecieved;
}
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("codec", new HttpClientCodec());
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("handler", new NoKeepAliveResponseHandler(responseRecieved));
return pipeline;
}
}
private static class NoKeepAliveResponseHandler extends SimpleChannelUpstreamHandler {
private final AtomicBoolean responseRecieved;
public NoKeepAliveResponseHandler(AtomicBoolean responseRecieved) {
this.responseRecieved = responseRecieved;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
responseRecieved.set(true);
}
}
private static class KeepAliveClientPipelineFactory implements ChannelPipelineFactory {
private final AtomicBoolean responseRecieved;
public KeepAliveClientPipelineFactory(AtomicBoolean responseRecieved) {
this.responseRecieved = responseRecieved;
}
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("codec", new HttpClientCodec());
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("handler", new KeepAliveResponseHandler(responseRecieved));
return pipeline;
}
}
private static class KeepAliveResponseHandler extends SimpleChannelUpstreamHandler {
private final AtomicBoolean responseRecieved;
public KeepAliveResponseHandler(AtomicBoolean responseRecieved) {
this.responseRecieved = responseRecieved;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
responseRecieved.set(true);
}
}
}
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.util.CharsetUtil;
public class KeepAliveTestServer {
private static final int KEEP_ALIVE_PORT = 10000;
private static final int NO_KEEP_ALIVE_PORT = 10001;
public static void main(String[] args) {
//Servers
ServerBootstrap keepAliveServer = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
keepAliveServer.setPipelineFactory(new KeepAliveServerPipelineFactory());
keepAliveServer.bind(new InetSocketAddress(KEEP_ALIVE_PORT));
ServerBootstrap noKeepAliveServer = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
noKeepAliveServer.setPipelineFactory(new NoKeepAliveServerPipelineFactory());
noKeepAliveServer.bind(new InetSocketAddress(NO_KEEP_ALIVE_PORT));
//server will not be closed - kill it
}
private static class NoKeepAliveRequestHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
//Writing response, wait till it is completely written and close channel after that
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
ChannelBuffer buffer = ChannelBuffers.copiedBuffer("pong", CharsetUtil.UTF_8);
response.setContent(buffer);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
response.addHeader(HttpHeaders.Names.CONTENT_LENGTH, buffer.readableBytes());
e.getChannel().write(response).addListener(ChannelFutureListener.CLOSE);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
e.getCause().printStackTrace();
super.exceptionCaught(ctx, e);
}
}
private static class NoKeepAliveServerPipelineFactory implements ChannelPipelineFactory {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("handler", new NoKeepAliveRequestHandler());
return pipeline;
}
}
private static class KeepAliveRequestHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
//Writing response, do not close it. There will be another in the same socket
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
ChannelBuffer buffer = ChannelBuffers.copiedBuffer("pong", CharsetUtil.UTF_8);
response.setContent(buffer);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=UTF-8");
response.addHeader(HttpHeaders.Names.CONTENT_LENGTH, buffer.readableBytes());
HttpHeaders.setKeepAlive(response, true);
e.getChannel().write(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
e.getCause().printStackTrace();
super.exceptionCaught(ctx, e);
}
}
private static class KeepAliveServerPipelineFactory implements ChannelPipelineFactory {
@Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("handler", new KeepAliveRequestHandler());
return pipeline;
}
}
}
@isopov
Copy link
Author

isopov commented Jul 17, 2011

Result is unexpected:

keep alive - 301
no keep alive - 463

It appeared that difference between creating new and reusing existent connection on the network is less than with both client and server on localhost. The result is given in how many microseconds it is needed to send request and receive response to it. In first case one connection was used for every 1000 such requests/responses, in the second case - new connection was created for each request/response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment