【Netty源码系列】—— NioEventLoopGroup初始化(一)

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

零. Reactor模型

        Netty中所使用的Reactor模型如上图所示,本节索要讲到的部分与mainReactor与subReactor息息相关。

一. NioEventLoopGroup与NioEventLoop之间的关系

1. NioEventLoopGroup

首先来看一下NioEventLoopGroup的继承关系:

我们来挨个梳理一下:

1. Executor接口

  • 核心能力: 提供最基础的任务执行抽象
  • 关键方法: execute(Runnable command)
  • 具体作用:
    • 将任务提交与执行策略分离
    • 屏蔽线程创建和管理的细节
    • 为上层提供统一的任务提交入口

2. ExecutorService接口

  • 增加能力:
    • 生命周期管理:
      • shutdown(): 平滑关闭,拒绝新任务但完成已提交任务
      • shutdownNow(): 尝试中断正在执行的任务并返回等待执行的任务列表
      • isShutdown()/isTerminated(): 检查服务状态
    • 任务提交增强:
      • submit(Runnable): 提交任务并返回Future
      • submit(Callable): 提交可返回结果的任务
    • 批量操作:
      • invokeAll(): 执行所有任务并等待全部完成
      • invokeAny(): 执行任务集合中任意一个完成即可返回

3. ScheduledExecutorService接口

  • 增加能力:
    • 延迟执行:
      • schedule(Runnable, delay, unit): 延迟指定时间后执行一次
      • schedule(Callable, delay, unit): 延迟执行并返回结果
    • 周期性执行:
      • scheduleAtFixedRate(): 固定速率执行,不考虑任务执行时间
      • scheduleWithFixedDelay(): 固定延迟执行,考虑任务执行时间
    • 返回特殊Future:
      •  ScheduledFuture: 可获取剩余延迟等信息

4. EventExecutorGroup接口

  • 增加能力:
    • EventExecutor管理:
      • next(): 返回下一个可用的EventExecutor,实现负载均衡
      • iterator(): 遍历所有管理的EventExecutor
    • 增强的生命周期管理:
      • shutdownGracefully(): 带静默期的优雅关闭
      • isShuttingDown(): 检查是否正在关闭
      • terminationFuture(): 获取终止完成的Future
    • Netty特有的Future支持:
      • 返回Netty的Promise和Future,而非JDK标准Future

5. AbstractEventExecutorGroup抽象类

  • 增加能力:
    • 方法委托模式:
      • 将大部分ExecutorService方法委托给next()返回的EventExecutor
      • 简化了实现,只需专注于next()方法和生命周期管理

6. MultithreadEventExecutorGroup抽象类

  • 增加能力:
    • 多线程管理:
      • 创建和管理多个EventExecutor实例
      • 通过数组children存储所有EventExecutor
    • 线程工厂
    • EventExecutor选择策略:
    • 任务拒绝处理:
    • 统一终止管理:
      • 实现terminationFuture()返回所有EventExecutor终止的聚合Future

7. MultithreadEventLoopGroup抽象类

  • 实现: EventLoopGroup接口,该接口定义了一个EventLoop该具备的能力
  • 增加能力:
    • Channel注册:
      • register(Channel): 将Channel注册到EventLoop
      • register(ChannelPromise): 带Promise的注册

8. NioEventLoopGroup具体类

  • 增加能力:
    • NIO特定功能:
      • 使用SelectorProvider创建Java NIO Selector
      • 默认使用SelectorProvider.provider()获取系统默认提供者
    • IO操作比例控制:
      • setIoRatio(int): 控制IO任务与非IO任务的执行比例
    • Selector优化:
      • rebuildSelectors(): 解决JDK NIO的epoll bug
    • SelectStrategy:
      • 控制select操作的策略(立即/阻塞/选择性阻塞)
    • NioEventLoop创建:
      • 创建特定于NIO的EventLoop实现

        EventExecutorGroup是一个通用化的执行器容器,管理了一堆形如EventExecutor的通用执行器。EventExecutorGroup它本身并没有相关能力,相关能力的实现都是通过调用自身管理的通用执行器进行实现的,例如AbstractEventExecutorGroup与MultithreadEventExecutorGroup抽象类实现相应方法时,都是通过next()方法找到下一个可用的通用执行器EventExecutor,调用通用执行器的相关能力完成操作。

        将EventExecutorGroup继续特化,特化为类似XXXEventLoopGroup时,相当于将通用执行器组特化为专门管理网络处理的执行器组。例如MultithreadEventLoopGroup实现EventLoopGroup接口后,执行器组就拥有了管理网络相关组建的能力(其实是所管理的执行器提供的能力)。

        总结:EventExecutorGroup是通用执行器容器,EventLoopGroup是其网络处理特化版本,大部分能力都是通过委托给内部管理的执行器实现的。

