Netty线程模型源码分析之NioEventLoopGroup的初始化

此生再无相见时 提交于 2020-04-23 21:01:20

创建NioEventLoopGroup

NioEventLoopGroup提供了很多构造方法,下面以参数最完整的构造方法为例:

public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory,
        final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory) {
    super(nThreads, threadFactory, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
                             final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory,
                             final RejectedExecutionHandler rejectedExecutionHandler) {
    super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory, rejectedExecutionHandler);
}

参数含义:

nThreads:用于处理I/O请求的线程数量,可通过参数io.netty.eventLoopThreads进行配置,默认值为CPU核心数的两倍。

executor:与threadFactory只能存在其一,都是用于创建并启动I/O线程,executor为null则默认为ThreadPerTaskExecutor的实例,并使用DefaultThreadFactory创建该实例。

threadFactory:用于创建并启动I/O线程,最终也会使用该参数创建ThreadPerTaskExecutor的实例。调用具有该参数的构造器时,threadFactory不能为null。

selectorProvider:Selector的提供者,具体依赖底层实现,默认为SelectorProvider.provider()返回值。

selectStrategyFactory:SelectStrategy的实例化工厂,默认为DefaultSelectStrategyFactory。

chooserFactory:注册Channel时,需要从NioEventLoopGroup中选择一个EventLoop与Channel绑定。通过实现EventExecutorChooserFactory,重写newChooser,传入自定义的EventExecutorChooser即可自定义选择规则,默认为DefaultEventExecutorChooserFactory。

rejectedExecutionHandler:当超过EventLoop最大待处理任务数时,EventLoop拒绝处理任务的策略。实现RejectedExecutionHandler可定制化自己的拒绝策略。默认为RejectedExecutionHandlers.reject(),即抛出异常RejectedExecutionException。

NioEventLoopGroup比较常用的是无参构造器和传入线程数量的构造器,最终都是调用如下构造方法:

public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                             final SelectStrategyFactory selectStrategyFactory) {
     super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}

其中executor为null,selectorProvider、selectStrategyFactory、rejectedExecutionHandler等都为默认值,接下来调用父类MultithreadEventLoopGroup的构造方法:

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

nThreads为0则使用默认I/O线程数量,继续调用父类MultithreadEventExecutorGroup的构造方法:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                            EventExecutorChooserFactory chooserFactory, Object... args) {
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            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.setSuccess(null);
            }
        }
    };
    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }

    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

实例化过程:

  • nThreads必须大于0,否则抛出异常;
  • executor为null,则使用DefaultThreadFactory实例构造一个ThreadPerTaskExecutor实例作为executor,后续用该executor启动I/O线程;
  • 初始化EventExecutor数组children,并循环调用newChild创建指定数量的NioEventLoop;
  • 如果success为false,说明调用newChild抛出异常,终止实例化过程,调用shutdownGracefully优雅的关闭已经创建的NioEventLoop,并终止对应的线程;
  • 如果最终全部NioEventLoop实例都正常创建,实例化chooser,后续调用next选择NioEventLoop实例将委托给该chooser完成;
  • 为EventExecutor数组中的每个成员注册终止事件的监听器,当某个NioEventLoop实例对应的线程被终止时,自动通知监听者,当所有的I/O线程都被终止后,terminationFuture会被设置为success,并通知关心NioEventLoopGroup终止事件的监听者;
  • 为children数组维护一个只读视图readonlyChildren,主要用于迭代。

到此,NioEventLoopGroup对象构造完成,下面分析newChild创建NioEventLoop的过程。

创建NioEventLoop

newChild的实现在NioEventLoopGroup中,源码如下:

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

下面进入NioEventLoop的构造方法:

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
    if (selectorProvider == null) {
        throw new NullPointerException("selectorProvider");
    }
    if (strategy == null) {
        throw new NullPointerException("selectStrategy");
    }
    provider = selectorProvider;
    final SelectorTuple selectorTuple = openSelector();
    selector = selectorTuple.selector;
    unwrappedSelector = selectorTuple.unwrappedSelector;
    selectStrategy = strategy;
}

由构造方法的参数可知,NioEventLoopGroup中提供的参数主要都用于构造NioEventLoop实例。作为NIO框架的Reactor线程,NioEventLoop需要处理网络I/O事件,因此必须聚合一个多路复用器对象,构造方法中主要完成该工作,后续详细分析。下面看父类SingleThreadEventLoop的构造方法:

protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                boolean addTaskWakesUp, int maxPendingTasks,
                                RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
    tailTasks = newTaskQueue(maxPendingTasks);
}

