最近闲来无事,对tomcat、netty以及nodejs的性能对比比较有兴趣,于是自行测试了一下。
测试环境:
测试工具:Apache JMeter2.9
测试代码:
tomcat下的jsp:
<%@page language="java" contentType="text/html; charset=GBK" %> <%@page import = "java.util.Date" %> <html> <body> hello world! <%= new Date() %> </body> </html>
netty下的server:
package com.hannah.netty; import java.net.InetSocketAddress; import java.util.concurrent.Executors; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.jboss.netty.handler.codec.http.HttpRequestDecoder; import org.jboss.netty.handler.codec.http.HttpResponseEncoder; public class AdminServer { public static void main(String[] args) { start(8080); } public static void start(int port) { // 配置服务器-使用java线程池作为解释线程 ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // 设置 pipeline factory. bootstrap.setPipelineFactory(new ServerPipelineFactory()); // 绑定端口 bootstrap.bind(new InetSocketAddress(port)); System.out.println("admin start on " + port); } private static class ServerPipelineFactory implements ChannelPipelineFactory { public ChannelPipeline getPipeline() throws Exception { // Create a default pipeline implementation. ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("encoder", new HttpResponseEncoder()); // http处理handler pipeline.addLast("handler", new AdminServerHandler()); return pipeline; } } } package com.hannah.thirdparty.netty; import java.util.Date; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.buffer.DynamicChannelBuffer; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.SimpleChannelUpstreamHandler; import org.jboss.netty.handler.codec.frame.TooLongFrameException; import org.jboss.netty.handler.codec.http.DefaultHttpResponse; import org.jboss.netty.handler.codec.http.HttpHeaders.Names; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.jboss.netty.handler.codec.http.HttpVersion; import org.jboss.netty.util.CharsetUtil; public class AdminServerHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { // HttpRequest request = (HttpRequest) e.getMessage(); // String uri = request.getUri(); // System.out.println("uri:" + uri); HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); ChannelBuffer buffer = new DynamicChannelBuffer(2048); buffer.writeBytes(("Hello World!\t" + new Date()).getBytes("UTF-8")); response.setContent(buffer); response.setHeader("Content-Type", "text/html; charset=UTF-8"); response.setHeader("Content-Length", response.getContent().writerIndex()); Channel ch = e.getChannel(); // Write the initial line and the header. ch.write(response); ch.disconnect(); ch.close(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { Channel ch = e.getChannel(); Throwable cause = e.getCause(); if (cause instanceof TooLongFrameException) { sendError(ctx, HttpResponseStatus.BAD_REQUEST); return; } cause.printStackTrace(); if (ch.isConnected()) { sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR); } } private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status); response.setHeader(Names.CONTENT_TYPE, "text/plain; charset=UTF-8"); response.setContent(ChannelBuffers.copiedBuffer("Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8)); // Close the connection as soon as the error message is sent. ctx.getChannel().write(response).addListener(ChannelFutureListener.CLOSE); } }
nodejs下的example.js:
// var url = require('url'); var http = require('http'); http.createServer(function (req, res) { // var pathname = url.parse(req.url).pathname; res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World!\t' + new Date()); }).listen(1337, '127.0.0.1'); console.log('Server running at http://127.0.0.1:1337/');
测试结果:
- 线程数:100;循环次数:100;
- 线程数:1000;循环次数:10;
- 线程数:1000;循环次数:100;
结果分析:整体上来看,并发小的时候,区别不大;当并发逐渐加大时,tomcat首先承受不住,nodejs和netty性能大致相当(netty性能略低点);最后一次请求的时候,tomcat报OutOfMemory的错误了!