2. NioEventLoop

看一下NioEventLoopGroup的继承关系:

Executor,ExecutorService,ScheduledExecutorService与上文类似。我们来讲一下不同的地方:

1. EventExecutor接口

  • 增加能力:
    • Netty特有的Future支持:
      • newPromise(): 创建Promise对象
      • newSucceededFuture()/newFailedFuture(): 创建已完成的Future
    • 执行器关系:
      • parent(): 获取父执行器组EventExecutorGroup
      • inEventLoop(): 检查当前线程是否为事件循环线程
    • 增强的生命周期管理:
      • shutdownGracefully(): 带静默期的优雅关闭
      • terminationFuture(): 获取终止完成的Future

2. OrderedEventExecutor接口

  • 增加能力:
    • 顺序保证:
      • 确保任务按提交顺序执行
      • 为Channel处理提供线程安全保证
    • 标记接口特性:
      • 没有定义新方法,但提供类型标记
      • 允许系统识别支持顺序执行的执行器

3. EventLoop接口

  • 增加能力:
    • 执行器组关系:
      • parent(): 返回EventLoopGroup

4. AbstractEventExecutor抽象类

  • 增加能力:
    • 基础实现:
      • 实现EventExecutor的大部分方法
      • 提供inEventLoop(Thread)的默认实现
    • Future工厂方法:
      • 实现newPromise(), newSucceededFuture()等方法

5. AbstractScheduledEventExecutor抽象类

  • 增加能力:
    • 任务调度队列:
      • 维护PriorityQueue<ScheduledFutureTask<?>>调度队列
      • 按执行时间排序的优先级队列
    • 调度任务管理:
      • schedule(): 实现所有ScheduledExecutorService方法
      • cancelScheduledTasks(): 取消所有调度任务
    • 时间轮算法支持:
      • 高效处理大量定时任务
      • 优化任务触发时间计算

6. SingleThreadEventExecutor抽象类

  • 增加能力:
    • 单线程执行模型:
      • 维护一个专用线程处理所有任务
      • 实现thread变量跟踪执行线程
    • 任务队列管理:
      • 维护taskQueue存储待执行任务
      • 支持多种队列实现(有界/无界)
    • 线程启动与关闭:
      • 延迟启动线程直到首次任务提交
      • 实现优雅关闭流程
    • 任务拒绝策略:
      • 队列满或关闭时的拒绝处理
    • 线程状态管理:
      • ST_NOT_STARTED, ST_STARTED, ST_SHUTTING_DOWN等状态

7. SingleThreadEventLoop抽象类

  • 实现: EventLoop
  • 增加能力:
    • Channel管理:
      • 维护注册到此EventLoop的所有Channel
      • 实现register(Channel)方法
    • 任务队列优化:
      • 引入tailTasks队列用于收尾工作
      • 支持任务优先级区分
    • 唤醒机制:
      • wakeup(boolean)方法唤醒事件循环
      • 解决阻塞在IO操作时的任务调度问题

