Netty Review - NioEventLoopGroup源码解析

文章目录

  • 概述
  • 类继承关系
  • 源码分析
  • 小结

在这里插入图片描述

在这里插入图片描述


概述

在这里插入图片描述

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

这段代码是在使用Netty框架时常见的用法,用于创建两个不同的EventLoopGroup实例,一个用于处理连接请求(通常称为bossGroup),另一个用于处理连接后的数据流(通常称为workerGroup)。

NioEventLoopGroup是Netty中用于处理NIO事件的EventLoopGroup的实现之一。在Netty中,事件循环(EventLoop)是Netty的核心,负责处理各种事件,比如接收连接、读取数据、写入数据等。

其主要功能和构造函数参数的含义:

  1. 构造函数参数说明:

    • 第一个构造函数参数表示创建的EventLoop的数量。对于bossGroup,它通常设置为1,因为它主要用于处理连接请求,而不是处理连接后的数据流。对于workerGroup,通常不设置数量,因为它会根据系统的CPU核心数量自动创建相应数量的EventLoop。
  2. 在构造函数中,NioEventLoopGroup会创建一组NioEventLoop实例。NioEventLoop是Netty中基于NIO的事件循环实现,它负责处理事件的派发和执行。

  3. NioEventLoopGroup的实现中,通常会创建一个或多个线程来执行事件循环中的任务。这些线程会不断地轮询注册在其上的Channel,处理各种事件。

  4. 对于bossGroup和workerGroup,它们分别负责处理不同类型的事件,bossGroup主要处理连接请求,而workerGroup主要处理连接后的数据流。

总的来说,NioEventLoopGroup的源码实现涉及到底层的NIO编程和多线程处理,它提供了一个高效的事件处理机制,使得Netty能够处理大量并发连接和数据流。


类继承关系

在这里插入图片描述


源码分析

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

NioEventLoopGroup类的构造函数,用于创建一个新的NioEventLoopGroup实例。

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

这个构造函数接受一个参数nThreads,表示要创建的EventLoop的数量。然后调用了另一个构造函数this(nThreads, (Executor) null)

在这个构造函数中,nThreads表示要创建的EventLoop的数量,通常情况下会根据CPU核心数来确定。参数(Executor) null表示没有指定额外的Executor,这意味着EventLoopGroup会使用默认的线程工厂来创建线程。

总的来说,这个构造函数的作用是根据指定的线程数量创建一个新的NioEventLoopGroup实例,并使用默认的线程工厂来创建线程。


继续

  public NioEventLoopGroup(int nThreads, Executor executor) {
        this(nThreads, executor, SelectorProvider.provider());
    }

这段代码是NioEventLoopGroup类的构造函数的另一个重载版本,接受三个参数:nThreadsexecutorSelectorProvider.provider()

这个构造函数接受三个参数:

  1. nThreads表示要创建的EventLoop的数量。
  2. executor表示一个可选的外部Executor,用于执行EventLoop中的任务。如果传入null,则会使用默认的线程工厂来创建线程。
  3. SelectorProvider.provider()返回一个默认的SelectorProvider,用于创建Selector实例。

然后,这个构造函数调用了另一个构造函数this(nThreads, executor, SelectorProvider.provider()),将这三个参数传递给它。

这个构造函数的作用是根据指定的线程数量创建一个新的NioEventLoopGroup实例,并允许传入一个可选的Executor,以及一个SelectorProvider,用于创建Selector实例。


继续

public NioEventLoopGroup(
        int nThreads, Executor executor, final SelectorProvider selectorProvider) {
    this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE);
}

这段代码是NioEventLoopGroup类的构造函数的另一个重载版本,接受四个参数:nThreadsexecutorselectorProviderDefaultSelectStrategyFactory.INSTANCE

这个构造函数接受四个参数:

  1. nThreads表示要创建的EventLoop的数量。
  2. executor表示一个可选的外部Executor,用于执行EventLoop中的任务。如果传入null,则会使用默认的线程工厂来创建线程。
  3. selectorProvider表示一个SelectorProvider,用于创建Selector实例。
  4. DefaultSelectStrategyFactory.INSTANCE是一个默认的Select策略工厂实例,用于创建Select策略。

然后,这个构造函数调用了另一个构造函数this(nThreads, executor, selectorProvider, DefaultSelectStrategyFactory.INSTANCE),将这四个参数传递给它。

