SBK
SBK

Reputation: 123

Netty closeFuture().sync().channel(); blocks rest api

I am in the middle of learning Netty an started some tutorials using spring boot. My goal is to create an application which set up a tcp port for receiving messages and to present them over a rest api.

Most of the tutorials are saying that I should add something like this

serverChannel = serverBootstrap.bind(tcpPort).sync().channel().closeFuture().sync().channel();

to start netty. When I do that, the rest services which I implemented are not working. Now when I use the following code snippet to start the application:

serverChannel = serverBootstrap.bind(tcpPort).sync().channel();

everything seems to be working just fine. Could someone explain me what might cause this issue?

Thanks

Upvotes: 8

Views: 3941

Answers (2)

user1738539
user1738539

Reputation: 941

Old, but I had same issue with my RestController not starting. Other answer helped solve it for me but here is full code for the Spring component.

import com.myserver.netty.handler.ClientInboundHandler;
import com.myserver.netty.handler.PacketDecoder;
import com.myserver.netty.handler.PacketEncoder;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@Log4j2
public class NettyServer {

   private EventLoopGroup masters = new NioEventLoopGroup();
   private EventLoopGroup workers = new NioEventLoopGroup();

   private Channel mainChannel;

   @PostConstruct
   public void start() {
       try {
           ServerBootstrap bootstrap = init();
           mainChannel = bootstrap.bind(8484).sync().channel(); // save the main channel so we can cleanly close it when app is shutdown
           log.info("Netty Server started......");
       } catch (Exception e) {
           e.printStackTrace();
       }
   }

   @PreDestroy
   public void stop() throws InterruptedException {
       log.info("Shutting down netty server");
       workers.shutdownGracefully().sync();
       masters.shutdownGracefully().sync();
       mainChannel.closeFuture().sync();
       log.info("Shutdown complete");
   }

   private ServerBootstrap init() {
       return new ServerBootstrap()
               .group(masters, workers)
               .channel(NioServerSocketChannel.class)
               .option(ChannelOption.SO_BACKLOG, 5000)
               .option(ChannelOption.TCP_NODELAY, true)
               .option(ChannelOption.SO_KEEPALIVE, true)
               .childHandler(new ChannelInitializer<SocketChannel>() {
                   @Override
                   protected void initChannel(SocketChannel channel) throws Exception {
                       channel.pipeline()
                               .addLast(new PacketDecoder())
                               .addLast(new ClientInboundHandler())
                               .addLast(new PacketEncoder());
                   }
               });
   }

}

Upvotes: 0

Frederic Br&#233;gier
Frederic Br&#233;gier

Reputation: 2206

The first part start the server, 1) binding it on a TCP port, 2) wait for the server to be ready (socket is listening) 3) and return the associated channel.

serverBootstrap.bind(tcpPort).sync().channel();
                 (1)           (2)       (3)

The second part is to wait for the main channel (listening socket) to shutdown (closeFuture().sync()) where closeFuture gives you the "future" on "close" operation (meaning shutdown of the server socket), and sync waiting for this future to be done. channel() gives you back the very same channel than first time, except it is now closed.

So you might find this code in various example because generally you start the server (bind) in the main thread or so, and then if you don't wait for something, the main thread will end up, giving your JVM finishing, and therefore your server to stop immediately after starting.

So in general, what we do is:

  • start the server
  • add in the pipeline the necessary handlers to handle your business logic (and the network protocol of course)
  • then finish your main by waiting on closeFuture, such that, once in your business logic you get the order to shutdown, you close the main channel, and therefore your main thread is closing too.

See for instance Shutdown netty programmatically

Upvotes: 9

Related Questions