netty源码--NioEventLoopGroup的初始化

5 篇文章 0 订阅
4 篇文章 0 订阅

NioEventLoopGroup的初始化


源码版本4.1.66

类图

从继承关系和线程组的作用来猜想:

  1. NioEventLoopGroup作为Executor的子类将会提供任务执行的方法(execute)
  2. 而作为ExecutorService的子类,它肯定也会为我们提供异步执行任务的方法(submit)
  3. 作为SchedualExecutorService的子类,它肯定也支持定时任务调度(schedule)
  4. NioEventLoopGroup作为一个线程组,当我们提交任务给它执行时,涉及到线程组中的线程选择,那么也肯定涉及到相应的选择器(next方法)

初始化过程

通常在创建服务端ServerBootstrap时,通常会初始化boss和worker两个线程组,而一般使用的就是NioEventLoopGroup的构造方法进行创建:

// 无参构造
public NioEventLoopGroup() {
    this(0);
}
// nThread 默认创建的线程数量
public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
}

我们可以通过指定线程数量创建指定NioEventLoop数量的NioEventLoopGroup(也可以使用无参构造,不指定数量),它会调用父类MultithreadEventLoopGroup的构造方法进行初始化:

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    // 再调用父类 MultithreadEventExecutorGroup 的构造
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

MultithreadEventLoopGroup为NioEventLoopGroup提供多线程初始化时的部分实现,其中包括指定线程的数量:

  1. 当指定线程数量参数时,按指定的数量进行创建
  2. 当不指定线程数量参数时,则会默认使用CPU核心数*2的数量创建

接下来再调用父类 MultithreadEventExecutorGroup 的构造方法。

MultithreadEventExecutorGroup构造

该类中有两个参数需要特别注意:

private final EventExecutor[] children;
private final EventExecutorChooserFactory.EventExecutorChooser chooser;
  • 第一个EventExecutor就是NioEventLoop的父类,children也就是该group对应的线程组。
  • 第二个则是为选择线程提供策略的选择器

接下来一起继续看构造方法,上一步在MultithreadEventLoopGroup中指定了线程数量之后,调用父类的构造如下:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor, Object... args) {
    this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args);
}

在这一步会传入一个参数 DefaultEventExecutorChooserFactory.INSTANCE,它的作用是:当需要从线程组中选出一个元素执行任务时,为我们提供选择策略工厂,为接下来初始化选择器做准备。

接下来,就到了关键的地方,这里先把代码贴出来,然后一步步进行分析:

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    checkPositive(nThreads, "nThreads");// 校验线程数量的合法性
	// 第一步:初始化ThreadPerTaskExecutor
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
	
    children = new EventExecutor[nThreads];
	// 第二步,初始化线程组
    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) {
                // 初始化失败要做的事,省略...
            }
        }
    }
	// 第三步:初始化选择器
    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);
            }
        }
    };

    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }

    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

其初始化大致分为以下三步

  1. 初始化任务执行器executor
  2. 初始化线程组children
  3. 初始化线程选择器chooser

接下来一步步看,首先是ThreadPerTaskExecutor的初始化。

初始化任务执行器executor

任务执行器的类型是ThreadPerTaskExecutor,通过查看它的类图可以看到它是Executor的子类,内部只提供了包含线程工厂的构造方法执行任务的execute方法

在这里插入图片描述

在上一步初始化ThreadPerTaskExecutor的过程中,传入netty提供的默认的线程工厂DefaultThreadFactory

if (executor == null) {
    executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}

execute()方法

ThreadPerTaskExecutor这个执行器通过execute()方法执行任务时的代码如下:

@Override
public void execute(Runnable command) {
    threadFactory.newThread(command).start();
}

从这里可以得知:在以后的任务执行中,将会调用DefaultThreadFactory的newThread方法获取一个线程,然后调用线程的start方法异步执行该任务

DefaultThreadFactory.newThread()

@Override
public Thread newThread(Runnable r) {
	// 将runnable包装成一个FastThreadLocalRunnable
    Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet());
    try {
        if (t.isDaemon() != daemon) {
            t.setDaemon(daemon);
        }

        if (t.getPriority() != priority) {
            t.setPriority(priority);
        }
    } catch (Exception ignored) {
        // Doesn't matter even if failed to set.
    }
    return t;
}
// 新建一个FastThreadLocalThread
protected Thread newThread(Runnable r, String name) {
    return new FastThreadLocalThread(threadGroup, r, name);
}

该方法主要分为以下两步:

  • 将任务Runnable包装成一个FastThreadLocalRunnable
  • 新建一个Thread子类FastThreadLocalThread
FastThreadLocalRunnable

FastThreadLocalRunnable是对Runnable的一层包装,内部重写了run()方法,在任务执行完毕之后添加了对线程变量FastThreadLocal的remove操作:

@Override
public void run() {
    try {
        runnable.run();
    } finally {
        FastThreadLocal.removeAll();
    }
}
FastThreadLocalThread

FastThreadLocalThread作为线程Thread的子类,构造方法中完成了线程的初始化操作:

public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
    super(group, FastThreadLocalRunnable.wrap(target), name);
    cleanupFastThreadLocals = true;
}

至于FastThreadLocalThread及FastThreadLocal的优点我们以后再详细探究…