这个构造函数的作用是根据指定的线程数量创建一个新的NioEventLoopGroup实例,并允许传入一个可选的Executor、SelectorProvider,以及一个默认的Select策略工厂实例。


继续

public NioEventLoopGroup(int nThreads, Executor executor, final SelectorProvider selectorProvider,
                         final SelectStrategyFactory selectStrategyFactory) {
    super(nThreads, executor, selectorProvider, selectStrategyFactory, RejectedExecutionHandlers.reject());
}

这段代码是NioEventLoopGroup类的构造函数的最终版本,接受五个参数:nThreadsexecutorselectorProviderselectStrategyFactoryRejectedExecutionHandlers.reject()

这个构造函数调用了父类构造函数super(),传递了五个参数:

  1. nThreads表示要创建的EventLoop的数量。
  2. executor表示一个可选的外部Executor,用于执行EventLoop中的任务。如果传入null,则会使用默认的线程工厂来创建线程。
  3. selectorProvider表示一个SelectorProvider,用于创建Selector实例。
  4. selectStrategyFactory表示一个Select策略工厂,用于创建Select策略。
  5. RejectedExecutionHandlers.reject()是一个拒绝执行处理器,用于处理任务提交被拒绝的情况。

这个构造函数的作用是根据指定的参数创建一个新的NioEventLoopGroup实例,并设置Executor、SelectorProvider、Select策略工厂和拒绝执行处理器。


继续

protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

这段代码是MultithreadEventLoopGroup类的构造函数,它是MultithreadEventExecutorGroup类的子类。让我们详细解释这段代码的实现:

这个构造函数通过调用父类构造函数super()来创建一个新的MultithreadEventLoopGroup实例,它接受三个参数:

  1. nThreads表示要创建的EventLoop的数量,如果传入的值为0,则使用默认值DEFAULT_EVENT_LOOP_THREADS
  2. executor表示一个可选的外部Executor,用于执行EventLoop中的任务。如果传入null,则会使用默认的线程工厂来创建线程。
  3. args表示可变参数,用于传递额外的参数给父类构造函数。

在这个构造函数中,首先通过三元运算符判断nThreads是否为0,如果是,则使用默认值DEFAULT_EVENT_LOOP_THREADS,否则使用传入的值。然后调用父类构造函数,传递这三个参数。


DEFAULT_EVENT_LOOP_THREADS

private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
    // 获取系统属性"io.netty.eventLoopThreads",如果未设置,则使用默认值为可用处理器数的两倍
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

    // 如果日志级别为DEBUG,则输出默认的EventLoop线程数
    if (logger.isDebugEnabled()) {
        logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}

这段代码是一个静态代码块,用于初始化DEFAULT_EVENT_LOOP_THREADS这个静态常量。

这个静态代码块主要做了两件事情:

  1. 通过SystemPropertyUtil.getInt()方法获取系统属性"io.netty.eventLoopThreads"的值,如果未设置,则使用默认值为可用处理器数的两倍。
  2. 如果日志级别为DEBUG,则通过日志记录框架输出默认的EventLoop线程数。

这个静态代码块的作用是在类加载时初始化DEFAULT_EVENT_LOOP_THREADS这个静态常量,并根据系统属性设置默认的EventLoop线程数。

总的来说,这个构造函数的作用是根据指定的参数创建一个新的MultithreadEventLoopGroup实例,并设置EventLoop的数量和Executor。


继续

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

这段代码是MultithreadEventExecutorGroup类的受保护构造函数,用于创建一个新的MultithreadEventExecutorGroup实例。

这个构造函数接受三个参数:

  1. nThreads表示要创建的线程数。
  2. executor表示一个可选的外部Executor,用于执行任务。如果传入null,则会使用默认的线程工厂来创建线程。
  3. args表示可变参数,用于传递额外的参数给子类的newChild()方法。

在这个构造函数中,调用了另一个构造函数this(nThreads, executor, DefaultEventExecutorChooserFactory.INSTANCE, args),将这三个参数传递给它,并传递了一个默认的EventExecutor选择器工厂实例DefaultEventExecutorChooserFactory.INSTANCE

这个构造函数的作用是根据指定的参数创建一个新的MultithreadEventExecutorGroup实例,并使用默认的EventExecutor选择器工厂。


继续

这段代码是MultithreadEventExecutorGroup类的受保护构造函数的具体实现,用于创建一个新的MultithreadEventExecutorGroup实例。

