title: Reusing HTTP response in Netty handler
body: <p>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. I'm doing some final optimizations and I'm currently looking into reducing unnecessary memory allocation by reusing whatever objects I can. A simplified example of a server highlighting my issue is the following:</p>
  <pre><code>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&lt;SocketChannel&gt;() {
              @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();
    }
  }
  </code></pre>
  <p>The handler code is the following:</p>
  <pre><code>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&lt;Object&gt; {
  
    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 (&quot;/ok&quot;.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 = &quot;{ \&quot;status\&quot;: ok }&quot;;
      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, &quot;max-age=0, no-cache, must-revalidate, proxy-revalidate&quot;);
      return response;
    }
  
    private static FullHttpResponse FailResponse() {
      String data = &quot;{ \&quot;status\&quot;: fail }&quot;;
      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, &quot;max-age=0, no-cache, must-revalidate, proxy-revalidate&quot;);
      return response;
    }
  }
  </code></pre>
  <p>The handler shows what I'm trying to accomplish. The handler contains static instances of fixed HTTP responses. 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. I was expecting that just calling <code>retain()</code> on the object would be enough, but it doesn't look like it is.</p>
  <p>What would be the most efficient way to reuse the HTTP response objects between requests?</p>