Netty NioEventLoop 创建过程源码分析

本文深入分析了Netty中的NioEventLoop创建过程,包括EventLoop与EventLoopGroup的关系、线程管理和分配、NIO与OIO传输的区别。通过构造函数和关键类的解析,揭示了EventLoop如何处理网络连接事件,并且介绍了任务执行器、线程工厂以及选择器的创建与工作原理。
摘要由CSDN通过智能技术生成

原文:https://wangwei.one/posts/netty-nioeventloop-analyse-for-create.html

前面 ,我们分析了Netty中的Channel组件,本篇我们来介绍一下与Channel关联的另一个核心的组件 —— EventLoop

Netty版本:4.1.30

概述

EventLoop定义了Netty的核心抽象,用于处理网络连接生命周期中所有发生的事件。

我们先来从一个比较高的视角来了解一下Channels、Thread、EventLoops、EventLoopGroups之间的关系。

Netty-EventLoop-Channel

上图是表示了拥有4个EventLoop的EventLoopGroup处理IO的流程图。它们之间的关系如下:

  • 一个 EventLoopGroup包含一个或多个EventLoop
  • 一个 EventLoop在它的生命周期内只和一个Thread绑定
  • 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理
  • 一个Channel在它的生命周期内只注册于一个EventLoop
  • 一个EventLoop可能会被分配给一个或多个Channel

EventLoop 原理

下图是Netty EventLoop相关类的UML图。从中我们可以看到EventLoop相关的类都是实现了 java.util.concurrent 包中的 ExecutorService 接口。我们可以直接将任务(Runable 或 Callable) 提交给EventLoop去立即执行或定时执行。

Netty EventLoop UML

例如,使用EventLoop去执行定时任务,样例代码:

public static void scheduleViaEventLoop() {
   
    Channel ch = new NioSocketChannel();
    ScheduledFuture<?> future = ch.eventLoop().schedule(
            () -> System.out.println("60 seconds later"), 60, TimeUnit.SECONDS);
}

Thread 管理

Netty线程模型的高性能主要取决于当前所执行线程的身份的确定。一个线程提交到EventLoop执行的流程如下:

  • 将Task任务提交给EventLoop执行
  • 在Task传递到execute方法之后,检查当前要执行的Task的线程是否是分配给EventLoop的那个线程
  • 如果是,则该线程会立即执行
  • 如果不是,则将线程放入任务队列中,等待下一次执行

其中,Netty中的每一个EventLoop都有它自己的任务队列,并且和其他的EventLoop的任务队列独立开来。

Nettu EventLoop Thread management

Thread 分配

服务于Channel的I/O和事件的EventLoop包含在EventLoopGroup中。根据不同的传输实现,EventLoop的创建和分配方式也不同。

NIO传输

Netty EventLoop Thread allocation NIO

在NIO传输方式中,使用尽可能少的EventLoop就可以服务多个Channel。如图所示,EventLoopGroup采用顺序循环的方式负责为每一个新创建的Channel分配EventLoop,每一个EventLoop会被分配给多个Channels。

一旦一个Channel被分配给了一个EventLoop,则这个Channel的生命周期内,只会绑定这个EventLoop。这就让我们在ChannelHandler的实现省去了对线程安全和同步问题的担心。

OIO传输

Netty EventLoop Thread allocation OIO

与NIO方式的不同在于,一个EventLoop只会服务于一个Channel。

NioEventLoop & NioEventLoopGroup 创建

初步了解了 EventLoop 以及 EventLoopGroup 的工作机制,接下来我们以 NioEventLoopGroup 为例,来深入分析 NioEventLoopGroup 是如何创建的,又是如何启动的,它的内部执行逻辑又是怎样的等等问题。

MultithreadEventExecutorGroup 构造器

我们从 NioEventLoopGroup 的构造函数开始分析:

EventLoopGroup acceptorEventLoopGroup = new NioEventLoopGroup(1);

NioEventLoopGroup构造函数会调用到父类 MultithreadEventLoopGroup 的构造函数,默认情况下,EventLoop的数量 = 处理器数量 x 2:

public abstract class MultithreadEventLoopGroup extends MultithreadEventExecutorGroup implements EventLoopGroup {
   

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(MultithreadEventLoopGroup.class);

    private static final int DEFAULT_EVENT_LOOP_THREADS;

    // 默认情况下,EventLoop的数量 = 处理器数量 x 2
    static {
   
        DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

        if (logger.isDebugEnabled()) {
   
            logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
        }
    }

    protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args)    {
   
        super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
    }

    ...
}

继续调用父类,会调用到 MultithreadEventExecutorGroup 的构造器,主要做三件事情:

  • 创建线程任务执行器 ThreadPerTaskExecutor
  • 通过for循环创建数量为 nThreads 个的 EventLoop
  • 创建 EventLoop 选择器 EventExecutorChooser
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
   
    if (nThreads <= 0) {
   
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }

    // 创建任务执行器 ThreadPerTaskExecutor
    if (executor == null) {
   
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    // 创建 EventExecutor 数组
    children = new EventExecutor[nThreads];

    // 通过for循环创建数量为 nThreads 个的 EventLoop
    for (int i = 0; i < nThreads; i ++) {
   
        boolean success = false;
        try {
   
            // 调用 newChild 接口
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
   
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
   
            if (!success) {
   
                for (int j = 0; j < i; j ++) {
   
                    children[j].shutdownGracefully();
                }

                for (int j = 0; j < i; j ++) {
   
                    EventExecutor e = children[j];
                    try {
   
                        while (!e.isTerminated()) {
   
                            e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                        }
                    } catch (InterruptedException interrupted) {
   
                        // Let the caller handle the interruption.
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
    }
	
    // 创建选择器
    chooser = chooserFactory.newChooser(children);

    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
   
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
   
            if (terminatedChildren.incrementAndGet() == children.length) {
   
                terminationFuture.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值