内部维护了一个tailTasks队列,目前没用。继续看父类SingleThreadEventExecutor的构造方法。

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedHandler) {
    super(parent);
    this.addTaskWakesUp = addTaskWakesUp;
    this.maxPendingTasks = Math.max(16, maxPendingTasks);
    this.executor = ObjectUtil.checkNotNull(executor, "executor");
    taskQueue = newTaskQueue(this.maxPendingTasks);
    rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

参数含义:

parent:当前NioEventLoop所属的NioEventLoopGroup,最终会调用AbstractEventExecutor的构造方法完成parent的赋值;

addTaskWakesUp:是否在调用addTask(Runnable)时唤醒执行器线程,这里被设置为false;

executor:用于创建并启动I/O线程;

maxPendingTasks:最大待处理的任务数,当超过该值时,新加入的任务将会被拒绝;用户可通过参数io.netty.eventLoop.maxPendingTasks进行配置,不能小于16,默认为Integer.MAX_VALUE;

rejectedHandler:任务拒绝策略。

由构造方法可知,创建了一个具有maxPendingTasks数量的taskQueue,用于保存待处理的任务,该队列默认为LinkedBlockingQueue。

到此,NioEventLoop构造完成,下面分析NioEventLoop中多路复用器的创建过程。

创建Selector

NioEventLoop在openSelector()创建Selector对象,代码有点冗长,一步步分析,看实现:

private SelectorTuple openSelector() {
    final Selector unwrappedSelector;
    try {
        unwrappedSelector = provider.openSelector();
    } catch (IOException e) {
        throw new ChannelException("failed to open a new selector", e);
    }

    if (DISABLE_KEYSET_OPTIMIZATION) {
        return new SelectorTuple(unwrappedSelector);
    }

    ......
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

首先调用SelectorProvider的openSelector()创建并打开一个新的Selector对象。Netty对Selector的SelectKeys进行了优化,用户可以通过io.netty.noKeySetOptimization开关决定是否启用该优化项,默认是关闭的,此时会创建一个SelectorTuple直接返回,selector和unwrappedSelector 都指向同一个Selector对象。

如果用户开启了SelectKeys的优化功能,继续执行:

private SelectorTuple openSelector() {
    ......
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

    Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                return Class.forName( "sun.nio.ch.SelectorImpl",false,
                        PlatformDependent.getSystemClassLoader());
            } catch (Throwable cause) {
                return cause;
            }
        }
    });

    if (!(maybeSelectorImplClass instanceof Class) ||
            // ensure the current selector implementation is what we can instrument.
            !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
        if (maybeSelectorImplClass instanceof Throwable) {
            Throwable t = (Throwable) maybeSelectorImplClass;
            logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
        }
        return new SelectorTuple(unwrappedSelector);
    }
    ......
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

创建一个SelectedSelectionKeySet实例,用于保存SelectKey,内部主要定义了reset方法,用于从start位置开始,将后面的元素全部置为null,size置为0。SelectedSelectionKeySet功能类似一个简版的list,内部持有一个数组,并实现了自动扩容的功能,但诸如remove、contains、iterator等均不支持。为什么要扩展AbstractSet变成Set呢?后面解释。

加载sun.nio.ch.SelectorImpl的Class对象,并判断该Class对象与unwrappedSelector对应Class对象相等,若不相等或加载过程中返回无效的Class对象,创建一个SelectorTuple直接返回。

如果成功加载了正确的Class对象,继续向下执行:

private SelectorTuple openSelector() {
    ......
    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField);
                if (cause != null) {
                    return cause;
                }
                cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField);
                if (cause != null) {
                    return cause;
                }

                selectedKeysField.set(unwrappedSelector, selectedKeySet);
                publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            }
        }
    });

    if (maybeException instanceof Exception) {
        selectedKeys = null;
        Exception e = (Exception) maybeException;
        logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
        return new SelectorTuple(unwrappedSelector);
    }
    selectedKeys = selectedKeySet;
    logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

此部分代码主要是通过反射的手段从Selector实例中获取publicKeys和publicSelectedKeys,修改这两个属性的访问权限为可写,然后使用Netty构造的SelectKeys包装对象selectedKeySet将JDK中的publicKeys和publicSelectedKeys替换掉。

此过程中若发生异常,创建一个SelectorTuple直接返回。否则,使用unwrappedSelector和包装类SelectedSelectionKeySetSelector的对象创建一个SelectorTuple返回。

由于Selector实例中publicKeys和publicSelectedKeys均为Set类型,因此SelectedSelectionKeySet为了兼容也被设计为Set。

到此,Selector的初始化过程介绍完了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!