【1.0】NioEventLoopGroup + NioEventLoop分析

本文深入解析Netty的Reactor线程模型,详细分析NioEventLoopGroup、NioEventLoop构造函数及其实现原理。阐述了Netty如何高效处理事件循环和任务执行,介绍了线程数量配置、chooser策略初始化以及NioEventLoop对象创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、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的层次结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值