Netty EventLoop and threading model

关注微信公众号(瓠悠笑软件部落),大家一起学习,一起摸鱼。
huyouxiao.com
本章将介绍:

  • 线程模型概述
  • 事件循环概念和实现
  • 任务调度
  • 实现细节

简单地说,线程模型指定了OS,编程语言,框架或应用程序上下文中线程管理的关键方面。如何以及何时创建线程显然会对应用程序代码的执行产生重大影响,因此开发人员需要了解与不同模型相关的权衡。 无论他们自己选择模型还是通过采用语言或框架隐式获取模型,都是如此。

在本章中,我们将详细研究Netty的线程模型。 它功能强大但易于使用,与Netty一样,旨在简化应用程序代码并最大限度地提高性能和可维护性。 我们还将讨论导致选择当前模型的经验。

如果您对Java的并发API(java.util.concurrent)有一个很好的了解,那么您应该直接在本章中找到讨论。 如果您对这些概念不熟悉或需要回顾一下,那么Brian Goetz等人的Java Concurrency in Practice。 (Addison-Wesley Professional,2006)是一个很好的资源。

7.1 Threading model overview

在本节中,我们将介绍一般的线程模型,然后讨论Netty的过去和现在的线程模型,回顾每个模型的优点和局限性。

正如我们在本章开头所指出的,线程模型指定了代码的执行方式。因为我们必须始终防范并发执行可能的副作用,了解所应用模型的含义很重要(还有单线程模型)。忽视这些事情只是希望最好的事情等于赌博 - 绝对不利于你。

由于具有多个内核或CPU的计算机很常见,因此大多数现代应用程序都采用复杂的多线程技术来高效利用系统资源。相比之下,我们在Java早期的多线程方法只不过是创建和启动新的Thread来执行并发工作单元,这是一种在繁重负载下运行不佳的原始方法。 Java 5
然后介绍了Executor API,其线程池通过线程缓存和重用大大提高了性能。

基本线程池模式可以描述为:

  • 从池的空闲列表中选择一个线程,并分配该线程以运行提交的任务(Runnable的实现)。
  • 任务完成后,线程将返回到列表并可供重用。
    这个模式就像图7.1阐述的那样:

Executor execution logic

池化和重用线程是对每个任务创建和销毁线程的改进,但它并没有消除上下文切换的成本,这很快就会随着线程数量的增加而变得明显,并且在重负载下会很严重。此外,仅仅因为应用程序的整体复杂性或并发性要求,在项目的生命周期中可能会出现其他与线程相关的问题。

简而言之,多线程可能很复杂。在接下来的部分中,我们将看到Netty如何帮助简化它。

7.2 Interface EventLoop

运行任务来处理连接生命周期中发生的事件,这是任何网络框架的基本功能。相应的编程结构通常被称为事件循环,术语Netty采用接口 io.netty.channel.EventLoop

下面的清单阐述了事件循环的基本概念,每一个任务都是 Runnable 的实例。

Executing tasks in an event loop

Netty的EventLoop是协作设计的一部分,它采用两种基本API:并发和网络。首先,Netty的包 io.netty.util.concurrent 是基于JDK java.util.concurrent 的JDK包构建的。其次,包io.netty.channel中的类扩展了这些类以便与Channel事件接口。生成的类层次结构如图7.2所示。

EventLoop class hierarchy
在此模型中,EventLoop由一个永不更改的Thread提供支持,任务(Runnable或Callable)可以直接提交给EventLoop实现,以便立即执行或计划执行。这取决于配置和
可用的CPU核数量,可以创建多个EventLoop以优化资源使用,并且可以分配单个EventLoop来服务多个 Channel。

请注意,Netty的EventLoop虽然扩展了ScheduledExecutorService,但只定义了一个方法parent() 。此方法(如以下代码段所示)旨在返回对当前EventLoop实现实例所属的EventLoopGroup的引用。

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

Event/Task Execution order
事件和任务是以先进先出的顺序执行的。通过保证以正确的顺序处理字节内容,这消除了数据损坏的可能性。

7.2.1 I/O and event handling in Netty 4

正如我们在第6章中详细描述的那样,由I / O操作触发的事件流经具有一个或多个已安装的ChannelHandler的ChannelPipeline。传播这些事件的方法调用然后可以被ChannelHandler拦截,并根据需要处理事件。

