小弟有点菜, 测试不科学的地方敬请指教。
环境:
16G内存, 8核双线程CPU, cenos6, jmeter, netty5, tomcat6, JDK1.7-64
1 netty的源码:example下的hello world
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class HttpHelloWorldServer { private final int port; public HttpHelloWorldServer(int port) { this.port = port; } public void run() throws Exception { // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(50); try { ServerBootstrap b = new ServerBootstrap(); b.option(ChannelOption.SO_BACKLOG, 1024); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new HttpHelloWorldServerInitializer()); Channel ch = b.bind(port).sync().channel(); System.out.println(" connection is ok"); ch.closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8125; } new HttpHelloWorldServer(port).run(); } }
HttpHelloWorldServerHandler:
import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpRequest; import static io.netty.handler.codec.http.HttpHeaders.Names.*; import static io.netty.handler.codec.http.HttpHeaders.*; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.*; public class HttpHelloWorldServerHandler extends ChannelHandlerAdapter { private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' }; @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof HttpRequest) { HttpRequest req = (HttpRequest) msg; if (is100ContinueExpected(req)) { ctx.write(new DefaultFullHttpResponse(HTTP_1_1, CONTINUE)); } boolean keepAlive = isKeepAlive(req); FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(CONTENT)); response.headers().set(CONTENT_TYPE, "text/plain"); response.headers().set(CONTENT_LENGTH, response.content().readableBytes()); if (!keepAlive) { ctx.write(response).addListener(ChannelFutureListener.CLOSE); } else { response.headers().set(CONNECTION, Values.KEEP_ALIVE); ctx.write(response); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
HttpHelloWorldServerInitializer:
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; public class HttpHelloWorldServerInitializer extends ChannelInitializer<SocketChannel> { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); // Uncomment the following line if you want HTTPS //SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); //engine.setUseClientMode(false); //p.addLast("ssl", new SslHandler(engine)); p.addLast("codec", new HttpServerCodec()); p.addLast("handler", new HttpHelloWorldServerHandler()); } }
2 tomcat的代码, 就一个sevlet
package testTomcatHttp; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestServlet extends HttpServlet { /** * */ private static final long serialVersionUID = 1L; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // TODO Auto-generated method stub PrintWriter printWriter = resp.getWriter(); printWriter.println("<h1>Hello World!</h1>"); } }
web.xml
<servlet> <servlet-name>hello</servlet-name> <servlet-class>testTomcatHttp.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>hello</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping>
测试备注:
1) 由于tomcat6中, 有个bug, acceptor参数设置无效,只能是1, 所以我们只改变maxThreads的值;
2) tomcat和netty的jvm参数配置为: -server -Xms2048m -Xmx3098m -Xloggc:/root/nettyhttp/gc.log
3) jMeter循环次数都是1000次,sleep 1ms
测试用例:
(1) netty : acceptor=1, I/O Threads=50, jMeter的用户线程数分别为100和50
(2) tomcat nio: acceptor=1, maxThreads=50, jMeter的用户线程数分别为100和50
(3) tomcat bio: acceptor=1, maxThreads=50, jMeter的用户线程数分别为100和50
备注: 不稳定,第一次测试500多, 第二次700多, 且刚开始的Throughput很高, 但是越后越慢, 且开始error
(4) 在tomcat bio模式下, 把maxThreads=16 netty I/OThreads默认, 在本机上也是16,
测试的结果, tomcat只有300多, 且有报错, 而netty的测试结果800上下,且没有报错。
所以, 根据测试结果可以推断出,
a netty+http在性能上和可靠性和还是不错的, 且可以nginx搭配使用。
b tomcat, nio模式还可以, 但是bio模式,测试结果不怎么好。
且,
netty + http 定制不用受web容器的限制, IO线程对于执行时间比较长的task, 可以异步交给现场池, 猜测tomcat的nio模式中, actiom-server中也可以这样做。但是tomcat, 别人都封装好了, 不会出现大问题。
具体使用哪个, 根据场景进行选择。
欢迎指教!