8. NioEventLoop具体类

  • 继承自: SingleThreadEventLoop
  • 增加能力:
    • NIO操作支持:
      • 维护Java NIO Selector实例
      • 处理SelectionKey的事件分发
    • IO比例控制:
      • ioRatio控制IO任务与非IO任务的执行比例
    • 选择策略:
      • 通过SelectStrategy控制select操作行为

        EventExecutor是一个通用化的执行器,其抽象实现类SingleThreadEventExecutor将执行器与一个线程进行绑定,这意味着一个线程对应一个执行器。将SingleThreadEventExecutor继续进行特化,成为SingleThreadEventLoop抽象类,在该类中增加了管理网络相关组建的能力。其中具体实现类例如NioEventLoop,管理了Selecor实例,这意味着将线程与Selecor进行了绑定,即一个线程一个Selecor。

总结:从NioEventLoopGroup与NioEventLoop不难看出,其实NioEventLoopGroup在Reactor模型中所扮演的角色就是mainReactor与subReactor,而NioEventLoop扮演的角色是mainReactor或subReactor中的每一个执行单元。该执行单元将单个线程与一个Selector进行绑定,来执行Selector中绑定的若干个Channel感兴趣的事件。

二. 示例代码

public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup(2);

        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();
                        p.addLast(new LoggingHandler(LogLevel.INFO));
                    }
                });
    }
}

三. 源码分析

EventLoopGroup bossGroup = new NioEventLoopGroup(1);

 首先看到这一行,点击进入NioEventLoopGroup的构造函数。 

    public NioEventLoopGroup(int nThreads) {
        this(nThreads, (Executor) null);
    }

可以看到,传入了一个线程数量的参数,该参数可以为 NioEventLoopGroup定制NioEventLoop的个数。继续往下深入构造函数

    public NioEventLoopGroup(int nThreads, Executor executor) {
        // todo:SelectorProvider按系统生成Selector
        this(nThreads, executor, SelectorProvider.provider());
    }

 SelectorProvider.provider(),此处使用了JDK包中的方法,最终生成一个了一个SelectorProvider对象继续传入(这个对象可以理解为一个Selector的构造工厂)

继续深入来到MultithreadEventLoopGroup构造函数

static {
    // todo:配置值或者系统可用核心数两倍
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
                "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    if (logger.isDebugEnabled()) {
            logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}   




protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    // todo:此处生成线程数量
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

在这里,提供了线程的默认数量(即初始构造NioEventLoopGroup时未传入线程个数)

继续深入来到MultithreadEventExecutorGroup的构造方法

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    checkPositive(nThreads, "nThreads");

    if (executor == null) {
        // todo:此时线程并未启动,仅仅进行线程工厂初始化
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    // todo:EventLoopGroup下EventLoop
    children = new EventExecutor[nThreads];

    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            // todo:初始化EventLoop
            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;
                    }
                }
            }
        }
    }

    // todo:选择器,选择下一个EventLoop
    // todo:针对数量是否为2的倍数进行优化,2的次方则位运算,否则模运算
    chooser = chooserFactory.newChooser(children);

    // todo:监听每个EventLoop的终止事件,当所有EventLoop都终止时,设置terminationFuture的状态为成功。
    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(该参数在之后传给EventLoop进行线程创建)

2. 根据线程数量nThreads创建EventLoop

3. 为EventLoopGroup绑定一个选择器chooser(该选择器之后用来选择EventLoopGroup中下一个可用的EventLoop)

4. 为EventLoopGroup中的每一个EventLoop绑定一个回调函数,该回调函数监听每个EventLoop的终止事件,当所有EventLoop都终止时,设置Group中的参数terminationFuture的状态为成功。

接下来深入细节:

1. 创建executor

咱们将EventLoopGroup中创建出来的executor记为总executor

executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());

点击进入ThreadPerTaskExecutor构造函数,结果发现ThreadPerTaskExecutor结构非常简单

public final class ThreadPerTaskExecutor implements Executor {
    private final ThreadFactory threadFactory;

    public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
        this.threadFactory = ObjectUtil.checkNotNull(threadFactory, "threadFactory");
    }

    @Override
    public void execute(Runnable command) {
        // todo: 总executor执行execute时,直接创建线程并启动
        threadFactory.newThread(command).start();
    }
}

构造函数传入参数是一个线程工厂

