Created
July 17, 2011 09:48
-
-
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)
This file contains 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.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); | |
} | |
} | |
} |
This file contains 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.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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.