Created
July 16, 2011 20:52
-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Output is:
keep alive - 77
no keep alive - 196