2

我正在尝试使 HTTP 传输编码块与 Netty 4.0 一起工作。

到目前为止,我取得了成功。它适用于小型有效载荷。

然后我尝试使用大数据,它开始挂起。

我怀疑我的代码可能有问题,或者可能是 ByteBuf.copy() 泄漏。

我将我的代码精简到最低限度,以确保我没有其他泄漏源或副作用,并且我已经结束编写这个测试。完整的代码在这里。

基本上,当您使用 wget 连接到端口 8888 时,它会发送 1GB 的 0x0。我在连接时重现了该问题

wget http://127.0.0.1:8888 -O /dev/null

这是处理程序:

    protected void channelRead0(ChannelHandlerContext ctx, FullHttpMessage msg) throws Exception {
        DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        HttpHeaders.setTransferEncodingChunked(response);
        response.headers().set(CONTENT_TYPE, "application/octet-stream");
        ctx.write(response);

        ByteBuf buf = Unpooled.buffer();
        int GIGABYTE = (4 * 1024 * 1024); // multiply 256B = 1GB
        for (int i = 0; i < GIGABYTE; i++) {
            buf.writeBytes(CONTENT_256BYTES_ZEROED);
            ctx.writeAndFlush(new DefaultHttpContent(buf.copy()));
            buf.clear();
           }
           ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
    }

我的方法有什么问题吗?

编辑 :

使用 VisualVM,我发现ChannelOutboundBuffer.

Entry[] buffer不断增长,被 addCapacity()多次调用。该Entry数组似乎包含已(或应该)写入线路的缓冲区的副本。

我看到有wireshark数据进来......

这是堆转储的 Dropbox 链接

4

1 回答 1

0

我发现我做错了什么。

for循环不正常,writeAndFlush()很可能是泄漏的原因。

我尝试了各种方法(请参阅要点链接中的许多修订)。请参阅撰写本文时的要点版本。

我发现在没有内存泄漏的情况下实现我想要做的最好的方法是扩展 InputStream 并写入上下文(不使用writeAndFlush())包装在io.netty.handler.stream.ChunkedStream.

    DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
    HttpHeaders.setTransferEncodingChunked(response);
    response.headers().set(CONTENT_TYPE, "application/octet-stream");
    ctx.write(response);
    InputStream is = new InputStream() {
      int offset = -1;
      byte[] buffer = null;


      @Override
      public int read() throws IOException {
          if (offset == -1 || (buffer != null && offset == buffer.length)) {
            fillBuffer();
          }
          if (buffer == null || offset == -1) {
            return -1;
          }
          while (offset < buffer.length) {
            int b = buffer[offset];
            offset++;
            return b;
          }
          return -1;
      }

      // this method simulates an application that would write to
      // the buffer.

      // ONE GB (max size for the test;
      int sz = 1024 * 1024 * 1024; 

      private void fillBuffer() {
        offset = 0;
        if (sz <= 0) { // LIMIT TO ONE GB
            buffer = null;
            return;
        }
        buffer = new byte[1024];
        System.arraycopy(CONTENT_1KB_ZEROED, 0,
                buffer, 0,
                CONTENT_1KB_ZEROED.length);
        sz -= 1024;
      }
};


ctx.write(new ChunkedStream(new BufferedInputStream(is), 8192));
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);

该代码以 8K 块向客户端写入 1GB 数据。我能够同时运行 30 个连接而没有内存或挂起问题。

于 2013-09-17T11:55:08.823 回答