先来看NioEventLoopGroup类的继承关系
从EventExecutorGroup开始是netty中实现的。EventExecutorGroup中主要增加了一个方法next(),这个方法主要是按一定的规则返回一个EventExecutor,以及一些关闭executors的方法。顺着往下看,左边的类主要是承担任务执行的主体类,主要看一下MultithreadEventExecutorGroup,这个类中有两个重要的域EventExecutor[]和EventExecutorChooserFactory.EventExecutorChooser,这个数组揭示了EventExecutorGroup和EventExecutor的关系,chooser完成返回EventExecutor的选取规则。右边的EventLoopGroup接口主要增加定义了将channel注册到EventLoop的register()方法。下面开始展开分析。
首先从NioEventLoopGroup的实例化开始。
在前两个类中主要设置了线程组的线程数量,然后主要的参数实例化都是在MultithreadEventExecutorGroup中的,来看MultithreadEventExecutorGroup构造器的代码
protected MultithreadEventExecutorGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory, Object... args) { if (nThreads <= 0) { throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads)); } //这里的executor起到创建线程的作用,它的execute方法内部就是new Thread().start() if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); } children = new EventExecutor[nThreads]; //依次创建EventExecutor,这里newChild方法的具体实现是在NioEventLoopGroup中的 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 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); } } }; //为每个EventExecutor添加监听器表示终止后要做的操作 for (EventExecutor e: children) { e.terminationFuture().addListener(terminationListener); } //记录只读children Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length); Collections.addAll(childrenSet, children); readonlyChildren = Collections.unmodifiableSet(childrenSet); }
总结一下要点1、EventLoopGroup其实就是EventLoop的集合,2、EventLoopGroup中的每个EventLoop构建时所传入的参数都一样。
接下来看NioEventLoop,先上继承关系图
比上面的那个复杂一些,但是总体上还是分主体类完成事件执行机制,实现接口增加EventExecutorGroup和EventExecutor的父子关系,其中EventExecutor中还定义了当前线程是否在事件循环执行中,以及是否是channel所绑定的EventLoop。
先看NioEventLoop的实例化过程
首先,父类构造调用一直到SingleThreadEventExecutor,这个类中有几个重要的域,其中thread这个域就是我们netty线程模型中实际工作的thread了,然后还有一个taskQueue<Runnable>这里面用于存放需要执行的业务逻辑,是一个阻塞队列。其构造器代码如下:
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"); }
可以看到这里面并没有初始化Thread域,那么什么时候才会去初始化线程实体,然后start呢,这个稍后再说。回到NioEventLoop中,NioEventLoop中也有一个重要的域,那便是Selector,netty中的channel需要注册到eventLoop中,其实也就是需要注册到EventLoop中的Selector上。
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; }
这里会将底层的Selector打开。至此所有准备工作都做好了,那么就要等待任务来,开启thread了。
先看SingleThreadEventExecutor中的execute方法
public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } //inEventLoop方法其实是比较当前线程和this.Thread是否“==”。刚开始this.thread还是NULL,故会执行else分支可以看到else中是一个startThread方法,
boolean inEventLoop = inEventLoop(); if (inEventLoop) { addTask(task); } else { startThread(); addTask(task); if (isShutdown() && removeTask(task)) { reject(); } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); }
我们先往后看是如何开启这个thread的然后再回过来看是谁调用了execute方法。
private void doStartThread() {
//断言thread是null,这是必然的,否则程序一定是出现了问题 assert thread == null;
//这里又是execute方法,那是不是和上一个execute方法相同呢,如果是这样那就成了互调死循环了,其实这里的execute的调用者executor是我们之前在EventLoopGroup中创建的然后
传过来的,追过去可以发现这个executor的实例是ThreadPerTaskExecutor,它的execute方法中的代码是{threadFactory.newThread(command).start()},
到这里会创建线程并执行传过来的runnable。我们再来看runnable中的逻辑,创建完线程后,首先将创建的线程赋值给当前eventloop的thread域,然后执行run方法,这里run是
抽象方法,实际的实现是在NioEventLoop中的。点过去发现run方法是一个无限循环的方法,至此就开始不停的netty底层的事件处理了。
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
if (interrupted) {
thread.interrupt();
}
boolean success = false;
updateLastExecutionTime();
try {
SingleThreadEventExecutor.this.run();
success = true;
}
。。。
}
回过头看是谁调用了SingleThreadEventExecutor中的execute方法,客户端发起的connect和服务端的bind的操作都会引发eventloop的execute方法的执行。
private static void doConnect( final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. final Channel channel = connectPromise.channel(); channel.eventLoop().execute(new Runnable() { @Override public void run() { if (localAddress == null) { channel.connect(remoteAddress, connectPromise); } else { channel.connect(remoteAddress, localAddress, connectPromise); } connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } }); }
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }
至此就完成了这些源码的主要分析。还有点需要注意。
由selector引发的操作,connect、read、write这些是IO事件,其他的是一些系统Task包括定时Task,这些任务都是由同样的NioEventLoop中的Thread来完成的,我们可以通过setIorait来调整io和系统task所占用的时间比。把IO线程和业务线程设计为公用一个thread是为了避免当IO线程和业务线程同时操作网络资源的时候竞争锁。这样分别给各自分出一些时间来执行任务可以实现局部的无锁化。