为什么我们真的需要多个 netty boss 线程?
Why do we really need multiple netty boss threads?
我真的很困惑老板群的线程数。我想不出我们需要多个老板线程的场景。在 do we need more than a single thread for boss group? 中,Netty 的创建者说,如果我们在不同的服务器引导程序之间共享 NioEventLoopGroup,则多个 boss 线程很有用,但我不明白其中的原因。
考虑这个简单的 Echo 服务器:
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();
}
在上面的示例中,我创建了一个具有 1 个线程的 bossGroup 和一个具有 4 个线程的 workerGroup,并将两个事件组共享给绑定到两个不同端口(例如 9000 和 9001)的两个不同引导程序。下面是我的处理程序:
@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();
}
}
在我上面的处理程序中,我故意通过执行 while(true); 来保持频道忙碌;现在,如果我使用参数 9000 启动我的应用程序,它将创建两个绑定在端口 9000 和 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
现在,如果我连接到两个端口并开始发送数据,则可以接收的最大连接数是 4,这是有道理的,因为我已经创建了 4 个工作线程并保持它们的通道忙碌而不关闭它:
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
您还可以这样做:
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
我不明白的是,我有一个老板线程,我能够使用两个服务器引导程序连接到两个端口。那么,为什么我们需要多个老板线程(默认情况下,老板线程的数量是 2*num_logical_processors)?
谢谢,
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.
正如 Norman Maurer 所说,这不是必需的,但它非常有用。
如果您将 1 个线程用于 2 个不同的 bootstrap,这意味着您无法同时处理与此 bootstrap 的连接。所以在非常非常糟糕的情况下,当 boss 线程只处理一个 bootstrap 的连接时,将永远不会处理与另一个的连接。
工人也一样EventLoopGroup
。
当你 bind() 一个新的端口时,Netty 注册一个新的 ServerSocketChannel 到 bossGroup 中的 next() 事件循环,所以我认为多线程 boos 可以用于多端口应用程序。
我真的很困惑老板群的线程数。我想不出我们需要多个老板线程的场景。在 do we need more than a single thread for boss group? 中,Netty 的创建者说,如果我们在不同的服务器引导程序之间共享 NioEventLoopGroup,则多个 boss 线程很有用,但我不明白其中的原因。
考虑这个简单的 Echo 服务器:
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();
}
在上面的示例中,我创建了一个具有 1 个线程的 bossGroup 和一个具有 4 个线程的 workerGroup,并将两个事件组共享给绑定到两个不同端口(例如 9000 和 9001)的两个不同引导程序。下面是我的处理程序:
@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();
}
}
在我上面的处理程序中,我故意通过执行 while(true); 来保持频道忙碌;现在,如果我使用参数 9000 启动我的应用程序,它将创建两个绑定在端口 9000 和 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
现在,如果我连接到两个端口并开始发送数据,则可以接收的最大连接数是 4,这是有道理的,因为我已经创建了 4 个工作线程并保持它们的通道忙碌而不关闭它:
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
您还可以这样做:
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
我不明白的是,我有一个老板线程,我能够使用两个服务器引导程序连接到两个端口。那么,为什么我们需要多个老板线程(默认情况下,老板线程的数量是 2*num_logical_processors)?
谢谢,
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.
正如 Norman Maurer 所说,这不是必需的,但它非常有用。
如果您将 1 个线程用于 2 个不同的 bootstrap,这意味着您无法同时处理与此 bootstrap 的连接。所以在非常非常糟糕的情况下,当 boss 线程只处理一个 bootstrap 的连接时,将永远不会处理与另一个的连接。
工人也一样EventLoopGroup
。
当你 bind() 一个新的端口时,Netty 注册一个新的 ServerSocketChannel 到 bossGroup 中的 next() 事件循环,所以我认为多线程 boos 可以用于多端口应用程序。