着重需要注意的是 execute方法,当总executor调用execute方法时,此时会创建出一个新的线程并且立即启动该线程!!!

newDefaultThreadFactory()这个方法感兴趣的同学可以追下去看看。

它主要的方法是newThread方法,该方法实现了一些给线程命名等功能,最终创建出一个FastThreadLocalThread线程,FastThreadLocalThread继承于系统Thread

public Thread newThread(Runnable r) {
    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;
}

2. 根据线程数量nThreads创建EventLoop

接下来看看这行代码

children[i] = newChild(executor, args);

点击进入newChild方法,跳转进入NioEventLoopGroup中的newChild方法

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}

继续点击进入NioEventLoop的构造方法

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory queueFactory) {
    // todo:此构造方法构造EventLoop executor, queue
    super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
            rejectedExecutionHandler);
    this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
    this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");

    // todo:开启selector
    final SelectorTuple selectorTuple = openSelector();
    this.selector = selectorTuple.selector;
    this.unwrappedSelector = selectorTuple.unwrappedSelector;
}

在该构造方法中,完成了两件非常重要的事情:

1. 通过EventLoopGroup中创建的总executor创建出了属于每个EventLoop的executor,并且创造出属于每个EventLoop的任务队列taskQueue与tailTaskQueue

2. 开启属于每个EventLoop的Selector