请看注释

protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    if (nThreads <= 0) {
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }

    // 如果传入的Executor为null,则使用默认的ThreadPerTaskExecutor,并创建一个新的线程工厂
    if (executor == null) {
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }

    // 初始化children数组,用于存储创建的EventExecutor实例
    children = new EventExecutor[nThreads];

    // 循环创建nThreads个EventExecutor实例
    for (int i = 0; i < nThreads; i ++) {
        boolean success = false;
        try {
            // 调用newChild方法创建EventExecutor实例,并将其存储到children数组中
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
            // 在创建EventExecutor实例时发生异常,抛出IllegalStateException异常
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
            // 如果创建EventExecutor实例失败,则关闭已创建的实例
            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;
                    }
                }
            }
        }
    }

    // 根据chooserFactory创建一个EventExecutor选择器
    chooser = chooserFactory.newChooser(children);

    // 创建一个监听器,用于监听所有EventExecutor实例的终止事件
    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
            if (terminatedChildren.incrementAndGet() == children.length) {
                terminationFuture.setSuccess(null);
            }
        }
    };

    // 添加终止事件监听器到每个EventExecutor实例的terminationFuture上
    for (EventExecutor e: children) {
        e.terminationFuture().addListener(terminationListener);
    }

    // 将children数组转换为不可修改的集合
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

这个构造函数的作用是根据指定的参数创建一个新的MultithreadEventExecutorGroup实例,并初始化一组EventExecutor实例。

如果创建EventExecutor实例失败,则会关闭已创建的实例,并等待它们终止。

最后,创建一个监听器用于监听所有EventExecutor实例的终止事件,并将children数组转换为不可修改的集合。


重点关注 children[i] = newChild(executor, args);

在这里插入图片描述

 @Override
    protected EventLoop newChild(Executor executor, Object... args) throws Exception {
        return new NioEventLoop(this, executor, (SelectorProvider) args[0],
            ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
    }

这段代码是MultithreadEventLoopGroup类中的一个重要方法newChild()的实现,它用于创建新的EventLoop实例。

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    // 从参数数组中获取需要的参数,包括SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler
    SelectorProvider selectorProvider = (SelectorProvider) args[0];
    SelectStrategyFactory selectStrategyFactory = (SelectStrategyFactory) args[1];
    RejectedExecutionHandler rejectedExecutionHandler = (RejectedExecutionHandler) args[2];

    // 调用NioEventLoop的构造函数创建一个新的NioEventLoop实例,并返回
    return new NioEventLoop(this, executor, selectorProvider, selectStrategyFactory.newSelectStrategy(), rejectedExecutionHandler);
}

这个方法的作用是根据给定的参数创建一个新的EventLoop实例。它首先从参数数组中获取所需的参数,包括SelectorProvider、SelectStrategyFactory和RejectedExecutionHandler,然后调用NioEventLoop的构造函数创建一个新的NioEventLoop实例,并将这些参数传递给它。

总的来说,newChild()方法的作用是创建一个新的EventLoop实例,并返回该实例。在MultithreadEventLoopGroup中,每个EventLoop实例都负责处理一部分任务。


new NioEventLoop(this, executor, selectorProvider, selectStrategyFactory.newSelectStrategy(), rejectedExecutionHandler)

这段代码是NioEventLoop类的构造函数,用于创建一个新的NioEventLoop实例。

让我们逐步解释这段代码的实现:

NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
             SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
    // 调用父类SingleThreadEventExecutor的构造函数创建一个新的实例
    super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);

    // 检查selectorProvider和selectStrategy是否为null,如果是则抛出NullPointerException
    if (selectorProvider == null) {
        throw new NullPointerException("selectorProvider");
    }
    if (strategy == null) {
        throw new NullPointerException("selectStrategy");
    }

    // 初始化成员变量provider为传入的selectorProvider
    provider = selectorProvider;

    // 调用openSelector()方法打开一个新的Selector,并将返回的SelectorTuple中的selector和unwrappedSelector分别赋值给成员变量selector和unwrappedSelector
    final SelectorTuple selectorTuple = openSelector();
    selector = selectorTuple.selector;
    unwrappedSelector = selectorTuple.unwrappedSelector;

    // 初始化成员变量selectStrategy为传入的strategy
    selectStrategy = strategy;
}

这个构造函数的作用是初始化NioEventLoop实例的成员变量。