事件的性质通常决定了它的处理方式;它可能会将数据从网络堆栈传输到您的应用程序中,反之亦然,或者执行完全不同的操作。但事件处理逻辑必须通用且足够灵活以处理所有可能的用例。因此,在Netty 4中,所有I / O操作和事件都由已分配给 EventLoop 的 Thread 处理。

这与Netty 3中使用的模型不同。在下一节中,我们将讨论早期模型及其替换原因。

7.2.2 I/O operations in Netty 3

先前版本中使用的线程模型仅保证入站(以前称为上游)事件将在所谓的I / O线程中执行(对应于Netty 4的EventLoop)。通过调用线程处理了所有出站(下游)事件,可能是I / O线程或任何其他线程。这一开始似乎是一个好主意,但由于需要仔细同步ChannelHandler中的出站事件,因此发现它存在问题。简而言之,无法保证多个线程不会同时尝试访问出站事件。例如,如果您同时触发下游事件,则可能会发生这种情况通过在不同的线程中调用 Channel.write() 来获得相同的Channel。

由于出站事件而触发了入站事件,因此发生了另一个负面影响。当 Channel.write() 导致异常时,您需要生成并触发exceptionCaught事件。但是在Netty 3模型中,因为这是一个入站事件,所以你在调用线程中执行代码,然后将事件交给I / O线程执行,并进行相应的上下文切换。

Netty 4中采用的线程模型通过在同一个线程中给定EventLoop中处理发生的所有事情解决了这些问题。这提供了更简单的执行架构,并且消除了ChannelHandler中的同步需求(除了可能在多个Channel之间共享的任何内容)。

既然您了解了EventLoop的作用,那么让我们看看如何安排任务执行。

7.3 Task scheduling

有时,您需要为以后(延迟)或定期执行安排任务。例如,您可能希望在客户端连接五分钟后注册要触发的任务。一个常见的用例是向心跳发送心跳消息以检测远程对等体检查连接是否仍然存在。如果没有响应,您知道可以关闭该频道。在下一节中,我们将向您展示如何使用核心Java API和Netty的EventLoop来安排任务。然后,我们将研究Netty实现的内部结构,并讨论它的优点和局限性。

7.3.1 JDK schedulingAPI

在Java 5之前,任务调度是基于 java.util.Timer 构建的,它使用后台线程并且具有与标准线程相同的限制。随后,JDK提供了 java.util.concurrent 包,它定义 ScheduledExecutorService接口。表7.1显示了 java.util.concurrent.Executors 的相关工厂方法。

java.util.concurrent.Executors factory methods
虽然没有多少选择,但是对于大多数用例来说,提供的选项已经足够了。下一个清单显示了如何使用ScheduledExecutorService在60秒延迟后运行任务。

Scheduling a task with a ScheduledExecutorService
虽然ScheduledExecutorService API很简单,但是负载很重可能引入性能消耗。在下一节中,我们将了解Netty如何以更高的效率提供相同的功能。

7.3.2 Scheduling tasks using EventLoop

ScheduledExectorService 的实现有很多限制,ScheduledExecutorService实现具有一些局限性,例如需要创建额外线程,作为线程池管理的一部分。如果许多任务被积极安排,这可能成为瓶颈。 Netty通过使用频道的 EventLoop 实施调度来解决这个问题,如下面的清单所示。
Scheduling a task with EventLoop
经过60秒后,Runnable实例将由分配给通道的EventLoop执行。要安排每60秒执行一次任务,请使用 scheduleAtFixedRate(),如下所示。

Scheduling a recurring task with EventLoop
正如我们前面提到的,Netty的EventLoop扩展了ScheduledExecutorService(见图7.2),因此它提供了JDK实现的所有可用方法,包括schedule()和scheduleAtFixedRate(),在前面的例子中使用。所有操作的完整列表可以在Javadocs中找到ScheduledExecutorService

Canceling a task using ScheduledFuture
这些示例说明了利用Netty的调度功能可以实现的性能提升。反过来,这些依赖于底层线程模型,我们将在下面进行讨论。

7.4 Implementation details

