3.4 管道处理器和数据流
我们需要看一下您发送或接收数据时会发生什么情况。在本章开始时,我们提到Netty具有处理程序的概念。要了解数据在写入或读取时会发生什么,首先必须了解什么是处理程序。处理程序本身依赖于上述ChannelPipeline来规定其执行顺序。因此,在没有定义ChannelPipeline的情况下就不可能定义处理程序的某些方面,而在没有定义ChannelHandlers的情况下就不可能定义ChannelPipeline的某些方面。毋庸置疑,我们必须从一个开始,然后再到另一个,对自己和彼此进行定义。下一节将以使这种循环依赖性可以忽略的方式介绍ChannelHandlers和ChannelPipeline。
3.4.1 ChannelPipeline 和 handlers
在许多方面,Netty ChannelHandler是您的应用程序最常处理的内容。即使您没有意识到这一点,如果您使用的是Netty应用程序,那么某个地方也会涉及至少一个ChannelHandler。换句话说,它们是许多事情的关键。那到底是什么呢?定义ChannelHandler并不容易,因为它们是如此通用,但是ChannelHandler可以被视为处理通过ChannelPipeline传入和传出的数据的任何代码。实际上,有一个父接口定义了一个称为ChannelHandler的处理程序。由此得出ChannelInboundHandler和ChannelOutboundHandler,如图3.5所示。

如果从数据流的角度来解释ChannelHandlers会更容易,但是重要的是要记住,本次讨论中使用的示例仅仅是示例。您将发现在本书的其余部分中,ChannelHandlers还可以通过许多其他方式应用。数据在Netty内部沿两个方向流动,如图3.5所示,入站(ChannelInboundHandler)和出站(ChannelOutboundHandler)处理器之间有明显的区别。如果预期的流量是从用户应用程序到远程端点的话,则称数据是出站的。相反,如果数据是从远程端点到用户应用程序的,则入站。
为了使数据通常从一端到达另一端,一个或多个ChannelHandler将以某种方式操纵数据。这些ChannelHandler将在应用程序的引导阶段添加,并且添加的顺序决定了它们处理数据的顺序。
ChannelHandler以特定顺序进行的这种安排实际上构成了我们一直称为ChannelPipeline的内容。每个ChannelHandler对数据执行其操作(如果可以处理,例如入站数据只能由ChannelInboundHandlers处理)然后可以将转换后的数据传递到ChannelPipeline中的下一个ChannelHandler,直到不再剩下ChannelHandler为止。
ChannelHandler 和 Servlet 相似点
实际上,Netty中使用的设计类似于Servlet中使用的设计。 ChannelHandler可以对数据执行某些操作,然后将其传递给ChannelPipeline中的下一个ChannelHandler。另一个经常执行的操作是根本不执行任何操作,而只是将特定事件传递给ChannelPipeline中的下一个ChannelHandler。然后,下一个ChannelHandler可以处理它,或者仅将其再次转发到下一个ChannelHandler。
图3.6显示了ChannelPipeline排列的示例。

如图3.6所示,ChannelInboundHandler和ChannelOutboundHandler都可以混合到同一ChannelPipeline中。
在此ChannelPipeline中,如果已读取消息或任何其他入站事件,则它将从ChannelPipeline的开头开始并传递到第一个ChannelInboundHandler。此ChannelInboundHandler可以处理事件,并且/或者将其传递给ChannelPipeline中的下一个ChannelInboundHandler。一旦ChannelPipeline中不再有ChannelnboundHandler,它就 到达了ChannelPipeline的尾部,这意味着将不再进行任何处理。
反之亦然,任何出站事件(例如,写入)都将从ChannelPipeline的尾部开始,并传递到ChannelPipeline中的最后一个ChannelOutboundHandler。在这里,它的行为与ChannelInboundHandler相同,这意味着它可以处理事件和/或将事件传递给ChannelPipeline中的下一个ChannelOutboundHandler。不同之处在于,当出站事件从ChannelPipeline的尾部流向头部时,下一个ChannelOutboundHandler实际上是前一个。一旦不再有可以将事件传递到的ChannelOutboundHandler,它将触发实际的传输(可能是网络套接字),因此触发对该事件的某些操作。例如,这可以是写操作。
ChannelInboundHandler and ChannelOutboundHandler base classes
通过使用传入每个方法的ChanneHandlerContext,可以将一个事件转发到ChannelPipeline中的下一个ChannelInbound或上一个ChannelOutboundHandler。因为这是您通常对Netty中的事件不感兴趣,所以它们提供了称为ChannelInboundHandlerAdapter和ChannelOutboundHandlerAdapter的抽象基类。每个方法都为每个方法提供实现,并通过在ChannelHandlerContext上调用相应的方法,将事件传递给ChannelPipeline中的next / prev处理程序。然后,您可以覆盖所讨论的方法以实际执行所需的处理。这意味着Netty可以跳过任何不是特定类型的处理程序,因此不能处理给定的操作。
您可能想知道,因此如果出站操作和入站操作是不同的,那么当处理程序混合在同一ChannelPipeline中时,这如何工作?回到图3.4,请记住,入站和出站处理程序具有不同的接口,都扩展ChannelHandler。因此,对于出站事件,由于Netty知道每个处理程序是否为ChannelInboundHandler或ChannelOutboundHandler的实现,因此将跳过ChannelInboundHandler。
将ChannelHandler添加到ChannelPipeline后,它还将获得所谓的ChannelHandlerContext。通常,获取对该对象的引用并保持引用状态是安全的。当使用数据报协议(例如UDP)时,情况并非如此。该对象以后可以用于获取基础通道,但是通常会保留下来,因为您使用它来编写/发送消息。这意味着在Netty中有两种发送消息的方法。您可以直接写入通道或写入ChannelHandlerContext对象。两者之间的主要区别在于,写入通道会直接导致消息从ChannelPipeline的尾部开始,写入上下文对象会使消息从ChannelPipeline中的下一个处理程序开始。
最后更新于
这有帮助吗?