它首先调用父类SingleThreadEventExecutor的构造函数,然后检查传入的selectorProvider和selectStrategy是否为null,如果是则抛出NullPointerException。

接着,将传入的selectorProvider赋值给成员变量provider,并调用openSelector()方法打开一个新的Selector,并将返回的SelectorTuple中的selector和unwrappedSelector分别赋值给成员变量selector和unwrappedSelector。

最后,将传入的strategy赋值给成员变量selectStrategy。

总的来说,这个构造函数的作用是初始化NioEventLoop实例的成员变量,包括selector、unwrappedSelector、provider和selectStrategy


重点关注: super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);final SelectorTuple selectorTuple = openSelector();

super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler)

protected SingleThreadEventLoop(EventLoopGroup parent, Executor executor,
                                boolean addTaskWakesUp, int maxPendingTasks,
                                RejectedExecutionHandler rejectedExecutionHandler) {
    // 调用父类SingleThreadEventExecutor的构造函数创建一个新的实例
    super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler);
    
    // 初始化tailTasks,使用newTaskQueue方法创建一个新的任务队列
    tailTasks = newTaskQueue(maxPendingTasks);
}

这段代码是SingleThreadEventLoop类的受保护构造函数的实现,用于创建一个新的SingleThreadEventLoop实例。

这个构造函数的作用是创建一个新的SingleThreadEventLoop实例,并初始化它的成员变量。首先调用父类SingleThreadEventExecutor的构造函数,然后使用newTaskQueue方法创建一个新的任务队列tailTasks,并将其赋值给成员变量tailTasks。

总的来说,这个构造函数的作用是初始化SingleThreadEventLoop实例的成员变量,并设置其父类、执行器、任务唤醒标志、最大挂起任务数和拒绝执行处理器

super(parent, executor, addTaskWakesUp, maxPendingTasks, rejectedExecutionHandler)

这段代码是SingleThreadEventExecutor类的受保护构造函数的实现,用于创建一个新的SingleThreadEventExecutor实例。让我们逐步解释这段代码的实现:

protected SingleThreadEventExecutor(EventExecutorGroup parent, Executor executor,
                                    boolean addTaskWakesUp, int maxPendingTasks,
                                    RejectedExecutionHandler rejectedHandler) {
    // 调用父类构造函数,设置父类EventExecutorGroup
    super(parent);

    // 设置是否唤醒线程的标志
    this.addTaskWakesUp = addTaskWakesUp;

    // 设置最大挂起任务数,确保最小为16
    this.maxPendingTasks = Math.max(16, maxPendingTasks);

    // 应用ThreadExecutorMap,确保executor为非空
    this.executor = ThreadExecutorMap.apply(executor, this);

    // 初始化任务队列
    taskQueue = newTaskQueue(this.maxPendingTasks);

    // 设置拒绝执行处理器
    rejectedExecutionHandler = ObjectUtil.checkNotNull(rejectedHandler, "rejectedHandler");
}

这个构造函数的作用是创建一个新的SingleThreadEventExecutor实例,并设置其成员变量。它首先调用父类的构造函数,设置父类EventExecutorGroup。然后,根据传入的参数设置是否唤醒线程的标志addTaskWakesUp、最大挂起任务数maxPendingTasks、执行器executor以及拒绝执行处理器rejectedExecutionHandler。

总的来说,这个构造函数的作用是初始化SingleThreadEventExecutor实例的成员变量,并设置其父类、执行器、唤醒线程标志、最大挂起任务数和拒绝执行处理器。

tailTasks = newTaskQueue(maxPendingTasks);

这段代码是SingleThreadEventExecutor类中的一个方法newTaskQueue()的实现。这个方法用于创建一个任务队列,用于存储待执行的任务。

让我们逐步解释这段代码的实现:

 
protected Queue<Runnable> newTaskQueue(int maxPendingTasks) {
    // 创建一个LinkedBlockingQueue实例作为任务队列,并设置最大容量为maxPendingTasks
    return new LinkedBlockingQueue<Runnable>(maxPendingTasks);
}

这个方法的作用是创建一个新的任务队列,用于存储待执行的任务。默认实现使用了LinkedBlockingQueue,它是一个基于链表的阻塞队列,具有无界的容量,但可以通过maxPendingTasks参数设置最大容量。如果子类重写这个方法,并且不会对队列执行阻塞调用,可以选择返回一个不支持阻塞操作的更高性能的实现。

