源码地址: GitHub
1.NioEventLoopGroup实例化过程
下面来分析对NioEventLoopGroup类进行实例化的过程中发生了什么。
NioEventLoopGroup 类层次结构
先给出类图:
我们查看上面接口的结构图:
ExecutorService模拟Java线程池接口,ScheduledExecutorService模拟定时器线程池接口。
对于NioEventLoopGroup核心的类继承关系就是:
NioEventLoopGroup –》MultithreadEventLoopGroup –》MultithreadEventExecutorGroup
面从这三个类出发分析NioEventLoopGroup实例化过程。
首先盗用一下网上的一张示意图
下面大致解释一下这个实例化过程做了什么
//对于不指定线程数参数的构造器,默认设置0 (但是在后面的构造器中会判断,如果设置为0 就会初始化为2*CPU)
public NioEventLoopGroup() {
this(0);
}
/**
* Create a new instance using the specified number of threads, {@link ThreadFactory} and the
* {@link SelectorProvider} which is returned by {@link SelectorProvider#provider()}.
*/
//然后调用:这里设置了NioEventLoopGroup线程池中每个线程执行器默认是null(这里设置为null,在后面的构造器中会判断,如果为null就实例化一个线程执行器)
public NioEventLoopGroup(int nThreads) {
this(nThreads, (Executor) null);
}
//再调用:这里就存在于JDK的NIO的交互了,这里设置了线程池的SelectorProvider, 通过SelectorProvider.provider() 返回。
public NioEventLoopGroup(int nThreads, Executor executor) {
this(nThreads, executor, SelectorProvider.provider());
}
//然后调用:在这个重载的构造器中又传入了默认的选择策略工厂DefaultSelectStrategyFactory;
public NioEventLoopGroup(
int nThreads, Executor executor, final SelectorProvider selectorProvider) {
this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}
//这里就是调用父类MultithreadEventLoopGroup的构造器了, 这里还添加了线程的拒绝执行策略。
public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,final SelectStrategyFactory selectStrategyFactory) {
super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}
在MultithreadEventLoopGroup构造器调用:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
接下来就是调用的基类MultithreadEventExecutorGroup的构造器:
//这个构造器里面多传入了一个参数 DefaultEventExecutorChooserFactory.INSTANCE ,
// 通过这个EventLoop选择器工厂可以实例化GenericEventExecutorChooser或者PowerOfTwoEventExecutorChooser
//这个类, 这个类是EventLoopGroup线程池里面的EventLoop的选择器,
//调用GenericEventExecutorChooser.next() 方法可以从线程池中选择出一个合适的EventLoop线程。
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}
然后就是重载调用MultithreadEventExecutorGroup类的构造器:
构造器调用到了这里其实也就是不断向上传递调用的终点了,由于构造器代码比较长,我就删除一些校验和不重要的代码,只保留核心代码:
/**
* 最终的创建实例构造器
*
* @param nThreads 该实例将使用的线程数
* @param executor 将要使用的executor, 默认为null
* @param chooserFactory 将要使用的EventExecutorChooserFactory
* @param args arguments which will passed to each {@link #newChild(Executor, Object...)} call
*/
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
/** 1.初始化线程池 */
//参数校验nThread合法性,
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
//executor校验非空, 如果为空就创建ThreadPerTaskExecutor, 该类实现了 Executor接口
//这个executor 是用来执行线程池中的所有的线程,也就是所有的NioEventLoop,其实从
//NioEventLoop构造器中也可以知道,NioEventLoop构造器中都传入了executor这个参数。
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
//这里的children数组, 其实就是线程池的核心实现,线程池中就是通过指定的线程数组来实现线程池;
//数组中每个元素其实就是一个EventLoop,EventLoop是EventExecutor的子接口。
children = new EventExecutor[nThreads];
//for循环实例化children数组,NioEventLoop对象
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
//newChild(executor, args) 函数在NioEventLoopGroup类中实现了,
// 实质就是就是存入了一个 NIOEventLoop类实例
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;
}
}
}
}
}
// 2.实例化线程工厂执行器选择器: 根据children获取选择器
chooser = chooserFactory.newChooser(children);
// 3.为每个EventLoop线程添加 线程终止监听器
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);
}
// 4. 将children 添加到对应的set集合中去重, 表示只可读。
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);//(EventLoop是EventExecutor的子接口)
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
总结一下上面的初始化步骤(3)中的一些重点:
1)NIOEventLoopGroup的线程池实现其实就是一个NIOEventLoop数组,一个NIOEventLoop可以理解成就是一个线程。
2)所有的NIOEventLoop线程是使用相同的 executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler以及是属于某一个NIOEventLoopGroup的。 这一点从 newChild(executor, args); 方法就可以看出: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]);
}
3)当有IO事件来时,需要从线程池中选择一个线程出来执行,这时候的NioEventLoop选择策略是由GenericEventExecutorChooser实现的, 并调用该类的next() 方法获取到下一个 NioEventLoop.
到了这里线程池的初始化就已经结束了, 基本这部分就只涉及 netty线程池的内容,不涉及到channel 与 channelPipeline和ChannelHandler等内容。下面的内容就是分析 NioEventLoop的构造器实现了。
2. NioEventLoop类层次结构
NioEventLoop 的类层次结构图还是比较复杂的, 不过我们只需要关注几个重要的点即可. 首先 NioEventLoop 的继承链如下:
NioEventLoop -> SingleThreadEventLoop -> SingleThreadEventExecutor -> AbstractScheduledEventExecutor
在 AbstractScheduledEventExecutor 中, Netty 实现了 NioEventLoop 的 schedule 功能, 即我们可以通过调用一个 NioEventLoop 实例的 schedule()方法来运行一些定时任务. 而在 SingleThreadEventLoop 中, 又实现了任务队列的功能, 通过它, 我们可以调用一个 NioEventLoop 实例的 execute() 方法来向任务队列中添加一个 task, 并由 NioEventLoop 进行调度执行.
通常来说, NioEventLoop 肩负着两种任务,:
1)第一个是作为 IO 线程, 执行与 Channel 相关的 IO 操作, 包括 调用 select 等待就绪的 IO 事件、读写数据与数据的处理等;
2)而第二个任务是作为任务队列, 执行 taskQueue 中的任务, 例如用户调用 eventLoop.schedule 提交的定时任务也是这个线程执行的.
NioEventLoop 的实例化过程
对于NioEventLoop的实例化,基本就是在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]);
}
从上面函数里面 new NioEventLoop()出发分析实例化过程:
(1)最先调用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、Executor、SelectorProvider、SelectStrategyFactory、RejectedExecutionHandler。从这里可以看出,一个NioEventLoop属于某一个NioEventLoopGroup, 且处于同一个NioEventLoopGroup下的所有NioEventLoop 公用Executor、SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler。
还有一点需要注意的是,这里的SelectorProvider构造参数传入的是通过在NioEventLoopGroup里面的构造器里面的 SelectorProvider.provider();方式获取的, 而这个方法返回的是一个单例SelectorProvider, 所以所有的NioEventLoop公用同一个单例SelectorProvider。
(2)核心的东西说完了,就是调用父类SingleThreadEventLoop的构造器:
protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedExecutionHandler) {
//父类构造器
super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
tailTasks = newTaskQueue(maxPendingTasks);
}
这里除了调用父类SingleThreadEventExecutor的构造器以外, 就是实例化了 tailTasks 这个变量;
对于tailTasks在SingleThreadEventLoop属性的定义如下:
private final Queue<Runnable> tailTasks;// 尾部任务队列
队列的数量maxPendingTasks参数默认是SingleThreadEventLoop.DEFAULT_MAX_PENDING_TASK,其实就是Integer.MAX_VALUE; 对于new的这个队列, 其实就是一个LinkedBlockingQueue 无界队列。
(3)再看调用的父类SingleThreadEventExecutor的构造器:
protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
boolean addTaskWakesUp, int maxPendingTasks,
RejectedExecutionHandler rejectedHandler) {
super(parent);// 设置EventLoop所属于的EventLoopGroup
this.addTaskWakesUp = addTaskWakesUp;
this.maxPendingTasks = Math.max(16, maxPendingTasks);//默认是Integer.MAX_VALUE
this.executor = ObjectUtil.checkNotNull(executor, "executor");
taskQueue = newTaskQueue(this.maxPendingTasks);//创建EventLoop的任务队列, 默认是 LinkedBlockingQueue
rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}
自此,NioEventLoop的实例化过程已经分析完毕。
3接下来分析ServerBootstrap 的引导过程
ServerBootstrap的继承结构图如下: ServerBootstrap中变量负责SocketChannel的生成,AbstractBootstrap负责ServerSocketChannel的生成。
服务端程序代码如下:(不重要的部分已经省略)
public class BaseServer {
...
public void start(){
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap sbs = new ServerBootstrap()
.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new BaseServerHandler());
};
}).option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// 绑定端口,开始接收进来的连接
ChannelFuture future = sbs.bind(port).sync();
System.out.println("Server start listen at " + port );
future.channel().closeFuture().sync();
} catch (Exception e) {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
...
}
这里调用时,我们传入了两个EventLoopGroup分别叫bossGroup和childGroup。boss这个EventLoopGroup作为一个acceptor负责接收来自客户端的请求,然后分发给worker这个EventLoopGroup来处理所有的时间event和channel的IO.
3.1设置EventLoopGroup对象
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
if (childGroup == null) {
throw new NullPointerException("childGroup");
}
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
查看上面源码可知,首先调用super.group() 这个方法调用ServerBootstrap的父类AbstractBootstrap的group(EventLoopGroup group) 方法,下面看看AbstractBootstrap方法的定义:
public B group(EventLoopGroup group) {
if (group == null) {
throw new NullPointerException("group");
}
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
return (B) this;
}
3.2 设置ServerBootstrap的channel() : channel(NioServerSocketChannel.class)
实际上是调用的是AbstractBootstrap里面的channel()函数,下面先粘贴出该方法的源码:
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
这里传入的是一个Class对象,根据传入的不同的Class对象,实例化不同的Channel,主要是有两种代表NIO和OIO的对象:NioServerSocketChannel和OioServerSocketChannel。在函数体的最后调用了:
channelFactory(new ReflectiveChannelFactory<C>(channelClass));
参数里面实例化了一个 ReflectiveChannelFactory对象,这个对象实现了ChannelFactory这个接口的。接下来进入这个函数
public B channelFactory(ChannelFactory<? extends C> channelFactory) {
if (channelFactory == null) {
throw new NullPointerException("channelFactory");
}
if (this.channelFactory != null) {
throw new IllegalStateException("channelFactory set already");
}
this.channelFactory = channelFactory;
return (B) this;
}
最后把new的 ReflectiveChannelFactory传递给了AbstractBootstrap的channelFactory属性,该属性定义如下:
private volatile ChannelFactory<? extends C> channelFactory;
3.3 接下来配置ServerBootstrap的childHandler(ChannelHandler childHandler)
该函数的主要作用是设置channelHandler来处理客户端的请求的channel的IO,这里我们一般都用ChannelInitializer这个类的实例或则继承自这个类的实例,我们实现如下:
childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024,true,true));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new BaseServerHandler());
};
})
我们ChannelInitializer这个类的继承图:
ChannelInitializer继承自ChannelHandler。 并且这个类其实就是往pipeline中添加了很多的channelHandler,在ServerBootstrap.childHandler(ChannelHandler childHandler)中的实现如下:传入的childHandler赋值给ServerBootstrap的childHandler属性。
3.4 接下来配置ServerBootstrap的option(ChannelOption option , T value);
这里调用父类AbstractBootstrap的option()方法,源码如下:
public <T> B option(ChannelOption<T> option, T value) {
if (option == null) {
throw new NullPointerException("option");
}
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return (B) this;
}
重要代码是options.put(option,value); 这里用到了options这个参数,在AbstractBootstrap的定义如下:
private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
可知是私有变量,而且是一个Map集合,这个变量主要是设置TCP链接中的一些可选项,而且这些属性是作用于每一个连接到服务器被创建的channel.
3.5 然后调用ServerBootstrap的childOption(ChannelOption childOption,T value);源码如下:
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
if (childOption == null) {
throw new NullPointerException("childOption");
}
if (value == null) {
synchronized (childOptions) {
childOptions.remove(childOption);
}
} else {
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}
这个函数和option()函数类似,唯一区别是该属性设定只作用于被acceptor(也就是boss EventLoopGroup)接收之后的channel。
3.6 设置ServerBootstrap的绑定端口和启动:bind(int port)
前面已经分析完了EventLoopGroup和EventLoop,那么有一个问题,我们知道一个EventLoop实际上是对应于一个线程,那么这个EventLoop是什么时候启动的呢?
//未完待续