java - Netty 比 Tomcat 慢

标签 java tomcat io netty

我们刚刚构建了一个服务器来将数据存储到磁盘并使用 Netty 作为前端。在负载测试期间,我们看到 Netty 扩展到每秒大约 8,000 条消息。鉴于我们的系统,这看起来非常低。对于基准测试,我们编写了一个 Tomcat 前端并运行相同的负载测试。通过这些测试,我们每秒收到大约 25,000 条消息。

以下是我们的负载测试机的规范:

  • Macbook Pro 四核
  • 16GB 内存
  • Java 1.6

这是 Netty 的负载测试设置:

  • 10 个线程
  • 每个线程 100,000 条消息
  • Netty 服务器代码(相当标准) - 我们在服务器上的 Netty 管道是两个处理程序:一个 FrameDecoder 和一个处理请求和响应的 SimpleChannelHandler。
  • 客户端 JIO 使用 Commons Pool 来池化和重用连接(池的大小与线程数相同)

这是 Tomcat 的负载测试设置:

  • 10 个线程
  • 每个线程 100,000 条消息
  • Tomcat 7.0.16,默认配置使用 Servlet 调用服务器代码
  • 客户端使用没有任何池的 URLConnection

我的主要问题是,为什么会有如此巨大的性能差异? 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,作为对空闲套接字的安全防护,但在开始负载测试之前我忘记删除该代码。每次从池中 check out Socket(使用 Commons Pool)时,此代码都会有效地 PING 服务器。我将这段代码注释掉了,现在 Netty 的速度为 21,000/秒,Tomcat 的速度为 25,000/秒。

尽管这在 Netty 方面是个好消息,但 Netty 的速度仍然比 Tomcat 少 4,000/秒。如果有人有兴趣看到,我可以发布我的客户端(我以为我已经排除但显然没有)。

最佳答案

messageReceived 方法是使用可能被 RequestHandler#handle 阻塞的工作线程执行的,该线程可能正忙于做一些 I/O 工作。 您可以尝试将 OrderdMemoryAwareThreadPoolExecutor(推荐)添加到 channel 管道中以执行处理程序,或者尝试将处理程序工作分派(dispatch)到新的 ThreadPoolExecutor并传递对套接字 channel 的引用,以便稍后将响应写回客户端。例如:

@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {   

    executor.submit(new Runnable() {
        processHandlerAndRespond(e);        
    });
}

private void processHandlerAndRespond(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());
} 

关于java - Netty 比 Tomcat 慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12806678/

相关文章:

file - 尝试在 HD 上写入文件时得到 "(Read-only file system)"

java - 如何计算每个单词的字母

java - Cloud Firestore - 动态查询

java - 将输出写入不同的目录

java - Flink 从 cassandra 获取数据用作数据集时出现问题

java - 在本地设置中通过 JConsole 监控 Tomcat

maven - 跳过聚合 Maven POM 中某些模块的部署

session - 条件 tomcat 粘性 session (集群)

使用文件系统将java图像输出到zip文件为空

java - 扫描仪在使用 next() 或 nextFoo() 后跳过 nextLine()?