总的来说,newTaskQueue()方法的作用是创建一个任务队列,并返回该队列的实例。


final SelectorTuple selectorTuple = openSelector()

这段代码是NioEventLoop类中的一个方法openSelector()的实现,用于打开一个新的Selector。让我们逐步解释这段代码的实现:

private SelectorTuple openSelector() {
    // 声明一个未包装的Selector
    final Selector unwrappedSelector;
    try {
        // 尝试打开一个新的Selector
        unwrappedSelector = provider.openSelector();
    } catch (IOException e) {
        // 打开Selector失败,抛出异常
        throw new ChannelException("failed to open a new selector", e);
    }

    // 如果禁用了选择键集合优化,则返回一个包含未包装的Selector的SelectorTuple
    if (DISABLE_KEY_SET_OPTIMIZATION) {
        return new SelectorTuple(unwrappedSelector);
    }

    // 尝试获取SelectorImpl类
    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;
            }
        }
    });

    // 如果没有获取到类或者当前的Selector实现不是我们要的实现,则返回一个包含未包装的Selector的SelectorTuple
    if (!(maybeSelectorImplClass instanceof Class) ||
        !((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);
    }

    // 获取到了SelectorImpl类,尝试使用Unsafe替换SelectionKeySet
    final Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
    final SelectedSelectionKeySet selectedKeySet = new SelectedSelectionKeySet();

    // 使用反射设置Selector的selectedKeys和publicSelectedKeys字段为选定的键集
    Object maybeException = AccessController.doPrivileged(new PrivilegedAction<Object>() {
        @Override
        public Object run() {
            try {
                // 获取selectedKeys和publicSelectedKeys字段
                Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
                Field publicSelectedKeysField = selectorImplClass.getDeclaredField("publicSelectedKeys");

                // 如果当前Java版本大于等于9并且具有Unsafe支持,则尝试使用Unsafe替换SelectionKeySet
                if (PlatformDependent.javaVersion() >= 9 && PlatformDependent.hasUnsafe()) {
                    // 使用Unsafe替换SelectionKeySet
                    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;
                    }
                    // 无法检索偏移量,尝试反射作为最后的手段。
                }

                // 尝试设置字段的访问权限
                Throwable cause = ReflectionUtil.trySetAccessible(selectedKeysField, true);
                if (cause != null) {
                    return cause;
                }
                cause = ReflectionUtil.trySetAccessible(publicSelectedKeysField, true);
                if (cause != null) {
                    return cause;
                }

                // 设置选定的键集到Selector的selectedKeys和publicSelectedKeys字段
                selectedKeysField.set(unwrappedSelector, selectedKeySet);
                publicSelectedKeysField.set(unwrappedSelector, selectedKeySet);
                return null;
            } catch (NoSuchFieldException e) {
                return e;
            } catch (IllegalAccessException e) {
                return e;
            }
        }
    });

    // 如果出现异常,则返回一个包含未包装的Selector的SelectorTuple
    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);
    }

    // 成功设置选定的键集,记录日志并返回一个包含未包装的Selector和SelectedSelectionKeySetSelector的SelectorTuple
    selectedKeys = selectedKeySet;
    logger.trace("instrumented a special java.util.Set into: {}", unwrappedSelector);
    return new SelectorTuple(unwrappedSelector,
                             new SelectedSelectionKeySetSelector(unwrappedSelector, selectedKeySet));
}

这个方法的作用是打开一个新的Selector,并尝试优化其内部的SelectionKey集合。

首先,它尝试直接打开一个新的Selector,如果失败,则抛出异常。

然后,它检查是否禁用了选择键集合优化。如果没有禁用,则尝试获取sun.nio.ch.SelectorImpl类,并验证当前Selector的实现是否与获取的类兼容。如果兼容,则尝试使用Unsafe替换SelectionKeySet以提高性能。

最后,返回一个包含未包装的Selector和可能的SelectedSelectionKeySetSelector的SelectorTuple。


小结

NioEventLoopGroup是Netty中用于处理NIO事件的事件循环组。

