簡體   English   中英

Netty分塊輸入流

[英]Netty chunked input stream

我已經看到很多關於netty中的分塊流的問題,但是大多數是關於出站流的解決方案,而不是入站流。

我想了解如何從通道獲取數據並將其作為InputStream發送到我的業務邏輯,而不首先將所有數據加載到內存中。 這就是我想要做的事情:

public class ServerRequestHandler extends MessageToMessageDecoder<HttpObject> {

  private HttpServletRequest request;
  private PipedOutputStream os;
  private PipedInputStream is;

  @Override
  public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    super.handlerAdded(ctx);
    this.os = new PipedOutputStream();
    this.is = new PipedInputStream(os);
  }

  @Override
  public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    super.handlerRemoved(ctx);
    this.os.close();
    this.is.close();
  }

  @Override
  protected void decode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out)
      throws Exception {
    if (msg instanceof HttpRequest) {
      this.request = new CustomHttpRequest((HttpRequest) msg, this.is);
      out.add(this.request);
    }
    if (msg instanceof HttpContent) {
      ByteBuf body = ((HttpContent) msg).content();

      if (body.readableBytes() > 0)
        body.readBytes(os, body.readableBytes());

      if (msg instanceof LastHttpContent) {
        os.close();
      }
    }

  }

}

然后我有另一個Handler將獲取我的CustomHttpRequest並發送到我稱之為ServiceHandler的地方,我的業務邏輯將從InputStream中讀取。

public class ServiceRouterHandler extends SimpleChannelInboundHandler<CustomHttpRequest> {
...
    @Override
    public void channelRead0(ChannelHandlerContext ctx, CustomHttpRequest request) throws IOException {
...
        future = serviceHandler.handle(request, response);
...

這不起作用,因為當我的Handler將CustomHttpRequest轉發給ServiceHandler,並且它嘗試從InputStream讀取時,線程阻塞,並且HttpContent永遠不會在我的Decoder中處理。

我知道我可以嘗試為我的業務邏輯創建一個單獨的線程,但我的印象是我在這里過於復雜。
我查看了ByteBufInputStream,但它說的是

請注意,它只讀取構建時確定的可讀字節數。

所以我認為它不適用於Chunked Http請求。 另外,我看到了ChunkedWriteHandler,這對於Oubound塊來說似乎很好,但我找不到像ChunkedReadHandler那樣的東西......

所以我的問題是:最好的方法是什么? 我的要求是:

- 在發送ServiceHandler之前,不要將數據保存在內存中;
- ServiceHandlers API應該是netty不可知的(這就是我使用CustomHttpRequest而不是Netty的HttpRequest的原因);

更新我在CustomHttpRequest上使用更具反應性的方法使其工作。 現在,請求沒有向ServiceHandlers提供一個InputStream,因此它們可以讀取(阻塞),而CustomHttpRequest現在有一個返回Future的readInto(OutputStream)方法,所有的服務處理程序只會在執行時執行此Outputstream已滿。 這是它的樣子

public class CustomHttpRequest {
  ...constructors and other methods hidden...
  private final SettableFuture<Void> writeCompleteFuture = SettableFuture.create();

  private final SettableFuture<OutputStream> outputStreamFuture = SettableFuture.create();

  private ListenableFuture<Void> lastWriteFuture = Futures.transform(outputStreamFuture, x-> null);

  public ListenableFuture<Void> readInto(OutputStream os) throws IOException {
    outputStreamFuture.set(os);
    return this.writeCompleteFuture;
  }

  ListenableFuture<Void> writeChunk(byte[] buf) {
    this.lastWriteFuture = Futures.transform(lastWriteFuture, (AsyncFunction<Void, Void>) (os) -> {
      outputStreamFuture.get().write(buf);
      return Futures.immediateFuture(null);
    });
    return lastWriteFuture;
  }


  void complete() {
    ListenableFuture<Void> future =
        Futures.transform(lastWriteFuture, (AsyncFunction<Void, Void>) x -> {
          outputStreamFuture.get().close();
          return Futures.immediateFuture(null);
        });
    addFinallyCallback(future, () -> {
      this.writeCompleteFuture.set(null);
    });

  }
}

我更新的ServletRequestHandler看起來像這樣:

public class ServerRequestHandler extends MessageToMessageDecoder<HttpObject> {

  private NettyHttpServletRequestAdaptor request;

  @Override
  public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    super.handlerAdded(ctx);
  }

  @Override
  public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    super.handlerRemoved(ctx);
  }


  @Override
  protected void decode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out)
      throws Exception {
    if (msg instanceof HttpRequest) {
      HttpRequest request = (HttpRequest) msg;

      this.request = new CustomHttpRequest(request, ctx.channel());

      out.add(this.request);
    }
    if (msg instanceof HttpContent) {
      ByteBuf buf = ((HttpContent) msg).content();
      byte[] bytes = new byte[buf.readableBytes()];
      buf.readBytes(bytes);

      this.request.writeChunk(bytes);

      if (msg instanceof LastHttpContent) {
        this.request.complete();
      }
    }
  }
}

這很好用,但是請注意,這里的所有內容都是在一個線程中完成的,也許對於大數據我可能想要生成一個新線程來為其他通道釋放該線程。

你是在正確的軌道 - 如果你的serviceHandler.handle(request, response); 調用正在執行阻塞讀取,您需要為它創建一個新線程。 請記住,應該只有少量的Netty工作線程,所以你不應該在工作線程中進行任何阻塞調用。

另一個要問的問題是,您的服務處理程序是否需要阻止? 它有什么作用? 如果它正在通過網絡挖掘數據,您是否可以以非阻塞方式將其合並到Netty管道中? 這樣,一切都是異步的,沒有阻塞調用和額外的線程需要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM