文章目录
一、NioEventLoopGroup
下面是服务器务端启动必须的两句代码:
class SimpleServer {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();//...
}
(1.1)NioEventLoopGroup构造函数分析
public NioEventLoopGroup() { this(0); }
public NioEventLoopGroup(int nThreads) { this(nThreads, null); }
public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
this(nThreads, threadFactory, SelectorProvider.provider());
}
public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory,
final SelectorProvider selectorProvider) {
super(nThreads, threadFactory, selectorProvider);
}
构造函数最终调用的super.MultithreadEventLoopGroup的构造函数:
NioEventLoopGroup extends MultithreadEventLoopGroup
implements MultithreadEventExecutorGroup
protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads,
threadFactory, args);
}
该函数说:不指定线程个数: EventLoopGroup workerGroup = new NioEventLoopGroup()创建对象,netty使用默认的线程个数,否则,用指定的线程个数:执行如下代码
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
"io.netty.eventLoopThreads", Runtime.getRuntime().availableProcessors()*2));
if(logger.isDebugEnabled()) {
logger.debug("-Dio.netty.eventLoopThreads: {}",
DEFAULT_EVENT_LOOP_THREADS);
}
}
SystemPropertyUtil.getInt() 的功能为:得到系统属性中指定key所对应的value(“io.netty.eventLoopThreads”),如果获取不到则返回默认值:cpu的核数*2,SystemPropertyUtil.java 类中的 getInt() 方法的代码如下:
public static int getInt(String key, int def) {
String value = get(key);
if (value == null) { return def; }
value = value.trim().toLowerCase();
//Pattern INTEGER_PATTERN = Pattern.compile("-?[0-9]+");
if(INTEGER_PATTERN.matcher(value).matches()) {
try {
return Integer.parseInt(value);
} catch (Exception e) { // Ignore }
}
log("Unable to parse the integer system property '" + key + "':" + value + " - " +
"using the default value: " + def);
return def;
}
优雅之处:将String类型转换为数字之前要进行正则匹配。
由于MultithreadEventLoopGroup的构造函数是调用的是其父类MultithreadEventExecutorGroup的构造函数,因此,看下此类的构造函数
(1.1.1)MultithreadEventExecutorGroup
当用户传入的参数为0时,netty设置Executor为null.
public abstract class MultithreadEventExecutorGroup extends AbstractEventExecutorGroup {
private final EventExecutor[] children;
//...
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) { //每一个任务的执行器(PerTask)
executor = new ThreadPerTaskExecutor(new DefaultThreadFactory());
}
children = new EventExecutor[nThreads];
//根据线程个数是否为2的幂次方,采用不同策略初始化chooser
if(isPowerOfTwo(children.length)) { chooser = new PowerOfTwoEventExecutorChooser(); }
else { chooser = new GenericEventExecutorChooser();}
//产生nTreads个NioEventLoop对象保存在children数组中
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 {
//如果newChild方法执行失败,则对前面执行new成功的几个NioEventLoop进行shutdown处理
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) {
Thread.currentThread().interrupt();
break;
}
}
}
}
}
1. 产生一个线程工厂:new DefaultThreadFactory():利用线程工厂,将线程的创建与要执行的任务解耦!
//MultithreadEventExecutorGroup.java:
protected ThreadFactory DefaultThreadFactory() {
return new DefaultThreadFactory(getClass());//getClass()为:NioEventLoopGroup.class
}
//DefaultThreadFactory.java:
@poolType: //定义线程池类型,最后转换成小写的线程池名字
@NORM_PRIORITY://定义线程的正常优先级(5)
@false: //标记是否为后台线程
public DefaultThreadFactory(Class<?> poolType) {
this(poolType, false, Thread.NORM_PRIORITY);
}
//点击this进入 --->
public DefaultThreadFactory(Class<?> poolType, boolean daemon, int priority) {
this(toPoolName(poolType), daemon, priority);
}
2.将创建好的DefaultThreadFactory作为参数传递给 ThreadPerTaskExecutor
public finla class ThreadPerTaskExecutor implements Executor{
private ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if(threadFactory == null) {
throw new NullPointerException("threadFactory");
}
this.threadFactory = threadFactory;
}
@Override
public void executor(Runnable command) { //command(命令)
threadFactory.newThread(command).start();
}
}
ThreadPerTaskExecutor为一个继承Executor类的final类,作用是将threadFactory初始化,并实现了executor()方法(此处使用代理模式 + 命令模式,使线程的创建于任务的提交解耦),新的线程会执行executor()方法所给定的command参数内容。
3. 根据线程个数是否为2的幂次方,采用不同策略初始化chooser
判断一个数是否为2的幂次方就很巧妙,代码如下,这些都是我们需要学习的点。
- chooser:从children中选取一个eventLoop的策略。
private static boolean isPowerOfTwo(int val) { return (val & -val) == val;}
3. 产生nThreads个NioEventLoop对象保存在children数组中 (children数组为成员变量:EventExecutor[] children),线程都是通过调用newChild()方法来产生的。
- children:EventExecutor数组,保存eventLoop。
- newChild():new一个NioEventLoop对象,具体如下:
@Override
protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception {
return new NioEventLoop(this, threadFactory,
(SelectorProvider)args[0]);
}
NioEventLoop的作用会在下面讲述 ---->
二、reactor线程的面纱(一)
netty最核心的就是reactor线程,对应项目中使用广泛的NioEventLoop,那么NioEventLoop里面到底在干些什么事?netty是如何保证事件循环的高效轮询和任务的及时执行?先看关系
(2.1)NioEventLoop构造函数分析
NioEventLoop(NioEventLoopGroup parent, ThreadFactory threadFactory,
SelectorProvider selectorProvider) {
super(parent, threadFactory, false);
if(selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
provider = selectorProvider;
selector = openSelector();
}
其父类SingleThreadEventLoop的构造
protected SingleThreadEventLoop(EventLoopGroup parent, ThreadFactory threadFactory,
boolean addTaskWakesUp) {
super(parent, threadFactory, addTaskWakesUp);
}
又调用父类SingleThreadEventExecutor的构造函数
几个主要的变量:
1、state:线程池当前的状态
2、taskQueue:存放任务的队列
3、thread:线程池维护的唯一线程
4、scheduledTaskQueue:定义在其父类AbstractScheduledEventExecutor中,用以保存延迟执行的任务。
protected SingleThreadEventExecutor(EventExecutorGroup parent, ThreadFactory threadFactory,
boolean addTaskWakesUp) {
if(threadFactory == null) { throw new NullPointerException("threadFactory"); }
this.parent = parent; this.addTaskWakesUp = addTaskWakesUp;//false
thread = threadFactory.newThread(new Runnable() {
@Override
public void run() {
boolean success = false;
updateLastExecutionTime();
try {//调用NioEventLoop类的run方法
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
for(;;) {
int oldState = STATE_UPDATER.get(SingleThreadEventExecutor.this);
if(oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet(
SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) {
break;
}
}
// Check if confirmShutdown() was called at the end of the loop.
if(success && gracefulShutdownStartTime == 0) {
logger.error(
"Buggy " + EventExecutor.class.getSimpleName() + " implementation; " +
SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must be called " +
"before run() implementation terminates.");
}
try { // Run all remaining tasks and shutdown hooks.
for (;;) { if(confirmShutdown()) { break; } }
} finally {
try { cleanup(); }
finally {
STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED);
threadLock.release();
if(!taskQueue.isEmpty()) {
logger.warn(
"An event executor terminated with " +
"non-empty task queue (" + taskQueue.size() + ')');
}
terminationFuture.setSuccess(null);
}
}
}
}
});
taskQueue = newTaskQueue();
}
protected Queue<Runnable> newTaskQueue() {
return new LinkedBlockingQueue<Runnable>();
}
NioEventLoop的run()方法是reactor线程的主体,在第一次添加任务的时候被启动,其中SingleThreadEventExecutor.this.run(); 点击run,发现其父类 SingleThreadEventExecutor 的execute()方法会被调用
@Override
public void execute(Runnable task) {
...
boolean inEventLoop = inEventLoop();
if (inEventLoop) { addTask(task); }
else {
startThread();
addTask(task);
...
}
...
}
外部线程在往任务队列里面添加任务的时候执行 startThread() ,netty会判断reactor线程有没有被启动,如果没有被启动,那就启动线程再往任务队列里面添加任务
private void startThread() {
if(STATE_UPDATER.get(this) == ST_NOT_STARTED) {
if(STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) {
doStartThread();
}
}
}
SingleThreadEventExecutor 在执行 doStartThread() 的时候,会调用内部执行器executor的 execute() 方法,将调用NioEventLoop的run方法的过程封装成一个runnable塞到一个由executor创建的线程中去执行。(executor 默认是ThreadPerTaskExecutor)
private void doStartThread() {
...
executor.execute(new Runnable() {
@Override
public void run() {
thread = Thread.currentThread();
...
SingleThreadEventExecutor.this.run();
...
}
}
}
默认情况下,ThreadPerTaskExecutor 在每次执行 execute() 方法的时候都会通过DefaultThreadFactory创建一个FastThreadLocalThread线程(netty中的reactor线程实体)
(2.2)ThreadPerTaskExecutor
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
1. 利用ThreadFactory创建来一个Thread,传入了一个Runnable对象,该Runnable重写的run代码仅仅是调用NioEventLoop类的run方法。其他的异常处理和收尾工作在这先不管。
2. 使用LinkedBlockingQueue类初始化taskQueue
其中,发现threadFactory.newThread(new Runnable{…}),点进去发现:
DefaultThreadFactory.java
@Override
public Thread newThread(Runnable r) {
Thread t = newThread(new DefaultRunnableDecorator(r), prefix + nextId.incrementAndGet());
try{//判断是否是守护线程,并进行设置
if(t.isDaemon()) { if (!daemon) { t.setDaemon(false); } }
else { if (daemon) { t.setDaemon(true); } }
//设置其优先级
if(t.getPriority() != priority) {
t.setPriority(priority);
}
} catch (Exception ignored) {
// Doesn't matter even if failed to set.
}
return t;
}
protected Thread newThread(Runnable r, String name) {
return new FastThreadLocalThread(r, name);
}
FastThreadLocalThread.java:
public FastThreadLocalThread(Runnable target, String name) {
super(target, name);// FastThreadLocalThread扩展了Thread
}
底层还是借助于类似于Thread thread = new Thread(.r)这种方式来创建线程。
关于NioEventLoop对象的作为:初始化如下4个属性
1. NioEventLoopGroup(在父类SingleThreadEventExecutor中)
2、selector
3、provider
4、thread(在父类SingleThreadEventExecutor中)
三、总结
1、new NioEventLoopGroup()时,不指定线程数,则线程数为:CPU的核数*2
2、MultithreadEventExecutorGroup的构造方法根据线程个数是否为2的幂次方,采用不同策略初始化chooser
3、产生nThreads个NioEventLoop对象保存在children数组中。
可以理解NioEventLoop就是一个线程,里面有如下几个属性:
1、NioEventLoopGroup (在父类SingleThreadEventExecutor中)
2、selector
3、provider
4、thread (在父类SingleThreadEventExecutor中)
NioEventLoop中维护了一个线程,线程启动时会调用NioEventLoop的run方法,执行I/O任务和非I/O任务:
I/O任务:即selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。
非IO任务:添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks方法触发。
NioEventLoop做了以下几件事情:
1、轮询IO事件
2、处理轮询到的事件
3、执行任务队列中的任务
NioEventLoopGroup线程池中有N个NioEventLoop线程。
四、各个类之间的关系
NioEventLoopGroup的层次结构
NioLoopGroup的层次结构