суббота, 21 января 2012 г.

NodeJS vs Netty

Bencmarked following two Hello World async HTTP servers

It takes some time for Netty to warm up, but it is able to handle something like 40% more requests per sec then NodeJS.

Apache Bench command line

ab -r30000 -c100 http://localhost:<port>/

NodeJS

var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');
view raw script.js hosted with ❤ by GitHub

Netty

apply plugin: "java"
apply plugin: "eclipse"
repositories {
mavenCentral()
}
dependencies {
compile (
"org.jboss.netty:netty:latest.integration",
"com.google.inject:guice:latest.integration",
"com.google.guava:guava:latest.integration"
)
}
view raw build.gradle hosted with ❤ by GitHub
package netty;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.google.inject.Singleton;
public class NettyModule extends AbstractModule {
@Override
protected void configure() {
}
@Provides
SocketAddress provideSocketAddress() {
return new InetSocketAddress(8080);
}
@Provides @Singleton
ChannelGroup provideChannelGroup() {
return new DefaultChannelGroup("http-server");
}
@Provides
ChannelFactory provideChannelFactory() {
return new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new NettyModule());
final NettyServer server = injector.getInstance(NettyServer.class);
server.startAndWait();
Runtime.getRuntime().addShutdownHook(new Thread(){
@Override
public void run() {
server.stopAndWait();
}
});
}
}
package netty;
import java.net.SocketAddress;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelFuture;
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.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpServerCodec;
import org.jboss.netty.handler.codec.http.HttpVersion;
import com.google.common.base.Charsets;
import com.google.common.util.concurrent.AbstractIdleService;
import com.google.inject.Inject;
import com.google.inject.Provider;
class NettyServer extends AbstractIdleService {
private final ChannelGroup allChannels;
private final SocketAddress address;
private final ChannelFactory factory;
private final ServerBootstrap bootstrap;
private final Provider<Handler> handler;
@Inject
NettyServer(ChannelFactory factory, ChannelGroup allChannels, SocketAddress address, Provider<Handler> handler) {
this.factory = factory;
this.bootstrap = new ServerBootstrap(factory);
this.allChannels = allChannels;
this.address = address;
this.handler = handler;
}
@Override
protected void startUp() throws Exception {
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new HttpServerCodec(), handler.get());
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
Channel channel = bootstrap.bind(address);
allChannels.add(channel);
}
@Override
protected void shutDown() throws Exception {
allChannels.close().awaitUninterruptibly();
factory.releaseExternalResources();
}
}
class Handler extends SimpleChannelHandler {
private final ChannelGroup allChannels;
@Inject
Handler(ChannelGroup allChannels) {
this.allChannels = allChannels;
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
allChannels.add(e.getChannel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
e.getCause().printStackTrace();
e.getChannel().close();
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.setContent(ChannelBuffers.copiedBuffer("Hello, world!", Charsets.UTF_8));
e.getChannel().write(response).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
future.getChannel().close();
}});
}
}

2 комментария:

  1. Typical php-codemonkey would simply stop reading after the first snippet, silently committing to 40% worse performance - 6 loc vs 100+ loc... :-)

    ОтветитьУдалить
  2. Typical java-hater would say too much code for this simple opperation (p.s: look at playframework)

    ОтветитьУдалить