本节更详细地探讨了Netty线程模型和调度实现的主要元素。我们还将提到需要注意的限制,以及正在进行的开发领域。

7.4.1 Thread management

Netty线程模型的优越性能取决于确定当前正在执行的线程的身份;也就是说,它是否是指定的那个到当前 Channel 及其 EventLoop。 (回想一下EventLoop是负责任的
用于处理 Channel在其生命周期内的所有事件。)

如果调用Thread是EventLoop的Thread,则执行相关的代码块。否则,EventLoop会调度任务以供以后执行,并将其放入内部队列。当EventLoop接下来处理它的事件时,它将执行那些事件队列。这解释了任何线程如何直接与Channel进行交互,而无需 ChannelHandler中进行同步。

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

我们之前说过了不阻塞当前I / O线程的重要性。我们将以另一种方式再次说明:“永远不要在执行队列中放置一个长时间运行的任务,因为它会阻止任何其他任务在同一个线程上执行。”如果你必须进行阻塞调用或执行长时间运行的任务,我们建议使用dedicatedEventExecutor。

EventLoop execution logic
除了这样的限制情况之外,使用中的线程模型可以强烈地影响排队任务对整体系统性能的影响,所采用的传输的事件处理实现也是如此。 (正如我们在第4章中看到的那样,Netty可以在不修改代码库的情况下轻松切换传输。)

7.4.2 EventLoop/thread allocation

为Channel提供I / O和Event事件的EventLoop包含在EventLoopGroup中。创建和分配EventLoop的方式因传输实现而异。

Asynchronous Transports
异步实现仅使用少量EventLoop(及其关联的线程),并且在当前模型中,这些可以在Channel之间共享。这允许许多Channel由最小可能数量的Thread服务,而不是为每个Channel分配一个Thread。

图7.4显示了一个EventLoopGroup,其固定大小为三个EventLoop(每个由一个Thread提供支持)。 创建EventLoopGroup时,EventLoop(及其Thread)直接分配,确保它们在需要时可用。 EventLoopGroup负责为每个新创建的Channel分配EventLoop。在当前实现中,使用循环方法实现了平衡分布,并且可以将相同的EventLoop分配给多个Channel。
(这可能会在将来的版本中发生变化。)

EventLoop allocation for non-blocking transorts(such as NIO and AIO)

一旦为Channel分配了一个EventLoop,它将在整个生命周期内使用此EventLoop(以及相关的Thread)。记住这一点,因为它使您免于担心ChannelHandler实现中的线程安全和同步。

另外,请注意EventLoop分配对ThreadLocal使用的影响。因为EventLoop通常支持多个Channel,所以ThreadLocal对于所有关联的Channel都是相同的。这使得它成为实现状态跟踪等功能的不良选择。但是,在无状态环境中,它仍然可以用于在Channel之间共享繁重或昂贵的对象,甚至是事件。

Blocking Transports
其他传输设备(如OIO(旧阻塞I / O))的设计略有不同,如图7.5所示。
EventLoop allocation of blocking transports

这里为每个Channel分配了一个EventLoop(及其Thread)。如果您已经开发了在java.io包中使用阻塞I / O实现的应用程序,则可能遇到过此模式。
但就像以前一样,保证每个Channel的I / O事件只能由一个Thread处理 - 一个为Channel的EventLoop提供支持的Thread。这是Netty设计一致性的另一个例子,它对Netty的可靠性和易用性有很大贡献。

7.5 Summary

在本章中,您了解了一般的线程模型和特别是Netty的线程模型,我们讨论了它们的性能和一致性优势详细地。
您已经了解了如何在EventLoop(I / O线程)中执行您自己的任务
框架本身。您学习了如何为延迟执行安排任务,并且我们在重负载下检查了可伸缩性问题。您还看到了如何验证任务是否已执行以及如何取消它。

通过我们对框架实现细节的研究,这些信息将帮助您最大限度地提高应用程序的性能,同时简化其代码库。有关线程池和并发编程的更多信息,我们建议使用Brian Goetz的Java Concurrency in Practice。他的书将使您更深入地了解即使是最复杂的多线程用例。

我们已经达到了令人兴奋的一点 - 在下一章我们将讨论 bootstrapping,配置和连接所有Netty组件的过程,好让你的程序跑起来。

深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值