【问题标题】:Why event loop uses more than one thread?为什么事件循环使用多个线程?
【发布时间】:2020-09-30 14:21:17
【问题描述】:

一直以为异步执行是为了资源的高效利用和线程的安全,今天却遇到了Netty的奇怪行为。

public class Example {
    public static void main(String[] args) throws Exception {
        EventLoopGroup group = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
            bootstrap.group(group)
                     .channel(NioServerSocketChannel.class)
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel channel) {
                            channel.pipeline()
                                   .addLast(new ChannelOutboundHandlerAdapter() {
                                        @Override
                                        public void read(ChannelHandlerContext ctx) {
                                            String id = String.valueOf(Thread.currentThread().getId());
                                            ctx.writeAndFlush(Unpooled.wrappedBuffer(id.getBytes(StandardCharsets.UTF_8)))
                                               .addListener(ChannelFutureListener.CLOSE);
                                            }
                                        });
                        }
                    })
                    .bind("localhost", 1234)
                    .sync()
                    .channel()
                    .closeFuture()
                    .syncUninterruptibly();
        } finally {
            group.shutdownGracefully()
                 .syncUninterruptibly();
        }
    }
}

当我第一次连接时,我得到了 16。然后是 17、18、19 等等。每个连接都是在一个新线程上执行的!为什么?如果它是多线程的,Netty 的意义何在?

【问题讨论】:

  • 唯一的一点是更大的吞吐量。同时服务的客户更多。
  • @AlexeiKaigorodov 为什么?与通常的多线程服务器有什么区别?
  • 通常的多线程服务器每个连接花费一个线程。这设置了大约 10000 个同时连接的自然限制。 Netty 是异步的,可以保留更多。

标签: java netty


【解决方案1】:

NioEventLoopGroup 使用工作线程来利用多个 CPU 内核。根据the no-argument constructor javadoc

NioEventLoopGroup()

使用默认线程数、默认 ThreadFactory 和 SelectorProvider.provider() 返回的 SelectorProvider 创建一个新实例。

根据MultithreadEventLoopGroup 的默认线程数将是可用处理器数量的两倍:

DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

通常这应该足以使 CPU 饱和而不会创建大量线程。产生的线程太少,您将无法获得完整的 CPU 利用率。生成太多,您将浪费大量时间在它们之间进行上下文切换。

【讨论】:

  • 所以如果我需要在连接之间共享状态,我必须使用同步?
  • @Hivemaster 如果状态是可变的并且您不使用 CAS(例如AtomicInteger),而多个线程正在改变状态,是的。您可以将线程数限制为 1 (new NioEventLoopGroup(1)),但在大多数现代 CPU 上这是一种巨大的浪费。
  • @Hivemaster 它实现了Future 所以它应该是但请检查类javadoc,例如见this answer。关于您之前的问题,有时您只需 volatile 就可以逃脱,但这取决于您的状态。
  • 这不是默认构造函数。
猜你喜欢
  • 2017-04-16
  • 2014-06-20
  • 2019-01-15
  • 1970-01-01
  • 2018-06-16
  • 1970-01-01
  • 2016-04-21
相关资源
最近更新 更多