Netty实战读书笔记三:EventLoop和线程模型

来源:互联网 发布:java电子邮箱格式校验 编辑:程序博客网 时间:2024/06/04 00:41

EventLoop 接口

运行任务来处理在连接的生命周期内发生的事件是任何网络框架的基本功能。与之相应的编 程上的构造通常被称为事件循环— 一个 Netty 使用了 interface io.netty.channel. EventLoop 来适配的术语。

代码清单 7-1 中说明了事件循环的基本思想,其中每个任务都是一个 Runnable 的实例。

Netty 的 EventLoop 是协同设计的一部分,它采用了两个基本的 API:并发和网络编程。 首先,io.netty.util.concurrent 包构建在 JDK 的 java.util.concurrent 包上,用 来提供线程执行器。其次,io.netty.channel 包中的类,为了与 Channel 的事件进行交互, 扩展了这些接口/类。图 7-2 展示了生成的类层次结构。

在这个模型中,一个 EventLoop 将由一个永远都不会改变的 Thread 驱动,同时任务 (Runnable 或者 Callable)可以直接提交给 EventLoop 实现,以立即执行或者调度执行。 根据配置和可用核心的不同,可能会创建多个 EventLoop 实例用以优化资源的使用,并且单个EventLoop 可能会被指派用于服务多个 Channel。

需要注意的是,Netty的EventLoop在继承了ScheduledExecutorService的同时,只定义了一个方法,parent()1。这个方法,如下面的代码片断所示,用于返回到当前EventLoop实 现的实例所属的EventLoopGroup的引用。

public interface EventLoop extends EventExecutor, EventLoopGroup {            @Override EventLoopGroup parent();}

事件/任务的执行顺序 事件和任务是以先进先出(FIFO)的顺序执行的。这样可以通过保证字
节内容总是按正确的顺序被处理,消除潜在的数据损坏的可能性。

Netty 4 中的 I/O 和事件处理

在Netty 4 中,所有的I/O操作和事件都由已经被分配给了 EventLoop的那个Thread来处理。Netty 4 中所采用的线程模型,通过在同一个线程中处理某个给定的 EventLoop 中所产生的所有事件,解决了这个问题。这提供了一个更加简单的执行体系架构,并且消除了在多个 ChannelHandler 中进行同步的需要(除了任何可能需要在多个 Channel 中共享的)。

线程模型

Netty线程模型的卓越性能取决于对于当前执行的Thread的身份的确定 2,也就是说,确定 它是否是分配给当前Channel以及它的EventLoop的那一个线程。(回想一下EventLoop将负责处理一个Channel的整个生命周期内的所有事件。)

如果(当前)调用线程正是支撑 EventLoop 的线程,那么所提交的代码块将会被(直接)执行。否则,EventLoop 将调度该任务以便稍后执行,并将它放入到内部队列中。当 EventLoop 下次处理它的事件时,它会执行队列中的那些任务/事件。

这也就解释了任何的 Thread 是如何 与 Channel 直接交互而无需在 ChannelHandler 中进行额外同步的。

注意,每个 EventLoop 都有它自已的任务队列,独立于任何其他的 EventLoop。图 7-3 展示了 EventLoop 用于调度任务的执行逻辑。这是 Netty 线程模型的关键组成部分。

我们之前已经阐明了不要阻塞当前 I/O 线程的重要性。我们再以另一种方式重申一次:“永 远不要将一个长时间运行的任务放入到执行队列中,因为它将阻塞需要在同一线程上执行的任何 其他任务。”如果必须要进行阻塞调用或者执行长时间运行的任务,我们建议使用一个专门的 EventExecutor。(见 6.2.1 节的“ChannelHandler 的执行和阻塞”)。

EventLoop/线程的分配

异步传输

异步传输实现只使用了少量的 EventLoop(以及和它们相关联的 Thread),而且在当前的线程模型中,它们可能会被多个 Channel 所共享。这使得可以通过尽可能少量的 Thread 来支撑大量的 Channel,而不是每个 Channel 分配一个 Thread。

一旦一个 Channel 被分配给一个 EventLoop,它将在它的整个生命周期中都使用这个 EventLoop(以及相关联的 Thread)。请牢记这一点,因为它可以使你从担忧你的 Channel- Handler 实现中的线程安全和同步问题中解脱出来。

另外,需要注意的是,EventLoop 的分配方式对 ThreadLocal 的使用的影响。因为一个 EventLoop 通常会被用于支撑多个 Channel,所以对于所有相关联的 Channel 来说, ThreadLocal 都将是一样的。这使得它对于实现状态追踪等功能来说是个糟糕的选择。然而, 在一些无状态的上下文中,它仍然可以被用于在多个 Channel 之间共享一些重度的或者代价昂 贵的对象,甚至是事件。

阻塞传输

用于像 OIO(旧的阻塞 I/O)这样的其他传输的设计略有不同,如图 7-5 所示。

这里每一个 Channel 都将被分配给一个 EventLoop(以及它的 Thread)。如果你开发的 应用程序使用过 java.io 包中的阻塞 I/O 实现,你可能就遇到过这种模型。

但是,正如同之前一样,得到的保证是每个 Channel 的 I/O 事件都将只会被一个 Thread (用于支撑该 Channel 的 EventLoop 的那个 Thread)处理。这也是另一个 Netty 设计一致性的例子,它(这种设计上的一致性)对 Netty 的可靠性和易用性做出了巨大贡献。

阅读全文
0 0
原创粉丝点击