不断点击super进入SingleThreadEventExecutor中的构造函数

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                    boolean addTaskWakesUp, Queue<Runnable> taskQueue,
                                    RejectedExecutionHandler rejectedHandler) {
    super(parent);
    this.addTaskWakesUp = addTaskWakesUp;
    this.maxPendingTasks = DEFAULT_MAX_PENDING_EXECUTOR_TASKS;

    // todo:此处注意,将Group创建的executor进行包装,将executor和EventLoop进行绑定
    this.executor = ThreadExecutorMap.apply(executor, this);
    this.taskQueue = ObjectUtil.checkNotNull(taskQueue, "taskQueue");
    this.rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

该构造函数中我要着重讲解的地方在于executor的创建,可以发现此时将EventLoopGroup创建的总exector与EventLoop自身引用传入ThreadExecutorMap.apply,点击看看ThreadExecutorMap.apply方法的原貌

此时大概能捋一捋EventLoop中的executor执行execute方法的原貌了

当EventLoop中的executor执行execute方法时候,首先会执行EventLoopGroup中总executor的execute方法,该方法会创建出一个新的线程,并且启动这个新的线程。

public static Executor apply(final Executor executor, final EventExecutor eventExecutor) {
    ObjectUtil.checkNotNull(executor, "executor");
    ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
    return new Executor() {
        @Override
        public void execute(final Runnable command) {
            executor.execute(apply(command, eventExecutor));
        }
    };
}

传入的可执行Runnable会将EventLoop的引用保存在一个ThreadLocal中,并且在comannd执行结束后将EventLoop的引用从ThreadLocal中删除

public static Runnable apply(final Runnable command, final EventExecutor eventExecutor) {
    ObjectUtil.checkNotNull(command, "command");
    ObjectUtil.checkNotNull(eventExecutor, "eventExecutor");
    // todo:装饰器模式应用,线程运行如下代码,在该代码中,将线程关联的EventLoop放入ThreadLocal中
    // todo:此时,线程中随时可以通过ThreadExecutorMap.currentExecutor获取线程关联EventLoop
    return new Runnable() {
        @Override
        public void run() {
            setCurrentEventExecutor(eventExecutor);
            try {
                command.run();
            } finally {
                setCurrentEventExecutor(null);
            }
        }
    };
}

public final class ThreadExecutorMap {

    private static final FastThreadLocal<EventExecutor> mappings = new FastThreadLocal<EventExecutor>();

    private ThreadExecutorMap() { }

    /**
     * Returns the current {@link EventExecutor} that uses the {@link Thread}, or {@code null} if none / unknown.
     */
    public static EventExecutor currentExecutor() {
        return mappings.get();
    }

    /**
     * Set the current {@link EventExecutor} that is used by the {@link Thread}.
     */
    private static void setCurrentEventExecutor(EventExecutor executor) {
        mappings.set(executor);
    }
|

通过上述方法,线程与EventLoop进行了绑定,在线程中可以通过ThreadLocal随时获取到线程对应 EventLoop的引用。

继续回到NioEventLoop的构造方法

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler,
             EventLoopTaskQueueFactory queueFactory) {
    // todo:此构造方法构造EventLoop executor, queue
    super(parent, executor, false, newTaskQueue(queueFactory), newTaskQueue(queueFactory),
            rejectedExecutionHandler);
    this.provider = ObjectUtil.checkNotNull(selectorProvider, "selectorProvider");
    this.selectStrategy = ObjectUtil.checkNotNull(strategy, "selectStrategy");

    // todo:开启selector
    final SelectorTuple selectorTuple = openSelector();
    this.selector = selectorTuple.selector;
    this.unwrappedSelector = selectorTuple.unwrappedSelector;
}

关注一下 openSelector方法

private SelectorTuple openSelector() {
    final Selector unwrappedSelector;
    try {
        // todo:原始selector
        unwrappedSelector = provider.openSelector();
    } catch (IOException e) {
        throw new ChannelException("failed to open a new selector", e);
    }

    // todo:未优化,直接返回
    if (DISABLE_KEY_SET_OPTIMIZATION) {
        return new SelectorTuple(unwrappedSelector);
    }

    // todo:反射获取Selector实现类
    Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                return Class.forName(
                        "sun.nio.ch.SelectorImpl",
                        false,
                        PlatformDependent.getSystemClassLoader());
            } catch (Throwable cause) {
                return cause;
            }
        }
    });

    // todo:检查Selector优化实现类是否有效,需要为原始类的子类或者同类
    if (!(maybeSelectorImplClass instanceof Class) ||
        // ensure the current selector implementation is what we can instrument.
        !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
        if (maybeSelectorImplClass instanceof Throwable) {
            Throwable t = (Throwable) maybeSelectorImplClass;
            logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
        }
        return new SelectorTuple(unwrappedSelector);
    }

    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

    // todo:优化过后的SelectorKey容器,替换Set<SelectionKey>类
    // todo:为什么替换?原来为Set类,现在虽继承Set,但实际为List,遍历更加方便
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) {
                    // Let us try to use sun.misc.Unsafe to replace the SelectionKeySet.
                    // This allows us to also do this in Java9+ without any extra flags.
                    long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
                    long publicSelectedKeysFieldOffset =
                            PlatformDependent.objectFieldOffset(publicSelectedKeysField);

                    if (selectedKeysFieldOffset != -1 && publicSelectedKeysFieldOffset != -1) {
                        PlatformDependent.putObject(
                                unwrappedSelector, selectedKeysFieldOffset, selectedKeySet);
                        PlatformDependent.putObject(
                                unwrappedSelector, publicSelectedKeysFieldOffset, selectedKeySet);
                        return null;
                    }
                    // We could not retrieve the offset, lets try reflection as last-resort.
                }

                Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
                if (cause != null) {
                    return cause;
                }
                cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
                if (cause != null) {
                    return cause;
                }

                selectedKeysField.set(unwrappedSelector, selectedKeySet);
                publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            }
        }
    });

    if (maybeException instanceof Exception) {
        selectedKeys = null;
        Exception e = (Exception) maybeException;
        logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, e);
        return new SelectorTuple(unwrappedSelector);
    }
    selectedKeys = selectedKeySet;
    logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

该方法首先通过provider.openSelector()创建出一个selector。

然后进入了最关键的部分,Netty对于原始selector进行的优化

这段代码通过反射获取到了Selector中的具体实现类(系统不同,实现类会有所不同)

// todo:反射获取Selector实现类
Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
    @Override
    public Object run() {
        try {
            return Class.forName(
                    "sun.nio.ch.SelectorImpl",
                    false,
                    PlatformDependent.getSystemClassLoader());
        } catch (Throwable cause) {
            return cause;
        }
    }
});

