简体   繁体   English

在 Netty 处理程序中重用 HTTP 响应

[英]Reusing HTTP response in Netty handler

I'm using Netty for a server that needs to handle hundreds of thousands of requests per second while maintaining as little variance as possible on response latencies.我将 Netty 用于需要每秒处理数十万个请求的服务器,同时在响应延迟上保持尽可能小的差异。 I'm doing some final optimizations and I'm currently looking into reducing unnecessary memory allocation by reusing whatever objects I can.我正在做一些最后的优化,我目前正在研究通过重用我能做到的任何对象来减少不必要的 memory 分配。 A simplified example of a server highlighting my issue is the following:突出显示我的问题的服务器的简化示例如下:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpObjectAggregator;

public class NettyServer {
  public void run() throws Exception {
    EventLoopGroup bossGroup = new NioEventLoopGroup();
    EventLoopGroup workerGroup = new NioEventLoopGroup();

    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup)
          .channel(NioServerSocketChannel.class)
          .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
              ChannelPipeline p = ch.pipeline();
              p.addLast(new HttpServerCodec());
              p.addLast(new HttpObjectAggregator(1048576));
              p.addLast(new NettyHandler());
            }
          });

      ChannelFuture f = b.bind(8080).sync();
      f.channel().closeFuture().sync();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }

  public static void main(String[] args) throws Exception {
    new NettyServer().run();
  }
}

The handler code is the following:处理程序代码如下:

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.CharsetUtil;

public class NettyHandler extends SimpleChannelInboundHandler<Object> {

  private static final FullHttpResponse okResponse = OkResponse();
  private static final FullHttpResponse failResponse = FailResponse();

  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.flush();
  }

  @Override
  protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
    FullHttpRequest request = (FullHttpRequest) msg;
    QueryStringDecoder query = new QueryStringDecoder(request.getUri());
    String path = query.path();

    ChannelFuture f;
    boolean keepAlive = HttpUtil.isKeepAlive(request);

    if ("/ok".equals(path)) {
      f = ctx.write(okResponse);
    } else {
      f = ctx.write(failResponse);
      keepAlive = false;
    }

    if (!keepAlive) {
      f.addListener(ChannelFutureListener.CLOSE);
    }
  }

  private static FullHttpResponse OkResponse() {
    String data = "{ \"status\": ok }";
    FullHttpResponse response = new DefaultFullHttpResponse(
        HttpVersion.HTTP_1_1,
        HttpResponseStatus.OK,
        Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
    );
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
    response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
    return response;
  }

  private static FullHttpResponse FailResponse() {
    String data = "{ \"status\": fail }";
    FullHttpResponse response = new DefaultFullHttpResponse(
        HttpVersion.HTTP_1_1,
        HttpResponseStatus.OK,
        Unpooled.copiedBuffer(data, CharsetUtil.UTF_8)
    );
    response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);
    response.headers().set(HttpHeaderNames.CACHE_CONTROL, "max-age=0, no-cache, must-revalidate, proxy-revalidate");
    return response;
  }
}

The handler shows what I'm trying to accomplish.处理程序显示了我要完成的工作。 The handler contains static instances of fixed HTTP responses.该处理程序包含固定 HTTP 响应的 static 实例。 For the server all responses except error codes come from a small group and can be preconstructed.对于服务器,除了错误代码之外的所有响应都来自一个小组,并且可以预先构建。 With the above code the second query to a handler will fail, since Netty's ref counts for the response has gone down to zero.使用上面的代码,对处理程序的第二个查询将失败,因为 Netty 对响应的引用计数已降至零。 I was expecting that just calling retain() on the object would be enough, but it doesn't look like it is.我原以为只需在 object 上调用retain()就足够了,但看起来并不像这样。

What would be the most efficient way to reuse the HTTP response objects between requests?在请求之间重用 HTTP 响应对象的最有效方法是什么?

You should call retainedDuplicate() as otherwise the readerIndex etc may become “invalid”您应该调用 reservedDuplicate retainedDuplicate()否则 readerIndex 等可能会变得“无效”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM