Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do we really need multiple netty boss threads?

Tags:

netty

I'm really confused about the number of threads for a boss group. I can't figure out a scenario where we need more than one boss thread. In do we need more than a single thread for boss group? the creator of Netty says multiple boss threads are useful if we share NioEventLoopGroup between different server bootstraps, but I don't see the reason for it.

Consider this simple Echo server:

public class EchoServer {

private final int port;
private List<ChannelFuture> channelFutures = new ArrayList<ChannelFuture>(2);

public EchoServer(int port) {
    this.port = port;
}

public void start() throws Exception {

    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    EventLoopGroup workerGroup = new NioEventLoopGroup(4);

    for (int i = 0; i != 2; ++i) {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class) // the channel type
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch)
                            throws Exception {
                        System.out.println("Connection accepted by server");
                        ch.pipeline().addLast(
                                new EchoServerHandler());
                    }
                });

        // wait till binding to port completes
        ChannelFuture f = b.bind(port + i).sync();
        channelFutures.add(f);
        System.out.println("Echo server started and listen on " + f.channel().localAddress());
    }

    for (ChannelFuture f : channelFutures)
        f.channel().closeFuture().sync();

    // close gracefully
    workerGroup.shutdownGracefully().sync();
    bossGroup.shutdownGracefully().sync();
}

public static void main(String[] args) throws Exception {
    if (args.length != 1) {
        System.err.println(
                "Usage: " + EchoServer.class.getSimpleName() +
                        " <port>");
        return;
    }
    int port = Integer.parseInt(args[0]);
    new EchoServer(port).start();
}

In the above example, I create a bossGroup with 1 thread and workerGroup with 4 threads and share both event groups to two different bootstraps that bind to two different ports (e.g. 9000 and 9001). Below is my handler:

@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

@Override
public void channelRead(ChannelHandlerContext ctx,
                        Object msg)  throws Exception  {
    ByteBuf in = (ByteBuf) msg;
    System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8) + " from channel " + ctx.channel().hashCode());
    ctx.write(in);
}

@Override
public void channelReadComplete(ChannelHandlerContext ctx) {
    System.out.println("Read complete for channel " + ctx.channel().hashCode());
    // keep channel busy forever
    while(true); 
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx,
                            Throwable cause) {
    cause.printStackTrace();
    ctx.close();
}
}

In my handler above, I purposely keep the channel busy by doing while(true); Now if I start my app with parameter 9000, it will create two server bootstraps that bind at port 9000 and 9001.

Echo server started and listen on /0:0:0:0:0:0:0:0:9090
Echo server started and listen on /0:0:0:0:0:0:0:0:9091

Now, if I connect to both ports and start sending data, the maximum # of connections that can be received is 4, which makes sense since I have created 4 worker threads and keep their channel busy without closing it:

echo 'abc' > /dev/tcp/localhost/9000
echo 'def' > /dev/tcp/localhost/9000
echo 'ghi' > /dev/tcp/localhost/9001
echo 'jkl' > /dev/tcp/localhost/9000
echo 'mno' > /dev/tcp/localhost/9001 # will not get connected

You can also do:

telnet localhost 9000 -> then send data "abc"
telnet localhost 9000 -> then send data "def"
telnet localhost 9001 -> then send data "ghi"
telnet localhost 9000 -> then send data "jkl"
telnet localhost 9001 -> # will not get connected

what I don't understand is, I have one boss thread and I'm able to connect to two ports with two server bootstraps. So why do we need more than one boss thread (and by default, the # of boss threads is 2*num_logical_processors)?

Thanks,

like image 422
BestCoderEver Avatar asked Dec 14 '15 19:12

BestCoderEver


2 Answers

the creator of Netty says multiple boss threads are useful if we share NioEventLoopGroup between different server bootstraps, but I don't see the reason for it.

As Norman Maurer said, it's not necessary, but it's very useful.

If you are using 1 thread for 2 different bootstraps, it means that you can't handle connections to this bootstraps simultaneously. So in very very bad case, when boss thread handle only connections for one bootstrap, connections to another will never be handled.

Same for workers EventLoopGroup.

like image 102
Suvitruf - Andrei Apanasik Avatar answered Nov 18 '22 11:11

Suvitruf - Andrei Apanasik


When you bind() a new port, Netty registy a new ServerSocketChannel to the next() eventloop in bossGroup, so i think multiple threads boos may be used on multiple ports applications.

like image 45
Wintters Avatar answered Nov 18 '22 11:11

Wintters