// todo:检查Selector优化实现类是否有效,需要为原始类的子类或者同类
if (!(maybeSelectorImplClass instanceof Class) ||
    // ensure the current selector implementation is what we can instrument.
    !((Class<?>) maybeSelectorImplClass).isAssignableFrom(unwrappedSelector.getClass())) {
    if (maybeSelectorImplClass instanceof Throwable) {
        Throwable t = (Throwable) maybeSelectorImplClass;
        logger.trace("failed to instrument a special java.util.Set into: {}", unwrappedSelector, t);
    }
    return new SelectorTuple(unwrappedSelector);
}

final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;

继续往下看,Netty中自己实现了一个类SelectedSelectionKeySet,该类继承自AbstractSet<SelectionKey>,但实际的实现是一个类似于数组的实现。通过反射将SelectedSelectionKeySet替换Selector中selectedKeys字段(该字段类型为Set<SelectionKey>)。

为什么要进行这种替换呢?

因为后续需要对Selector中的selectedKeys进行大量遍历,而原始的selectedKeys实现为HashMap,这将会导致遍历效率不高,并且额外还需要创建一个迭代器。

// todo:优化过后的SelectorKey容器,替换Set<SelectionKey>类
// todo:为什么替换?原来为Set类,现在虽继承Set,但实际为List,遍历更加方便
final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
    @Override
    public Object run() {
        try {
            Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
            Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

            if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) {
                // Let us try to use sun.misc.Unsafe to replace the SelectionKeySet.
                // This allows us to also do this in Java9+ without any extra flags.
                long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
                long publicSelectedKeysFieldOffset =
                        PlatformDependent.objectFieldOffset(publicSelectedKeysField);

                if (selectedKeysFieldOffset != -1 && publicSelectedKeysFieldOffset != -1) {
                    PlatformDependent.putObject(
                            unwrappedSelector, selectedKeysFieldOffset, selectedKeySet);
                    PlatformDependent.putObject(
                            unwrappedSelector, publicSelectedKeysFieldOffset, selectedKeySet);
                    return null;
                }
                // We could not retrieve the offset, lets try reflection as last-resort.
            }

            Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
            if (cause != null) {
                return cause;
            }
            cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
            if (cause != null) {
                return cause;
            }

            selectedKeysField.set(unwrappedSelector, selectedKeySet);
            publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
            return null;
        } catch (NoSuchFieldException e) {
            return e;
        } catch (IllegalAccessException e) {
            return e;
        }
    }
});

3. 为EventLoopGroup绑定一个选择器chooser

下面来看看选择器代码

chooser = chooserFactory.newChooser(children);

@Override
public EventExecutorChooser newChooser(EventExecutor[] executors) {
    if (isPowerOfTwo(executors.length)) {
        return new PowerOfTwoEventExecutorChooser(executors);
    } else {
        return new GenericEventExecutorChooser(executors);
    }
}

private static final class PowerOfTwoEventExecutorChooser implements EventExecutorChooser {
    private final AtomicInteger idx = new AtomicInteger();
    private final EventExecutor[] executors;

    PowerOfTwoEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

    @Override
    public EventExecutor next() {
        return executors[idx.getAndIncrement() & executors.length - 1];
    }
}

private static final class GenericEventExecutorChooser implements EventExecutorChooser {
    // Use a 'long' counter to avoid non-round-robin behaviour at the 32-bit overflow boundary.
    // The 64-bit long solves this by placing the overflow so far into the future, that no system
    // will encounter this in practice.
    private final AtomicLong idx = new AtomicLong();
    private final EventExecutor[] executors;

