4

I am trying to use Netty (4.0.24) to create several servers (several ServerBootstraps) in one application (one main method). I saw this question/answer but it leaves many questions unanswered: Netty 4.0 multi port with difference protocol each port So here are my questions: The above answer suggests that all we need to do is create multiple ServerBootstrap objects and bind() to each. But most of the code examples I see for a single ServerBootstrap will then call something like this:

try {
    b.bind().sync().channel().closeFuture().sync();
}
finally {
    b.shutdown();
}

So doesn't the sync() call result in the ServerBootstrap b blocking? So how can we do this for multiple ServerBootstraps? And what happens if we do not call sync()? Is the set of sync calls only for being able to gracefully shutdown the server with b.shutdown()? If so, is there any way to gracefully shutdown multiple ServerBootstraps?

Also, I don't understand what happens when we just call bind() without calling sync(). Does the server somehow keep running? How do we shut it down gracefully?

Obviously I'm pretty confused about how all this works, and sadly Netty documentation is really lacking in this regard. Any help would be greatly appreciated.

Community
  • 1
  • 1
Marc
  • 3,386
  • 8
  • 44
  • 68

2 Answers2

16

Following example you referenced and adding your question on sync() method, here an example code:

EventLoopGroup bossGroup = new NioEventLoopGroup(numBossThreads);
EventLoopGroup workerGroup = new NioEventLoopGroup(numWorkerThreads);
ServerBootstrap sb1 = null;
ServerBootstrap sb2 = null;
ServerBootstrap sb3 = null;
Channel ch1 = null;
Channel ch2 = null;
Channel ch3 = null;
try {
    sb1 = new ServerBootstrap();
    sb1.group(bossGroup, workerGroup);
    ...
    ch1 = sb1.bind().sync().channel();

    sb2 = new ServerBootstrap();
    sb2.group(bossGroup, workerGroup);
    ...
    ch2 = sb2.bind().sync().channel();

    sb3 = new ServerBootstrap();
    sb3.group(bossGroup, workerGroup);
    ...
    ch3 = sb3.bind().sync().channel();
} finally {
    // Now waiting for the parent channels (the binded ones) to be closed
    if (ch1 != null) {
        ch1.closeFuture().sync();
    }
    if (b1 != null) {
        b1.shutdownGracefully();
    }
    if (ch2 != null) {
        ch2.closeFuture().sync();
    }
    if (b2 != null) {
        b2.shutdownGracefully();
    }
    if (ch3 != null) {
        ch3.closeFuture().sync();
    }
    if (b3 != null) {
        b3.shutdownGracefully();
    }

So now on the explanations (I try):

  • The bind() command creates the listening corresponding socket. It returns immediately (not blocking) so the parent channel might not be yet available.
  • The first sync() command (bind().sync()) waits for the binding to be done (if an exception is raised, then it goes directly to the finally part). At this stage, the channel is ready and listening for new connections for sure.
  • The channel() command gets this listening channel (the parent one, connected to no one yet). All clients will generate a "child" channel of this parent one.
  • In you handler, after some event, you decide to close the parent channel (not the child one, but the one listening and waiting for new socket). To do this close, just call parentChannel.close() (or from a child channel child.parent().close()).
  • The closeFuture() command is getting the future on this closing event.
  • When this future is over (done), this is when the last sync() command (closeFuture().sync()) will take place.
  • Once the parent channel is closed, you can ask for a gracefully shutdown of the binded channel.

So doing this way (waiting for the closeFuture then shutdownGracefully) is a clean way to shutdown all resources attached to this ServerBootstrap.

Of course you can change a bit the things. For instance, not getting the channel first but only later when you want to block before gracefully shutdown.

EventLoopGroup bossGroup = new NioEventLoopGroup(numBossThreads);
EventLoopGroup workerGroup = new NioEventLoopGroup(numWorkerThreads);
ServerBootstrap sb1 = null;
ServerBootstrap sb2 = null;
ServerBootstrap sb3 = null;
ChannelFuture cf1 = null;
ChannelFuture cf2 = null;
ChannelFuture cf3 = null;
try {
    sb1 = new ServerBootstrap();
    sb1.group(bossGroup, workerGroup);
    ...
    cf1 = sb1.bind();

    sb2 = new ServerBootstrap();
    sb2.group(bossGroup, workerGroup);
    ...
    cf2 = sb2.bind();

    sb3 = new ServerBootstrap();
    sb3.group(bossGroup, workerGroup);
    ...
    cf3 = sb3.bind();
} finally {
    // Now waiting for the parent channels (the binded ones) to be closed
    if (cf1 != null) {
        cf1.sync().channel().closeFuture().sync();
    }
    if (cf2 != null) {
        c2.sync().channel().closeFuture().sync();
    }
    if (cf3 != null) {
        cf3.sync().channel().closeFuture().sync();
    }
    if (b1 != null) {
        b1.shutdownGracefully();
    }
    if (b2 != null) {
        b2.shutdownGracefully();
    }
    if (b3 != null) {
        b3.shutdownGracefully();
    }

This way you don't block at all while opening all 3 channels, but then wait for all 3 to be done before shutdown them.

Finally, if you don't block on bind() event then on closeFuture() event, it's up to you to define how you will wait after the sbx.bind() commands and before to shutdown the ServerBootstraps.

Frederic Brégier
  • 2,108
  • 1
  • 18
  • 25
  • 1
    This is the most awesome answer in the history of the known universe. Thanks! – Marc Nov 24 '14 at 20:18
  • @brainstorm you seemd to have removed your question? OK, I suppose you've got your answer ;-) – Frederic Brégier Apr 03 '19 at 19:06
  • @FredericBrégier I have a better version of the question here. https://stackoverflow.com/questions/55463519/netty-proxy-server-not-returning-response-from-handlers – brain storm Apr 03 '19 at 19:14
-4
public static void main(String[] args) {
  new Thread(new Runnable(){
    @Override
    public void run() {
      //{...} ServerBootstrap 1
    }
  }).start();
  new Thread(new Runnable(){
    @Override
    public void run() {
      //{...} ServerBootstrap 2
    }
  }).start();
  new Thread(new Runnable(){
    @Override
    public void run() {
      //{...} ServerBootstrap 3
    }
  }).start();
}