一、EventLoop 事件循环对象

io.netty.channel. EventLoop 定义了 Netty 的核心抽象,用于处理网络连接的生命周期中所发 生的事件。
io.netty.util.concurrent 包构建在 JDK 的 java.util.concurrent 包上。而 io.netty.channel 包中的类,为了与 Channel 的事件进行交互,扩展了这些接口/类。一个 **EventLoop 将由一个永远都不会改变的 Thread 驱动,同时任务(Runnable 或者 Callable)可以直接提交给EventLoop实现,以立即执行或者调度执行。
根据配置和可用核心的不同,可能会创建多个 EventLoop 实例用以优化资源的使用,并且
单个 EventLoop 可能会被指派用于服务多个 **Channel。 Netty 的 EventLoop 在继承了 ScheduledExecutorService 的同时,只定义了一个方法 parent()。
image.pngimage.png

EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件。 它的继承关系比较复杂

  • 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
  • 另一条线是继承自 netty 自己的 OrderedEventExecutor,
    • 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
    • 提供了 parent 方法来看看自己属于哪个 EventLoopGroup

在 Netty 4 中,所有的 I/O 操作和事件都由已经被分配给了 EventLoop 的那个 Thread 来处理。

  • 一个EventLoopGroup 包含一个或者多个EventLoop;
  • 一个EventLoop 在它的生命周期内只和一个Thread 绑定;
  • 所有由EventLoop 处理的I/O 事件都将在它专有的Thread 上被处理;
  • 一个Channel 在它的生命周期内只注册于一个EventLoop
  • 一个EventLoop 可能会被分配给一个或多个Channel。

线程管理

在内部,当提交任务,如果 当前调用线程 正是支撑 EventLoop 的线程,那么所提交的代码块将会被(直接)执行。否则,EventLoop 将调度该任务以便稍后执行,并将它放入到内部队列中。当 EventLoop 下次处理它的事件时,它会执行队列中的那些任务/事件。 :::tips 一个EventLoop 可以看成是一个只有一个线程的线程池(**SingleThreadExecutor**) ::: image.png

二、EventLoopGroup 事件循环组

image.png

EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)

  • 继承自 netty 自己的 EventExecutorGroup

    • 实现了 Iterable 接口提供遍历 EventLoop 的能力
    • 另有 next 方法获取集合中下一个 EventLoop
  • EventLoopGroup 是一组 EventLoop 的抽象,Netty 为了更好的利用多核CPU资源,一般会有多个 EventLoop 同时工作,每个 EventLoop 维护着一个 Selector 实例。
  • EventLoopGroup 提供 next 接口,可以从组里面按照一定规则获取其中一个 EventLoop来处理任务。在 Netty 服务器端编程中,我们一般都需要提供两个 EventLoopGroup,例如:BossEventLoopGroup 和 WorkerEventLoopGroup。
  • 通常一个服务端口即一个 ServerSocketChannel对应一个Selector 和一个EventLoop线程。BossEventLoop 负责接收客户端的连接并将 SocketChannel 交给 WorkerEventLoopGroup 来进行 IO 处理
  • BossEventLoopGroup 通常是一个单线程的 EventLoop,EventLoop 维护着一个注册了ServerSocketChannel 的 Selector 实例 。BossEventLoop 不断轮询 Selector 将连接事件分离出来
  • 通常是 OP_ACCEPT 事件,然后将接收到的 SocketChannel 交给 WorkerEventLoopGroup
  • WorkerEventLoopGroup 会由 next 选择其中一个 EventLoop来将这个 SocketChannel 注册到其维护的 Selector 并对其后续的 IO 事件进行处理

image.png

线程的分配

服务于 Channel 的 I/O 和事件的 EventLoop 包含在 EventLoopGroup 中。
异步传输实现只使用了少量的 EventLoop(以及和它们相关联的 Thread),而且在当前的线程模型中,它们可能会被多个 Channel 所共享。这使得可以通过尽可能少量的 Thread 来支撑大量的 Channel,而不是每个 Channel 分配一个 Thread。EventLoopGroup 负责为每个新创建的 Channel 分配一个 EventLoop。在当前实现中,使用顺序循环(round-robin)的方式进行分配以获取一个均衡的分布,并且相同的 EventLoop 可能会被分配给多个 Channel
一旦一个**Channel被分配给一个EventLoop,它将在它的整个生命周期中都使用这个EventLoop(**以及相关联的 Thread)。请牢记这一点,因为它可以使你从担忧你的 ChannelHandler 实现中的线程安全和同步问题中解脱出来。
需要注意,EventLoop 的分配方式对 ThreadLocal 的使用的影响。因为一个 EventLoop 通 常会被用于支撑多个 Channel,所以对于所有相关联的 Channel 来说,ThreadLocal 都将是一样的。这使得它对于实现状态追踪等功能来说是个糟糕的选择。然而,在一些无状态的上下文中,它仍然可以被用于在多个 Channel 之间共享一些重度的或者代价昂贵的对象,甚至是事件。
image.png