咱们都知道Netty的线程模型是基于React的线程模型,而且咱们都知道Netty是一个高性能的NIO框架,那么其线程模型一定是它的重要贡献之一。数组
在使用netty的服务端引导类ServerBootstrap或客户端引导类Bootstrap进行开发时,都须要经过group属性指定EventLoopGroup, 由于是开发NIO程序,因此咱们选择NioEventLoopGroup。框架
接下来的两篇文章,我将从源码角度为你们深刻浅出的剖析Netty的React线程模型工做机制。ide
本篇侧重点是NioEventLoopGroup。oop
首先咱们先回顾一下,服务端初始化程序代码(省略非相关代码):性能
EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); ... // 已省略非相关代码 // 侦听8000端口 ChannelFuture f = b.bind(8000).sync(); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); }
在分析源码以前,咱们先看看NioEventLoopGroup的类继承结构图:this
初始化bossGroup及workerGroup时,使用了NioEventLoopGroup的无参构造方法,本篇将今后无参构造入手,详细分析NioEventLoopGroup的初始化过程。spa
首先咱们看看NioEventLoopGroup的无参构造方法:.net
public NioEventLoopGroup() { this(0); }
其内部继续调用器构造方法,并指定线程数为0:线程
public NioEventLoopGroup(int nThreads) { this(nThreads, (Executor) null); }
继续调用另外一个构造方法,指定线程为0,且Executor为null:debug
public NioEventLoopGroup(int nThreads, Executor executor) { this(nThreads, executor, SelectorProvider.provider()); }
在此构造中,它会指定selector的辅助类 "SelectorProvider.provider()",咱们继续查看它的调用:
public NioEventLoopGroup( int nThreads, Executor executor, final SelectorProvider selectorProvider) { this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE); }
此构造方法中,初始化了一个默认的选择策略工厂,用于生成select策略:
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider, final SelectStrategyFactory selectStrategyFactory) { super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject()); }
通过上面一系列的构造方法调用,此时个参数值对应以下:
nThreads:0
executor: null
selectorProvider: SelectorProvider.provider()
selectStrategyFactory: DefaultSelectStrategyFactory.INSTANCE
以及指定了拒绝策略RejectedExecutionHandlers.reject()
接着其会调用父类MultithreadEventLoopGroup的MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args)构造方法
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) { super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args); }
此构造方法主要作了一件事,就是当指定的线程数为0时,使用默认的线程数DEFAULT_EVENT_LOOP_THREADS,此只是在MultithreadEventLoopGroup类被加载时完成初始化
private static final int DEFAULT_EVENT_LOOP_THREADS; 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); } }
因此根据代码,咱们得出,若是初始化NioEventLoopGroup未指定线程数时,默认是CPU核心数*2
接着继续调用父类MultithreadEventExecutorGroup的MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args)构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) { this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args); }
在此构造方法中,咱们指定了一个EventExecutor的选择工厂DefaultEventExecutorChooserFactory,此工厂主要是用于选择下一个可用的EventExecutor, 其内部有两种选择器, 一个是基于位运算,一个是基于普通的轮询,它们的代码分别以下:
基于位运算的选择器PowerOfTwoEventExecutorChooser
private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; PowerOfTwoEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[idx.getAndIncrement() & executors.length - 1]; } }
基于普通轮询的选择器GenericEventExecutorChooser
private static final class GenericEventExecutorChooser implements EventExecutorChooser { private final AtomicInteger idx = new AtomicInteger(); private final EventExecutor[] executors; GenericEventExecutorChooser(EventExecutor[] executors) { this.executors = executors; } @Override public EventExecutor next() { return executors[Math.abs(idx.getAndIncrement() % executors.length)]; } }
咱们接着回到刚刚的构造器,其内部会继续调用MultithreadEventExecutorGroup的另外一个构造方法,此构造方法是NioEventLoopGroup的核心代码
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { // 为了便于代码剖析,以省略非相关代码 // 初始化executor if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } // 初始化EventExecutor children = new EventExecutor[nThreads]; for (int i = 0; i < nThreads; i ++) { children[i] = newChild(executor, args); } // 生成选择器对象 chooser = chooserFactory.newChooser(children); }
此构造方法主要作了三件事:
一、初始化executor为ThreadPerTaskExecutor的实例:
经过前面的构造方法调用,咱们知道executor为null,因此在此构造方法中,executor会被初始化为ThreadPerTaskExecutor实例。咱们看一下ThreadPerTaskExecutor的源码:
public final class ThreadPerTaskExecutor implements Executor { private final ThreadFactory threadFactory; public ThreadPerTaskExecutor(ThreadFactory threadFactory) { if (threadFactory == null) { throw new NullPointerException("threadFactory"); } this.threadFactory = threadFactory; } @Override public void execute(Runnable command) { threadFactory.newThread(command).start(); } }
经过ThreadPerTaskExecutor 的代码发现,ThreadPerTaskExecutor 实现了Executor接口,其内部会经过newDefaultThreadFactory()指定的默认线程工厂来建立线程,并执行相应的任务。
二、初始化EventExecutor数组children
在MultithreadEventExecutorGroup的构造方法中咱们看到,EventExecutor数组children初始化时是经过newChild(executor, args)实现的,而newChild的在MultithreadEventExecutorGroup中是个抽象方法
protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;
根据最开始的类继承结构图,咱们在NioEventLoopGroup中找到了newChild的实现
@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]); }
因此今后newChild的实现中,咱们能够看出MultithreadEventExecutorGroup的children,其实就是对应的一组NioEventLoop对象。 关于NioEventLoop下一篇会做详细介绍。
三、根据咱们指定的选择器工厂,绑定NioEventLoop数组对象
chooser = chooserFactory.newChooser(children);
在前面的构造方法中,咱们指定了chooserFactory为DefaultEventExecutorChooserFactory,在此工厂内部,会根据children数组的长度来动态选择选择器对象,用于选择下一个可执行的EventExecutor,也就是NioEventLoop。
@Override public EventExecutorChooser newChooser(EventExecutor[] executors) { if (isPowerOfTwo(executors.length)) { return new PowerOfTwoEventExecutorChooser(executors); } else { return new GenericEventExecutorChooser(executors); } }
至此,NioEventLoopGroup初始化完成了。
经过上面的代码分析,在NioEventLoopGroup初始化的过程当中,其实就是初始化了一堆可执行的Executor数组,而后根据某种chooser策略,来选择下一个可用的executor。
咱们再回顾总结一下:
一、NioEventLoopGroup初始化时未指定线程数,那么会使用默认线程数,
即 线程数 = CPU核心数 * 2;
二、每一个NioEventLoopGroup对象内部都有一组可执行的NioEventLoop(NioEventLoop对象内部包含的excutor对象为ThreadPerTaskExecutor类型)
三、每一个NioEventLoopGroup对象都有一个NioEventLoop选择器与之对应,其会根据NioEventLoop的个数,动态选择chooser(若是是2的幂次方,则按位运算,不然使用普通的轮询)
因此经过上面的分析,咱们得出NioEventLoopGroup主要功能就是为了选择NioEventLoop,而真正的重点就在NioEventLoop中,它是整个netty线程执行的关键。
下一篇咱们将详细介绍NioEventLoop,敬请期待。