下面我们总结下NioEventLoopGroup源码:

  1. 构造函数:NioEventLoopGroup类提供了多个构造函数,用于创建实例。这些构造函数允许指定线程数量、执行器、选择器提供者和选择策略工厂等参数。构造函数内部会调用父类MultithreadEventLoopGroup的构造函数,并设置相关参数。

  2. 多线程事件循环组:NioEventLoopGroup继承自MultithreadEventLoopGroup类,用于管理多个NioEventLoop实例。它负责创建和管理一组NioEventLoop线程,每个线程处理一个Selector的事件循环。

  3. 打开新的Selector:NioEventLoopGroup内部使用openSelector()方法来打开一个新的Selector。该方法尝试优化Selector的内部SelectionKey集合,以提高性能。优化过程包括尝试使用Unsafe替换SelectionKeySet,以更有效地处理SelectionKey的集合。

  4. 任务队列:NioEventLoopGroup内部使用任务队列来存储待执行的任务。默认情况下,它使用LinkedBlockingQueue作为任务队列,但允许子类重写newTaskQueue()方法以选择更高性能的实现。

  5. 执行器和拒绝策略:NioEventLoopGroup使用执行器来执行任务,并提供拒绝执行处理器来处理任务执行失败的情况。

总的来说,NioEventLoopGroup源码实现了一个用于管理和处理NIO事件的多线程事件循环组,它提供了灵活的构造函数和优化的Selector打开机制,以提高整体性能和并发能力。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/383666.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算几何】确定两条连续线段向左转还是向右转

确定两条连续线段向左转还是向右转 目录 一、说明二、算法2.1 两点的叉积2.2 两个段的叉积 三、旋转方向判别3.1 左转3.2 右转3.3 共线判别 一、说明 如果是作图&#xff0c;或者是判别小车轨迹。为了直观地了解&#xff0c;从当前点到下一个点过程中&#xff0c;什么是左转、…

树莓派4B(Raspberry Pi 4B)使用docker搭建阿里巴巴sentinel服务

树莓派4B&#xff08;Raspberry Pi 4B&#xff09;使用docker搭建阿里巴巴sentinel服务 由于国内访问不了docker hub&#xff0c;而国内镜像仓库又没有适配树莓派ARM架构的sentinel镜像&#xff0c;所以我们只能退而求其次——自己动手构建镜像。本文基于Ubuntu&#xff0c;Jav…

springboot169基于vue的工厂车间管理系统的设计

基于VUE的工厂车间管理系统设计与实现 摘 要 社会发展日新月异&#xff0c;用计算机应用实现数据管理功能已经算是很完善的了&#xff0c;但是随着移动互联网的到来&#xff0c;处理信息不再受制于地理位置的限制&#xff0c;处理信息及时高效&#xff0c;备受人们的喜爱。本…

书生谱语-大语言模型测试demo

课程内容简介 1.作业 demo1 demo2 demo3 demo4

Makefile编译原理 make 中的路径搜索_1

一.make中的路径搜索 问题&#xff1a;在实际的工程项目中&#xff0c;所有的源文件和头文件都放在同一个文件夹中吗&#xff1f; 实验1 &#xff1a; VPATH 引子 mhrubuntu:~/work/makefile1/17$ ll total 28 drwxrwxr-x 4 mhr mhr 4096 Apr 22 00:46 ./ drwxrwxr-x 7 mhr m…

《UE5_C++多人TPS完整教程》学习笔记10 ——《P11 设置加入游戏会话(Setup for Joining Sessions)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P11 设置加入游戏会话&#xff08;Setup for Joining Sessions&#xff09;》 的学习笔记&#xff0c;该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版&#xff0c;UP主&…

Python远程控制工具的使用

本节我们对所编写的远程控制工具的功能进行测试。首先开启主控端程序&#xff0c; 如下所示&#xff1a; 接下来打开被控端程序。当被控端打开时&#xff0c;主控端会收到被控端的连接请 求。 开启被控端程序&#xff1a; 主控端接收到连接请求并显示被控端主机的信息&#xff…

MySQL-----DCL基础操作

▶ DCL简介 DCL英文全称是Data ControlLanguage(数据控制语言)&#xff0c;用来管理数据库用户、控制数据库的访问权限。 DCL--管理用户 ▶ 查询用户 use mysql; select * from user; ▶ 创建用户 ▶ 语法 create user 用户名主机名 identified by 密码 设置为在任意主机上访问…

Z-Stack一直卡在HAL_BOARD_INIT();

原因是Debugger没有配置好&#xff0c;因为默认是Simulator&#xff0c;不是TI的驱动&#xff0c;所以仿真出现一直卡在 HAL_BOARD_INIT(); 的情况&#xff0c;解决方法就是将Simulator改为Texas Instruments 改成下面的样子