    GenericEventExecutorChooser(EventExecutor[] executors) {
        this.executors = executors;
    }

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

创建选择器时Netty进行了一个小优化,对于2的次方数量的EventLoop,进行位运算选择。其他使用模运算。 

通过以上几大步骤,Netty中EventLoopGroup的创建大致完成

NioEventLoopGroup 构造方法 几个入参: int nThreads 线程数量 Executor executor 线程池 SelectorProvider selectorProvider 这个应该比较熟悉了,可以创建selector ThreadFactory threadFactory 线程工厂 SelectStrategyFactory selectStrategyFactory select的策略 RejectedExecutionHandler rejectedExecutionHandler 拒绝策略 Ev.. 阅读详情

相关推荐

Netty EventLoopGroup 详解:Nio、Epoll、Poll 、KQueue和IoUring

Netty个高性能的网络通信框架,它使用 EventLoopGroup 来处理 I/O 事件。不同的 EventLoopGroup 实现针对不同的操作系统和应用场景优化性能。和,并提供了详细的代码示例和注释。

微笑听雨 3028

NioEventLoopGroup初始化

本文是我对NettyNioEventLoopGroup及NioEventLoop初始化工作的源码阅读笔记, 如下图,是Netty的Reactor线程模型图,本文描述NioEventLoopGroup等价于我在图中标红的MainReactor组件,全篇围绕它的初始化展开,难免地方理解的不正确,欢迎留言 在Nio网络编程模型的图示是下面那张图, 单条Thread全职执行个Selecto...

weixin_30606669的博客 478

Vivado DCP文件实战:手把手教你封装IP核并避开那些‘坑’

本文详细介绍了Vivado DCP文件在FPGA开发中的IP核封装实战技巧,包括DCP文件的优势、创建流程及常见陷阱规避。通过具体案例和Tcl脚本示例,帮助开发者高效实现知识产权保护与模块化协作,提升工程效率与跨器件兼容性。

weixin_30721077的博客 665

NettyNioEventLoopGroup介绍

Netty中的核心类之的:NioEventLoopGroup

小飞侠的博客 2407

吃透Netty源码系列NioEventLoopGroup

Netty源码深度解析系列前言Netty的Reactor模式 前言 netty更新的是快,最新发布版本已经是4.1.45.Final了,以前有学过netty,觉得学的还不够深入,这次打算从源码级别去更加深入的理解内部机制。我不想介绍太多关于netty是什么,怎么用,我更想介绍下原理,这样才能更好的去使用它,扩展它,完善它。我打算从常用的些类开始介绍,比如NioEventLoopGroup S...

王伟王胖胖的博客 5646

Netty源码分析:NioEventLoopGroup

Netty学习笔记:NioEventLoopGroup在工作之余,看到自己公司的超哥(俞超)关于Netty系列博文,讲解的很好,因此,自己在学习之余也跟了下源代码,来了解Netty,也做了相关的笔记,将形成系列博文,这是第篇。超哥的博文地址在这里:http://www.jianshu.com/p/c5068caab217Netty版本:4.0.23.Final借用超哥的例子,般服务端的代码如下

wojiushimogui的博客 6779

nettyNioEventLoopGroup

NettyNioEventLoopGroup及NioEventLoop初始化工作的源码阅读笔记, 如下图,是Netty的Reactor线程模型图,本文描述NioEventLoopGroup等价于我在图中标红的MainReactor组件,全篇围绕它的初始化展开,难免地方理解的不正确 在Nio网络编程模型的图示是下面那张图, 单条Thread全职执行个Selector,首先是服务端在启动的时候,会把代表服务端的ServerSockerChannel注册进Selector,且感兴趣的事件是Accept,

追逐消失的记忆 1万+

Netty源码()NioEventLoopGroup初始化的过程

今天我们来讲讲Netty的启动流程,要理解Netty的启动流程,我们要理解NIO模型到Reactor模型,因为Netty的实现就是基于Reactor模型的。 个主selector线程用来监听所有客户端的连接,然后为对应的连接上来的客户端开辟个selector线程来监听客户端的上的事件,进行相应的处理。这就是NIO的Reactor模型。但是在Netty中怎么实现的呢?就让我们走进Netty源码世界,探究竟。由于Netty的启动流程过于复杂。我们这篇博客只讲Netty启动流程中小部分NioEvent

了不起的盖茨比的博客 1212

Netty学习()——NioEventLoopGroup初始化

1、什么是netty ? 维基百科中的解释如下 2、怎么使用netty来进行通信? 接下来看段简单的netty客户端的代码 EventLoopGroup eventLoopGroup = new NioEventLoopGroup(); try{ Bootstrap bootstrap = new Bootstrap(); ...

xuhangsong的博客 1543

Netty NioEventLoopGroup 详解及详细源码展示

NettyNioEventLoopGroup是实现高性能网络编程的核心组件,它通过线程池化的事件循环机制处理海量并发连接。该组件采用自动扩容设计,默认创建2倍CPU核心数的事件循环,并使用Round-Robin策略分配任务。源码分析显示其核心类结构包括MultithreadEventExecutorGroup和NioEventLoop等关键类,实现了通道注册、任务调度及优雅关闭等功能。优化技术包括线程数自动调优、Selector优化等,确保了高吞吐量和低延迟。

csdn_tom_168的博客 1481

netty源码--NioEventLoopGroup初始化

关于netty源码知识分享

qq_41868309的博客 455

吃透Netty源码系列五十五之NioEventLoopGroup创建细节

吃透Netty源码系列五十五之NioEventLoopGroup创建细节行代码的秘密NioEventLoopGroup的terminationFuture终止回调NioEventLoop的初始化io.netty.noKeySetOptimizationio.netty.selectorAutoRebuildThresholdNioEventLoop构造方法的newTaskQueuePlatfo...

王伟王胖胖的博客 893

Netty源码分析-NioEventLoopGroup初始化

public class GateServer{ private static int port = 9090; public static void main(String[] args) { //配置服务端的NIO线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(); Ev...

G-Lawliet的博客 436

深入理解 NioEventLoopGroup初始化

NioEventLoopGroup维护的是事件循环,EventLoop, 在Netty的主从Reactor线程模型中,两个事件循环组其实也是线程组,因为每个EventLoop在他的整个生命周期中都始终和条线程唯绑定,EventLoop的线程使用的是它自己封装的。我们知道,Netty中的线程可不止个, 多个EventLoop意味着多个线程, 任务队列的作用就是当其他线程拿到CPU的执行权时,却得到了其他线程的IO请求,这时当前线程就把这个请求以任务的方式提交到对应线程的任务队列里面。

mf97532的博客 662

Netty--初始化NioEventLoopGroup

写在这篇博客前!! 为何要写关于Netty源码博客呢? 因为自己最近初学Netty,熟系了Netty的基本操作以及Netty从BIO 到 NIO的系列进化过程。梳理了Netty所采用的Reactor模型 但是对Netty内部系列关于Reactor模型的实现有太多的细节需要梳理。所以决心想写些关于Netty的博客。为了自己能够更加深入Netty以及更加熟悉NettyNetty初始化Nio...

Tjimmy的博客 476

【硬核】肝了月的Netty知识点

说实话我不信有人能看完

敖丙 10万+

Netty4.0源码解析:NioEventLoop/NioEventLoopGroup初始化

、引言 Netty程序在启动时,需要指定最少个EventLoopGroup实例(服务端引导可以指定2个,客户端引导只能指定1个)。般情况下我们指定的EventLoopGroup实现类都是NioEventLoopGroup。在了解NioEventLoopGroup的作用及其内部实现原理之前,我们先来复习下Reactor线程模型: Reactor线程模型有三种类型:单线程模型、多线程模型和主从线...

APlus 1310

Netty源码剖析(NioEventLoopGroup创建流程)

原创不易,转载请注明出处 本文基于netty版本4.1.33.Final 文章目录前言1.Reactor网络模型简单介绍2.总结 前言 我们在《基于Netty实现个服务端/客户端通信demo》文中基于netty实现了个nio服务端、客户端通信的demo,也算是初步玩了玩netty,体验了下基于netty的nio网络编程,说实话我首次接触netty编程的时候很懵逼,而且在很长段时间内都处于懵逼状态,根本记不住这些api,但是当我对java nio 编程很熟练,并且了解了netty核心运行原理的.

猿上生活 603
上一篇: 【第一天】Netty实现IM即时通信系统——IM系统简介
supermario19
博客等级 码龄9年 33粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值