netty源码阅读(一)之NioEventLoop创建
netty服务端统一模板示例:
NioEventLoop创建分析
看了服务端统一模板示例之后,首先可以看到创建了两个NioEventLoopGroup对象,分别调用了两种构造函数创建。
分别看下它的两个参构造器:
可以看到最终无参的构造器也调用了那个有参的构造器只不过参数是0。
一直追踪下去发现最终调用了另外一个构造函数并在哪里调用了父类的构造函数:
父类(MultithreadEventLoopGroup)构造函数:
那么在这里可以看到作者判断了nThreads是否等于0,如果是0那么就传入DEFAULT_EVENT_LOOP_THREADS否则就把nThreads本身传进去,回到之前可以看到nThreads在无参构造函数中是0,在有参构造函数中是传入的值1。
接下来看下DEFAULT_EVENT_LOOP_THREADS的值是多少:
可以看到首先尝试从配置文件里面读io.netty.eventLoopThreads如果没有就获取cpu核心数*2,然后和1比较取最大的值。
然后继续调用父类(MultithreadEventExecutorGroup)的构造函数:
可以看到最终创建逻辑都在MultithreadEventExecutorGroup的构造函数中:
ThreadPerTaskExecutor的创建:
首先判断了nThreads小于0,如果是则抛出参数异常,然后判断executor是否为null,如果是则创建一个ThreadPerTaskExecutor,追溯到NioEventLoopGroup的一参的构造函数可以看到executor直接就是一个null传入的,所以此刻executor=null。
查看ThreadPerTaskExecutor源码可以看到:
ThreadPerTaskExecutor这个类其实就是把ThreadFactory保存起来,然后在调用execute直接将任务交给ThreadFactory去做。
那么这里ThreadFactory就是由newDefaultThreadFactory()方法创建的一个对象。
可以看到ThreadFactory就是DefaultThreadFactory这个类的实例:
以上就是new DefaultThreadFactory(getClass());调用顺序了,那么在第二处调用可以看到作者调用了toPoolName()方法,那这个方法的作用就是返回线程名字的前面固定的格式的字符串。
可以看到首先通过class获取到类名poolName,然后判断他的长度,如果是0那么返回unknown,如果是1就将它变成小写返回,如果大于1时判断poolName的第0个字符是大写并且第1个字符是小写那个就将它的第0个字符变成小写并返回,否则直接返回。所以这个方法的作用就是通过类获取到类名再将它的首字母变成小写返回。
最后看最后一个构造函数,最后一个构造函数除了保留必要的参数外还保存了成员变量prefix= poolName + '-' + poolId.incrementAndGet() + '-';在这里可以看到prefix的值就是“处理后的类名-一个自增的id-”的格式。
回到上面ThreadPerTaskExecutor的类中可以看到它的方法execute就是调用DefaultThreadFactory的newThread方法获取一个线程然后启动。所以接下来看DefaultThreadFactory的Thread newThread(Runnable r)的方法:
这里可以看到这个方法继续调用了另外一个newThread方法获取线程对象然后设置一些参数,最后返回,接下来看另外一个newThread方法:
可以看到newThread直接new了一个FastThreadLocalThread对象,而FastThreadLocalThread这个对象继承与Thread再这基础上增加了一个成员变量而已。所以FastThreadLocalThread其实就可以看做一个比普通的Thead多一点点东西的类。
回到MultithreadEventExecutorGroup这个类,到这里ThreadPerTaskExecutor就创建完了并且赋值给了executor变量。
接下来就是EventLoop的创建了:
从源码中可以看到首先创建了一个EventExecutor的数组,长度就是从最开始传进来的nThreads了。
然后对数组中的每一个元素利用newChild()方法逐一创建对象赋值。点进去可以看到这个方法是个抽象方法,具体实现在我们的最开始的那个子类(NioEventLoopGroup)中:
可以看到它直接创建了一个NioEventLoop并返回,查看这个构造函数源码:
它继续调用了父类的方法,然后再将SelectorProvider进行了保存并创建了一个Selector并且保存:
到这里可以看到他将executor(上一大步创建的ThreadPerTaskExecutor对象进行了保存)。
最后一步创建EventExecutorChooser(线程选择器)
回到MultithreadEventExecutorGroup的最主要的那个构造方法,在EventLoop创建完成之后:
在这里利用chooserFactory这个工厂的newChooser()方法创建EventExecutorChooser,参数呢就是刚才创建的EventLoop数组,接下来从上一个构造函数中找chooserFactory这个对象:
然后找到newChooser()方法:
那么isPowerOfTwo()这个方法的作用就是判断参数是不是2的幂,如果是就创建一个PowerOfTowEventExecutorChooser实例不是就创建一个GenericEventExecutorChooser实例:
可以看到这个两个类之间的差别就在于next()这个方法,这两个方法的作用都是返回数组中的下一个元素,如果第一次取就返回第0个元素,第二次取返回第1个元素,如果当前以及在最后一个了就取第0个元素返回,接着又是第一个,依次循环。
那么这两个类最大的车别就在于获取数组下标的方式不同,GenericEventExecutorChooser是直接自增然后对数组的长度取模再取绝对值,而PowerOfTowEventExecutorChooser是以自增后和数组的长度减一进行与运算得到下标。
到这里线程选择器也创建完了。整个NioEventLoop的创建也已经大致走完了!!!!!!