小弟有点菜, 测试不科学的地方敬请指教。
环境:
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, 别人都封装好了, 不会出现大问题。
具体使用哪个, 根据场景进行选择。
欢迎指教!