Skip to content

Instantly share code, notes, and snippets.

@isopov
Created July 16, 2011 20:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save isopov/1086777 to your computer and use it in GitHub Desktop.
Save isopov/1086777 to your computer and use it in GitHub Desktop.
Simple bench comparing creating new connection per each HttpRequest and using one keep-alive connection (Based on Netty)
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.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
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.NioClientSocketChannelFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
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.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 KeepAliveLocalhostTest {
private static final int KEEP_ALIVE_PORT = 10000;
private static final int NO_KEEP_ALIVE_PORT = 10001;
private static final SocketAddress KEEP_ALIVE_ADRESS = new InetSocketAddress("127.0.0.1", KEEP_ALIVE_PORT);
private static final SocketAddress NO_KEEP_ALIVE_ADRESS = new InetSocketAddress("127.0.0.1", NO_KEEP_ALIVE_PORT);
private static final int NUM_MESSAGES = 1000;
private static final int NUM_ITERATIONS = 100;
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));
//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);
keepAliveTime += testCommunication(keepAliveClient, keepAliveResponceRecieved, true, NUM_MESSAGES);
} else {
keepAliveTime += testCommunication(keepAliveClient, keepAliveResponceRecieved, true, NUM_MESSAGES);
noKeepAliveTime += testCommunication(noKeepAliveClient, noKeepAliveResponceRecieved, false,
NUM_MESSAGES);
}
}
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 and servers
keepAliveClient.releaseExternalResources();
keepAliveServer.releaseExternalResources();
noKeepAliveClient.releaseExternalResources();
noKeepAliveServer.releaseExternalResources();
}
private static long testCommunication(ClientBootstrap client, AtomicBoolean responceRecieved, boolean keepAlive,
int numMessages) {
long start = System.nanoTime();
Channel channel = null;
if (keepAlive) {
channel = client.connect(KEEP_ALIVE_ADRESS).awaitUninterruptibly().getChannel();
}
for (int i = 0; i < numMessages; i++) {
if (!keepAlive) {
channel = client.connect(NO_KEEP_ALIVE_ADRESS).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 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 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 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;
}
}
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);
}
}
}
@isopov
Copy link
Author

isopov commented Jul 16, 2011

Output is:
keep alive - 77
no keep alive - 196

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