关于ThreadPerTaskExecutor的作用就介绍到这里,接下来一起看这个executor将会在下面发挥什么作用。

初始化线程组children

先把代码片段贴出来:

children = new EventExecutor[nThreads];// EventExecutor数组

for (int i = 0; i < nThreads; i ++) {
    boolean success = false;
    try {
        children[i] = newChild(executor, args);
        success = true;
    } catch (Exception e) {
        //...
    } finally {
        if (!success) {
            // 执行失败要做的事,省略...
        }
    }
}

不难看出这里将会通过循环的方式初始化数组children,newChild方法会把刚才创建的 ThreadPerTaskExecutor 当作参数传入。

这里就不难想到:该任务执行器会为NioEventLoop执行任务提供帮助(创建FastThreadLocalThread并执行任务)。

接下来一起看一下newChild,newChild方法是 MultithreadEventExecutorGroup 提供的一个模板方法,在这里由子类 NioEventLoopGroup 进行实现:

protected abstract EventExecutor newChild(Executor executor, Object... args) throws Exception;

// NioEventLoopGroup 的实现
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    SelectorProvider selectorProvider = (SelectorProvider) args[0];
    SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
    RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];
    EventLoopTaskQueueFactory taskQueueFactory = null;
    EventLoopTaskQueueFactory tailTaskQueueFactory = null;

    int argsLength = args.length;
    if (argsLength > 3) {
        taskQueueFactory = (EventLoopTaskQueueFactory) args[3];
    }
    if (argsLength > 4) {
        tailTaskQueueFactory = (EventLoopTaskQueueFactory) args[4];
    }
    return new NioEventLoop(this, executor, selectorProvider,
                            selectStrategyFactory.newSelectStrategy(),
                            rejectedExecutionHandler, taskQueueFactory, tailTaskQueueFactory);
}

这里暂时先把关注点放在最后一行:初始化NioEventLoop

NioEventLoop初始化

先来看一下NioEventLoop的类关系图:

在这里插入图片描述

然后再来看它的构造方法:

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory taskQueueFactory, EventLoopTaskQueueFactory tailTaskQueueFactory) {
    // 调用父类构造
    super(parent, executor, false, newTaskQueue(taskQueueFactory), 
          newTaskQueue(tailTaskQueueFactory),  rejectedExecutionHandler);
    this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
    this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");
    // 初始化Selector
    final SelectorTuple selectorTuple = openSelector();
    this.selector = selectorTuple.selector;
    this.unwrappedSelector = selectorTuple.unwrappedSelector;
}

接下来一步步看他的构造过程:

1,首先新建任务队列

newTaskQueue()方法最终将会创建一个多生产者单消费者模型的队列,这里我们后面再详细探究:

private static Queue<Runnable> newTaskQueue0(int maxPendingTasks) {
    // This event loop never calls takeTask()
    return maxPendingTasks == Integer.MAX_VALUE ? PlatformDependent.<Runnable>newMpscQueue()
            : PlatformDependent.<Runnable>newMpscQueue(maxPendingTasks);
}

2,然后调用SingleThreadEventLoop构造

这里将继续调用父类 SingleThreadEventExecutor 的构造,然后把 tailTaskQueue 赋值给 SingleThreadEventLoop 的 tailTasks 属性上:

protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                boolean addTaskWakesUp, Queue<Runnable> taskQueue, Queue<Runnable> tailTaskQueue,
                                RejectedExecutionHandler rejectedExecutionHandler) {
    super(parent, executor, addTaskWakesUp, taskQueue, rejectedExecutionHandler);
    tailTasks = ObjectUtil.checkNotNull(tailTaskQueue, "tailTaskQueue");
}

父类 SingleThreadEventExecutor 的构造主要目的是指定任务队列、执行器等参数,这里就不再继续下去了。

3,初始化Selector

回到NioEventLoop构造方法的这三行:

final SelectorTuple selectorTuple = openSelector();
this.selector = selectorTuple.selector;
this.unwrappedSelector = selectorTuple.unwrappedSelector;

openSelector() 用于初始化Selector:

  1. 获取到Java层面的Selector(java.nio.channels)
  2. 将该Selector包装成一个SelectedSelectionKeySetSelector

到这里NioEventLoop的初始化就结束了,了解Reactor模型的话,不难猜到后续NioEventLoop必然会作为一个线程去处理连接或读写请求,这里就涉及到客户端如何处理连接、读、写请求的内容,后面再详细探究。

初始化选择器chooser

这里通常情况下是创建一个GenericEventExecutorChooser来作为选择器,当我们需要选取一个组里的线程去执行任务时,将会通过该选择器的提供的next()策略来挑选:

@Override
public EventExecutor next() {
    return executors[(int) Math.abs(idx.getAndIncrement() % executors.length)];
}

这里可以看到这里选取的方式就是针对当前索引%数组长度,说白了就是挨个取。

总结

到这里整个NioEventLoopGroup的初始化就大致介绍完成了,也算是对它有了一个比较初步的了解,后面我们再对本文涉及的一些内容如:FastThreadLocal、MpScQueue、ServerBootstrap的初始化等等内容进行详细探究。

下一节:
ServerBootstrap的初始化及启动流程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值