Search code examples
httprequestnettyhttpserverhttpconnectionreactor-netty

Netty Server not processing HTTP Requests after using Server Bootstrap for customizing


I have a spring reactive-based Microservice which is using Netty Server version 4.1.101.Final. It was working completely fine and processing all the HTTP requests that I fired. Now I want to catch the connection-ids to figure out which connection is getting used and which is getting closed. For this, I have written a custom Connection Logging handler. Following is my ConnectionLoggingHandler :

public class ConnectionLoggingHandler extends ChannelInboundHandlerAdapter{
    private static final AtomicInteger connectionIdCounter = new AtomicInteger(0);
    
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception{
        int connectionId=connectionIdCounter.incrementAndGet();
        System.out.println("Connection established : ID = " + connectionId);
        ctx.channel().attr(AttributeKey.valueOf("connectionId")).set(connectionId);
        super.channelActive(ctx);
    }
    
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception{
        //retrieve connectionID
        Integer connectionId = (Integer) ctx.channel().attr(AttributeKey.valueOf("connectionId")).get();
        if(connectionId!=null){
          System.out.println("Connection closed : ID = " + connectionId);  
        }
        super.channelInactive(ctx);
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception{
        //retrieve connectionID
        Integer connectionId = (Integer) ctx.channel().attr(AttributeKey.valueOf("connectionId")).get();
        if(connectionId!=null){
          System.out.println("Exception in Connection: ID = " + connectionId);  
        }
        cause.printStackTrace();
        ctx.close();
    }
}

Now I want to add this handler to my Netty Server. I tried this using two ways.

Code for Custom Netty Server:

public class CustomNettyServer{
    private int port =8080;
    
    @PostConstruct
    public void startServer() throws InterruptedException{
        EventLoopGroup boss= new NioEventLoopGroup();
        EventLoopGroup worker=new NioEventLoopGroup();
        try{
            ServerBootstrap bootstrap new ServerBootstrap();
            bootstrap.group(boss,worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<Channel>(){
                    @Override
                    protected void initChannel(Channel ch) throws Exception{
                        ch.pipeline().addLast(new HttpServerCodec(),new HttpRequestDecoder(),new HttpContentDecompressor(),new HttpResponseEncoder(),new HttpContentCompressor(),new HttpObjectAggregator(512*1024),new ConnectionLoggingHandler());
                        
                    }
                });
            Channel channel = bootstarp.bind(port).sync().channel();
            channel.closeFuture().sync();
        }
        finally{
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}

When I use this server, then I can see the logs present in my custom handler when I hit a request through Postman for the establishment of the connection. However, my HTTP request is not getting processed.
It says Discarded inbound message. Please check your pipeline configuration. I don't know what am I missing here.

Then I also tried another way to customize my netty server. Below is my code for CustomNettyServer.

public class CustomNettyServer implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory>{
    @Override
    public void customize(NettyReactiveWebServerFactory factory){
        factory.addServerCustomizers(server->
        server.tcpConfiguration(tcp->
        tcp.doOnConnection(connection->
        connection.addHandlerLast(new ConnectionLoggingHandler()))));
    }
}

Using this method, I cannot see logs getting printed due to my ConnectionLoggingHandler whenever I hit a request and the request is getting processed successfully. I can see Handler getting added to the pipeline when the request is made and removed when the request is served. But the logs are not getting generated. I have also kept the logging level to DEBUG.

I don't know what I am doing wrong in these two ways and I am stuck here.
So I want to know how I can get the logs for the Connection-IDs that are being opened, used, and closed.
Thanks.


Solution

  • Reactor Netty (the default runtime used by Spring WebFlux) does not use Bootstrap classes provided by Netty.

    The correct way for extending Reactor Netty is via the lifecycle callbacks. In your example you was using doOnConnection, but at this point the channel is already active and your implementation under ConnectionLoggingHandler#channelActive will be never invoked (check the javadoc for addHandlerLast). You would better use the callback doOnChannelInit, as below

    @Component
    public class CustomNettyServer
            implements WebServerFactoryCustomizer<NettyReactiveWebServerFactory> {
        @Override
        public void customize(NettyReactiveWebServerFactory factory) {
            factory.addServerCustomizers(server ->
                    server.doOnChannelInit((obs, ch, addr) ->
                            ch.pipeline().addAfter(NettyPipeline.HttpCodec, "test", new ConnectionLoggingHandler())));
        }
    }