Netty 比 Tomcat 慢
我们刚刚完成了构建一个将数据存储到磁盘的服务器,并在 Netty 面前进行了处理。在负载测试期间,我们看到 Netty 扩展到每秒大约 8,000 条消息。考虑到我们的系统,这看起来非常低。对于基准测试,我们编写了一个 Tomcat 前端并运行相同的负载测试。通过这些测试,我们每秒收到大约25,000条消息。
以下是我们负载试验机的规格:
- Macbook Pro Quad core
- 16GB 内存
- Java 1.6
以下是 Netty 的负载测试设置:
- 10 条主题
- 每个线程 100,000 条消息
- Netty服务器代码(非常标准) - 我们在服务器上的Netty管道有两个处理程序:FrameDecoder和SimpleChannelHandler,用于处理请求和响应。
- 客户端 JIO 使用 Commons Pool 来池化和重用连接(池的大小与线程数相同)
以下是 Tomcat 的负载测试设置:
- 10 条主题
- 每个线程 100,000 条消息
- Tomcat 7.0.16,使用默认配置使用 Servlet 调用服务器代码
- 客户端使用 URL 连接而不进行任何池化
我的主要问题是,为什么在性能上有如此巨大的差异?关于Netty,有没有明显的东西可以让它比Tomcat跑得更快?
编辑:这是Netty服务器的主要代码:
NioServerSocketChannelFactory factory = new NioServerSocketChannelFactory();
ServerBootstrap server = new ServerBootstrap(factory);
server.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
RequestDecoder decoder = injector.getInstance(RequestDecoder.class);
ContentStoreChannelHandler handler = injector.getInstance(ContentStoreChannelHandler.class);
return Channels.pipeline(decoder, handler);
}
});
server.setOption("child.tcpNoDelay", true);
server.setOption("child.keepAlive", true);
Channel channel = server.bind(new InetSocketAddress(port));
allChannels.add(channel);
我们的处理程序如下所示:
public class RequestDecoder extends FrameDecoder {
@Override
protected ChannelBuffer decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) {
if (buffer.readableBytes() < 4) {
return null;
}
buffer.markReaderIndex();
int length = buffer.readInt();
if (buffer.readableBytes() < length) {
buffer.resetReaderIndex();
return null;
}
return buffer;
}
}
public class ContentStoreChannelHandler extends SimpleChannelHandler {
private final RequestHandler handler;
@Inject
public ContentStoreChannelHandler(RequestHandler handler) {
this.handler = handler;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer in = (ChannelBuffer) e.getMessage();
in.readerIndex(4);
ChannelBuffer out = ChannelBuffers.dynamicBuffer(512);
out.writerIndex(8); // Skip the length and status code
boolean success = handler.handle(new ChannelBufferInputStream(in), new ChannelBufferOutputStream(out), new NettyErrorStream(out));
if (success) {
out.setInt(0, out.writerIndex() - 8); // length
out.setInt(4, 0); // Status
}
Channels.write(e.getChannel(), out, e.getRemoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
Throwable throwable = e.getCause();
ChannelBuffer out = ChannelBuffers.dynamicBuffer(8);
out.writeInt(0); // Length
out.writeInt(Errors.generalException.getCode()); // status
Channels.write(ctx, e.getFuture(), out);
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
NettyContentStoreServer.allChannels.add(e.getChannel());
}
}
更新:
我已经设法将我的Netty解决方案提高到4,000 /秒。几周前,我正在测试连接池中的客户端PING,作为对空闲套接字的安全防护,但在开始负载测试之前,我忘记了删除该代码。每次从池中签出套接字(使用共享资源池)时,此代码都会有效地对服务器进行PING。我注释掉了该代码,现在使用Netty可以获得21,000 /秒,使用Tomcat可以获得25,000 /秒。
虽然,这对Netty来说是个好消息,但我仍然比Tomcat少4,000 /秒。如果有人有兴趣看到这一点,我可以发布我的客户端(我以为我已经排除了,但显然不是)。