MySQL篇----第二十篇

系列文章目录 文章目录 系列文章目录前言一、NULL 是什么意思二、主键、外键和索引的区别?三、你可以用什么来确保表格里的字段只接受特定范围里的值?四、说说对 SQL 语句优化有哪些方法?(选择几条)前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍…

Educational Codeforces Round 135 (Rated for Div. 2)C. Digital Logarithm(思维)

文章目录 题目链接题意题解代码 题目链接 C. Digital Logarithm 题意 给两个长度位 n n n的数组 a a a、 b b b&#xff0c;一个操作 f f f 定义操作 f f f为&#xff0c; a [ i ] f ( a [ i ] ) a [ i ] a[i]f(a[i])a[i] a[i]f(a[i])a[i]的位数 求最少多少次操作可以使 …

单片机学习笔记---串口向电脑发送数据电脑通过串口控制LED

目录 串口向电脑发送数据 每隔一秒串口就发送一个递增的数给电脑 电脑通过串口控制LED 波特率的具体计算 HEX模式和文本模式 前两节是本节的理论基础&#xff0c;这节开始代码演示&#xff01; 串口向电脑发送数据 接下来先开始演示一下串口单向发送一个数字给电脑&…

【Git】上传本地文件到Git(以Windows环境为例)

Git 的下载参考&#xff1a;Git 安装及配置 一、Git 上传的整体流程 1、工作区 > 本地仓库 将本地文件上传到Git&#xff0c;需要先上传到本地仓库&#xff0c;然后再上传到远程仓库。要上传文件到本地仓库&#xff0c;不是直接拷贝进去的&#xff0c;而是需要通过命令一步…

2024-02-11 Unity 编辑器开发之编辑器拓展2 —— 自定义窗口

文章目录 1 创建窗口类2 显示窗口3 窗口事件回调函数4 窗口中常用的生命周期函数5 编辑器窗口类中的常用成员6 小结 1 创建窗口类 ​ 当想为 Unity 拓展一个自定义窗口时&#xff0c;只需实现继承 EditorWindow 的类即可&#xff0c;并在该类的 OnGUI 函数中编写面板控件相关的…

QT入门-信号与槽

1.QT基本框架 #include "myWindow.h"#include <QApplication>int main(int argc, char *argv[]) {QApplication a(argc, argv);myWindow w;w.show();return a.exec(); } QApplicata&#xff1a;应用程序对象&#xff0c;必须有且只能有一个 Qwidget&#xff1…

过河卒(洛谷)

题目 原题 题目描述 棋盘上 A A A 点有一个过河卒&#xff0c;需要走到目标 B B B 点。卒行走的规则&#xff1a;可以向下、或者向右。同时在棋盘上 C C C 点有一个对方的马&#xff0c;该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。…

3秒实现无痛基于Stable Diffusion WebUI安装ComfyUI!无需重复安装环境!无需重复下载模型!安装教程

标题略有夸张的表达了接下来这一套确实很简单&#xff0c;相较于直接下载或者通过秋叶包更新而言。大大节省磁盘空间&#xff0c;和下载时间。 这篇教程不需要你有&#xff1a; 代码基础。都是复制粘贴就完事。魔法。 这篇教程默认你已经有&#xff1a; 1. 本地能够正常使用…

汽车出租管理系统

文章目录 汽车出租管理系统一、系统演示二、项目介绍三、系统部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 汽车出租管理系统 一、系统演示 汽车租赁系统 二、项目介绍 语言&#xff1a;java 框架&#xff1a;SpringBoot、…

【2024年数据】67个“绿色金融”主题DID政策汇总(已去重)

DID”发文趋势和主题分布 数据来源&#xff1a;中国知网、各期刊官网 时间跨度&#xff1a;2017-2024年 数据范围&#xff1a;中国各省 数据指标&#xff1a; 序号 用于构建DID的政策 文献标题 1 “宽带中国” 数字技术创新与中国企业高质量发展——来自企业数字专利的证据…

陶陶摘苹果C++

题目&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; int main(){//一、分析问题//已知&#xff1a;10 个苹果到地面的高度a[10],陶陶把手伸直的时候能够达到的最大高度height//未知&#xff1a;陶陶能够摘到的苹果的数目sum。//关系&#xff…