当前位置: 首页 > 网络应用技术

一篇文章谈论了管道(下图)

时间:2023-03-06 23:15:10 网络应用技术

  然后,我们继续在上一篇文章“一篇文章谈论Netty IO活动”中介绍管道的设计和实施!

  在我们介绍了出站事件的所有入站事件和掩模表示以及事件的触发和传播路径之后,我相信每个人都可以根据特定的业务场景选择适当的ChannelHandler类型以及对ChannelHandler类型的适当监视通过ChannelInboundHandler和ChannelOutBoundHandler。

  本节应介绍如何在管道中添加自定义通道手。Netty在此过程中做什么工作?

  以上是Netty Server配置ServerBootstrap启动的一部分。我们可以看到,将ChannelHandler添加到相应的管道中,将ChannelHandler添加到通过ChannelPipipipeLine#ADDLAST方法的管道末端。

  最后,这些沉重的AD的重载方法将被调用到此方法中,以完成ChannelHandler的添加。

  该方法的逻辑相对复杂。它涉及许多细节。为了清楚地告诉所有人,作者仍然使用总分的总结构,首先描述了该方法的整体逻辑,然后在核心细节的要点扩展了详细分析。

  由于将ChannelHandler添加到管道的操作可以在多个线程中执行,以确保操作的操作安全性,因此使用同步句子块来包装整个逻辑软件包。

  您需要注意的是,如果一个频道手用@sharable注意标记,这意味着可以在多个管道中多次添加其一个实例(每个通道对应于一个管道实例),并且许多不同的不同差异可能是由不同的反应器线程执行,因此您需要在使用共享ChannelHandler时确保线程的安全性。

  例如,以下示例代码:

  Echoserverhandler是我们为我们定制的频道手。它以@sharable标记。在整体情况下,只有一个示例,并且将其添加到多个通道管道中。结果,它将由多个反应器线程执行。

  但是,您需要注意的是:尽管此时将ChannelHandlerContext物理插入到管道中,但此时仍在instrahnhandler的状态。从逻辑上讲,它并没有真正插入管道中。您需要等到当恢复处理方法的当时频道手的时,状态将变为add_complete,并且仅添加的channeHandler可以添加_complete,可以在管道中响应该事件。

  在上一篇文章中的“ NextChannelHandler的写入方法”中,我们还在文章中的一篇有关Netty发送数据的过程中的一篇文章中,我们还在NextChannelHandler的写作方法的部分中提到了是否确定ChannelHandler的状态是否是否确定是add_complete,否则当前的ChannelHandler无法响应在管道中扩散的事件。必须等到恢复相应的处理方法,因为处理方法可能包含ChannelHandler初始化的一些重要逻辑。

  实际上,不仅是写入事件和冲洗事件,还需要在传播时确定频道手的状态。所有入站事件和出站事件都需要使用InvokeHandler方法来确定当前频道Handler的状态是否为add_complete。在频道Handler响应之前,其处理方法被称为返回。

  该逻辑主要用于处理ChannelInitializer的添加方案,因为当前只有特殊的频道Handler(特殊的ChannelHandler)才能在频道添加到管道中之前添加。

  添加已待处理的HandlerAdedTask任务到Pipeline的任务列表pendendLandLercallBackhead:

  待处理的手段任务负责调用ChannelHandler中的处理方法。

  如果当前执行线程不是ChannelHandler指定的executor(!executor.ineventloop()),则在频道指定的执行程序中需要处理处理方法的回调。

  重要的是要注意,您需要在回调处理方法之前将频道手的状态设置为add_complete。目前,它将停止对事件的回应并错过事件。

  这属于用户的极端使用。

  在将ChannelHandler添加到Pipeline中添加的整个逻辑过程之后,我们将看到如何为ChannelHandler为ChannelHandler创建相应的ChannelHandLercontext,以及在ChannelHandleContext中包含哪些上下文信息。

  在创建ChannelHandlerContext之前,您需要进行两个重要的领域操作:

  如果用户再次将ChannelHandler添加到管道,则指定了它的特定名称,则此处指定的名称在管道中是唯一的。

  如果用户未指定ChannelHandler的名称,则他需要在管道中为ChannelHandler生成一个唯一名称。

  管道使用fastThreadLocal类型的namecacher来缓存各种类型的ChannelHandler.Later的基本名称,正式名称,并未按照此基本名称进行冲突。高速缓存namecaches中的键代表特定的ChannelHandler类型,值代表基本名称代表基本名称。该特定类型的频道手。

  ChannelHandler的自动名称的默认名称的逻辑是:

  尽管用户不太可能重复相同类型的频道手管道,但Netty是由相同类型的同类类型的ChannelHandler的名称引起的冲突,以防止这种情况,从而缓存相同类型的ChannelHandler的基本名称,然后连续使用它。重新修复增加的名称后缀以生成管道中唯一的名称。

  通过上一个介绍,我们了解到,当我们将ChannelHandler添加到管道中时,Netty允许我们为ChannelHandler指定特定的执行者,以在ChannelHandler中执行各种事件。

  通常,我们为ChannelHandler指定EventExeCutorGroup。创建ChannelHandLercontext时,使用ChildExeCutor方法通过Everdexecutor方法从ChannelHandler中选择EventExecutor。

  EventExeCutorGroup是一个NetTy定义的线程池模型,包含多个EventExecutor,Evernexecutor是Netty中的线程执行模型。可以审查忘记的学生。

  在介绍执行程序的绑定逻辑之前,作者需要在此处向您介绍一个相关的重要参数:默认值为真。

  我们知道,在Netty中,每个通道将对应于独立管道。如果我们启动参数,则意味着在与通道相对应的管道中,如果我们为多个ChannelHandler指定相同的EventXecutorGroup,则此多个ChannelHandler只能在EventExecutorGroup中使用同一EventXecutor。

  这是什么意思?相交,例如,我们有以下初始化代码:

  EventExeCutorGroup包含三个执行线程:EventExeCutor1,EventExeCutor2和EventExeCutor3。

  假设此时第一个连接是连接的,则在创建Channel1之后初始化Pipeline1时,如果打开了参数,则EverTut1CutorGroup键入EverHandler1,ChannelHandler2和ChannelHandler3。

  在与通道2相对应的第二个管道2中,EventEnexecutor 2由ChannelHandler1,ChannelHandler2和ChannelHandler3绑定的EventExeCutor2是EventExeCutorGroup中的EventExeCutor2。

  连接EventExeCutor3的第三个由ChannelHandler1,ChannelHandler2和ChannelHandler3在第三个连接EventExecutor3中的第三个ChannelHandler3。

  接受等等..........

  如果关闭了参数,则与通道1相对应的管道中的ChannelHandler1将绑定到EventExecutorGroup中的EventExeCutor1。ChannelHandler2将绑定到EventExeCutor2,并且ChannelHandler3将绑定到FiveCutor3。

  同样,与另一个通道相对应的管道中的通道手绑定逻辑与通道1相同。它们在EventExecutorGroup中粘合到不同的Evertexecutor。

  当我们了解参数的作用时,很容易理解以下绑定逻辑。

  如果我们不指定ChannelHandler的执行人,则默认值将是与通道对应的反应器线程负责执行ChannelHandler。

  如果我们没有打开,Netty将以我们指定的指定方式将Match-Rabin的EventExecutor绑定。

  如果我们打开,则固定了同一管道实例中同一EventExecutorGroup的绑定关系。在管道中,如果多个ChannelHandler指定相同的EventXecutorGroup,则这些ChannelHandler的执行程序将被束缚在固定的EventExecutor上。

  这种固定的绑定关系缓存每个管道中的地图 在ChildExeCutors字段中,KEY是用户为ChannelHandler指定的EventExeCutorGroup,值是管道实例中的绑定EventExecutor。

  下一步是从ChildExeCutors中获取管道实例中指定的EventExecutorGroup。

  如果建立了绑定关系,则通道手直接指定指定绑定事件Exececuter。

  在介绍了创建ChannelHandLercontext的两个预操作后,我们回顾了ChannelHandlerContext中包含的特定上下文信息。

  在这里,作者专注于订单属性和ExecutionMask属性。每个人都很容易理解其他属性。

  如果我们不指定channelhandler或指定的执行程序类型的执行人是orderevanentector,则有序= true。

  那么,此顺序属性会在管道中的频道响应中影响事件有什么影响?

  我们以前曾介绍过,在频道Handler中的事件响应管道中,将调用InvokeHandler()方法,以确定是否仍跳过调用ChannelHandler的事件调用方法。

  另一个重要的属性保留了当前频道Handler的某些执行条件信息掩码,例如:::

  在这里,您需要一个fastThreadLocal类型掩码字段来缓存与ChannelHandler相对应的执行掩码。由于定义了ChannelHandler类,其执行掩码是固定的,并且Netty需要接收大量的连接,创建大量的频道,,,,,,,,创建大量的频道,,,,,,,,,地机为facterler basks obl.并初始化这些通道的相应管道。在中间,掩模需要在此处缓存。

  尽管ChannelHandler的执行掩码方法相对较长,但逻辑非常简单。在本文的第三部分“ 3.管道中的事件分类”中,作者介绍了各种事件类型的遮罩,我在这里看到如何看待使用这些基本事件掩码来计算ChannelHandler执行操作的执行。

  如果ChannelHandler是类型ChannelInboundHandler,则所有入站事件蒙版将设置为执行掩码掩码。

  最后,所有入站事件都是一个接一个地旅行的,频道Handler对面具收集面具不感兴趣。在这样的回合之后,ChannelHandler被执行。

  在此过程中,我们可以看到ChannelHandler的执行掩码包含ChannelHandler感兴趣的事件掩码集合。当事件在管道中传播时,可以在ChannelHandlerContext中使用此执行掩码来判断当前的ChannelHandler是否合格对于事件。

  以同样的方式,我们还可以计算与ChannelOutBoundHandler类型的ChannelHandler相对应的执行掩码。

  那么,Netty框架如何判断我们定制的ChannelHandler感兴趣的事件以及哪些事件对哪些事件不感兴趣?

  在这里,我们以ChannelInboundHandler类型为例。在本文的第三部分中,作者对所有入站事件进行了全面的介绍,但是在实际开发中,我们可能不需要聆听所有入站事件,我们可能只需要监视聆听聆听。或他们两个。

  对于我们不感兴趣的事件,我们只需要在其相应的回调方法上标记@skip注释即可。Netty会认为ChannelHandler对@SKIP注释的事件不感兴趣。ChannelHandler不需要执行响应。

  当我们编写自定义ChannelHandler时,是否会在所有事件恢复方法中提供由ChannelInboundHandler或ChannelOutBoundHandler接口提供的所有事件恢复方法,而我们对@SKIP注释不感兴趣的事件很麻烦?

  实际上,不需要。Netty为我们提供了ChannelInboundHandlerAdapter类和ChannelOutBoundlerAdapter类。Netty已经在这些适配器类中标记为@SKIP注释。类并涵盖我们感兴趣的事件恢复方法。

  从上一节的内容来看,我们可以看到将ChannelHandler添加到管道中的逻辑仍然更加复杂,并且涉及更多细节。

  然后,在了解将ChannelHandler添加到管道的过程之后,从管道中删除ChannelHandler的逻辑就得到了充分的理解。

  Netty提供了上述三种方法,可以从管道中删除指定的通道手。在下面,我们以第一种方式作为示例来介绍ChannelHandler的删除过程。

  首先,您需要找到通过getContextordie方法对应于管道中的ChannelHandler的指定的ChannelHandelRcontext。为了确认要删除的通道Handler确实存在于管道中。

  上下文方法是在管道中找到两条路链接列表,以查找要删除的ChannelHandlerContext。

  删除方法的总体代码结构与Addlast0方法的代码结构相同。总体逻辑是在管道中的两个路链接列表结构中删除指定的channehandlercontext,然后处理已删除的ChannelHandler中处理方法的恢复。

  在ChannelHandler中执行HandleRemped回调时,您需要判断ChannelHandler的状态:只有当HandlerState为add_complete时,才能调整HandleRremaver的方法。

  此处表达的语义是,只有在恢复了Channehanler的处理方法后,才能从管道中删除Channelhanler时恢复其处理的方法。

  在恢复了通道手的处理方法之后,将通道手的状态设置为删除_complete。

  实际上,有关管道初始化的相关内容。我们已经简要介绍了Niosterversotcetcetcetcetcetcetcetcetcetcetotor中的Nioserversocketchannel中管道的初始化和过程。

  作者还简要介绍了Niosocketchannel的Niosocketchannel中管道的初始化时间和过程。

  本节的作者将结合这两种类型的渠道,以充分介绍管道的整个初始化过程。

  从前面提到的两篇文章和本文之前的相关内容中,我们知道Netty提供了一个特殊的ChannelInboundHandler,称为ChannelInitializer。用户可以使用此特殊的ChannelHandler自定义通道中管道的初始化逻辑。

  如果用户只想在管道中添加固定的通道手,以直接通过以下代码添加它。

  如果要添加多个ChannelHandler,则可以添加逻辑以通过ChannelInitializer定义定义。

  由于通道化器中Nioserversocketchannel管道中管道的逻辑将变得更加复杂。在下面,我们将使用此复杂情况告诉管道的初始化过程。

  用户自定义以初始化管道来定制的上述渠道启动器,存储在Serverbootstrap启动类中的处理程序字段中。用于后续初始化调用

  当服务器启动时,它将伴随着Nioservesocketchannel的创建和初始化。当Nioserversokcetchannel的初始化时,将在管道中添加新的ChannelInitializer。在新的ChannelInitializer中,将引用用户定义的ChannelInliner安装。然后实现了初始化过程。

  在此引入Netty的原因是引入新的ChannelInitializer来初始化Nioserversocketchannel中管道中的管道。它与前面引入的管道初始化的两种初始化方式兼容。

  忘记Netty创业过程的学生可以回顾作者的文章“ Starty反应堆启动的完整过程”。

  请注意,此时Nioserversocketchannel尚未开始注册到主反应堆。根据此时的“将ChannelHandler到管道的介绍”的引入,此时将此新的ChannelInitializer添加到管道后,Netty将添加到管道任务列表中添加到add.pendingHandlerAdleradedTask.NioServerSocketchannel成功地注册到主反应堆中时,随后添加通过主反应堆线程,将在任务中执行此挂起的手段。在此回调方法中,将执行intchannel方法中的代码。

  当Nioserversocketchannel成功地在主反应堆上注册时,他在管道中逐一执行任务。

  管道任务列表中的Porteline任务列表:

  最终,在管道上的edededdeddeddeddedtask中执行了pipelinitializer中ChannelInitializer的处理式回调。

  该ChannelInitializer是在初始NioServerCocketchannel的INIT方法中添加到管道中的ChannelInitializer。

  在Handelradederer中的ChannelInitializer匿名方法中执行Initchannel方法。请注意,此时执行的ChannelInitializer类是本节开头的Netty框架添加的ChannelInitializer。

  在匿名类ChannelInitializer类中执行Initchannel方法后,您需要从管道中删除ChannelInitializer。并回电,并回电了ChannelInitializer的处理方法。删除过程已详细介绍了“ 6.”。。

  执行非硝化方法后,此时管道的结构如下图所示:

  根据第四部分中描述的添加逻辑,此时NioServersocketchannel已成功地在主反应堆上注册,并且不再需要将PendendHandlerAdedTask任务添加到Pipeine任务列表,但直接直接将其添加到PiendheradLeradedTask任务,但直接CallCustomize在ChannelInitializer中的处理程序的恢复,与上面的逻辑相同。区别在于,用户定义的初始化逻辑的初始化逻辑最终在此处传输。执行用户定义的初始化逻辑,删除用户 - 定义的用户 - 定义 - 定义 -管道的ChannelInitializer。

  然后,Netty将以异步任务的形式将ServerBootstrapacceptor添加到管道末端。此时,NioServersocketchannel中管道的初始化已完成。

  7.1部分中管道初始化的示例相对复杂。当我们找出这个复杂示例的初始化逻辑时,Niosocketchannel中管道的初始化过程变得简单。

  我们在文章“如何有效接收网络连接”中介绍。当客户端启动连接并完成三个握手时,nioserversocketchannel上的OP_ACCEPT事件处于活动状态,然后将在NioServersocketchannel的pipline中触发ChannelRead事件。

  在这里,用户定义的ChannelInitializer被添加到NioSocketchannel的管道中,因为此时NioSocketchannel尚未在子反应器中注册。因此,将ChannelInitializer添加到管道中,将伴随着悬而未决的手机列表。

  每个人都应该熟悉后续过程,这与我们在7.1部分中的介绍完全相同。当NioSocketchannel成功地注册到子反应器时,它将执行Pipeline的任务列表中的悬挂式handleradedTask任务。ChannelItialItialItializerhandelradeded方法,在此方法中执行初始方法,在此处封装了用户定义的初始化的初始化逻辑。管道,从管道中删除ChannelInitializer,并回电其处理方法。

  在这一点上,客户端ocketchannel中管道的初始化已完成。

  在本文的第三部分中,“ 3.管道中的事件分类”,我们将Netty事件类型介绍为三个类别,即入站事件,出站事件和异常事件。,以及触发时机以及事件的方向。

  在本节中,我们将分析事件如何从Netty事件类别中的源代码的角度传播。

  在第三部分中,我们介绍了所有入站事件。这些事件在管道中的逻辑和传播方向是相同的。唯一的区别是回调方法是不同的。

  在本节中,我们以ChannelRead事件的交流为例,以说明入站事件如何在管道中传播的方式。

  我们在第三部分中提到,在Niosocketchannel中,在每个读取循环中读取数据后,在管道中触发了通道读事件的触发时机。

  从这里可以看出,管道中任何入站事件的扩展的起点从HeadContext的耳机节点开始。

  ChannelRead事件从HeadContext开始到管道中传播。首先,它将在HeadContext中回电。

  在ChannelHandler中执行相应的事件调用方法时,指定的执行程序中需要进行回调方法的执行。

  当HeadContext的ChannelReread方法异常时,将调整HeadContext的异常方法。

  继续通过CTX.Firechannelread(MSG)中的CTX.Firechannelread(MSG)向后传播ChannelRead事件。

  FindContextInbound方法这是管道中整个入站事件的核心。

  因为我们需要继续在管道中传播ChannelRead事件,所以我们当前的核心问题是通过FindContextinBound方法找到对ChannelRead事件感兴趣的下一个ChannelInboundHandler。

  ChannelRead事件已经以这种方式在管道中传播。在差异期间,只有对频道读事件感兴趣的Channeinboundhandler可以响应。其他类型的ChannelHandler直接跳过。

  如果ChannelRead事件在管道中传播,则没有其他ChannelInboundhandler可以有效地处理,最终将传输到管道结束的尽头。在本文的第二部分中,我们还提到TailContext对入站事件的存在的意义是进行口袋治疗。例如:打印日志和释放字节。

  本节中介绍的FindContextInbound方法和上一篇文章“通过NetTy发送数据的完整过程进行了一篇文本对话”中介绍的FindContextOutBound方法都是管道中Netty异步事件的核心。

  事件的核心问题是根据管道中事件的方向找到对事件的响应的下一个渠道Handler。

  例如:在这里,我们有我们在管道中传播的频道读事件,我们需要找到对管道中的ChannelRead事件感兴趣的下一个ChannelInboundHandler,并执行ChannelInbouderr.ditermine的ChannelRead事件。通过CTX.Firechannelread(MSG)向后扩展。

  参数蒙版说,我们正在传播频道读事件蒙版mask_channel_read。

  通过CTX = CTX.NEXT在管道中找到下一个ChannelHandler,并确定下一个ChannelHandler是否有资格通过SkipContext方法进行响应事件。如果不是,您将跳过回头。

  例如:下一个ChannelHandler如果是ChannelOutBoundHandler,或者下一个ChannelInboundHandler对ChannelRead事件不感兴趣,则直接跳过。

  该方法主要用于确定下一个ChannelHandler是否具有蒙版所代表的事件的响应资格。

  以上是SkipContext方法的核心逻辑。这里表达的核心语义是:

  这里的大多数学生可能会对这种情况感到困惑。再加上这种情况,实际上,它对我们的核心语义没有太大影响。

  在此需要添加条件的原因是防止HTTPContentCompressor在不同执行者的情况下无法正确创建压缩内容,这会导致某些异常。但是这不是本文的重点。您只需要在这里了解核心语义。只是专门研究这种特殊情况。

  关于出站事件的传播,作者在上一篇文章“一篇文章了解发送数据的整个过程”中详细介绍了,本节中未重复。

  最后,我们将引入管道中异常事件的传播。异常事件(作为入站事件)在管道中散布。

  异常事件的触发因素有两种情况:一个是内部框架内部的异常。目前,Netty将直接在管道中触发异常事件的传播。异常事件将从HeadContext向后扩散到HeadContext的TailContext。

  例如,在阅读读取循环中的数据时,Netty具有异常:

  目前,Netty将直接从管道触发异常事件的传播。

  像入站事件一样,异常事件将从管道中的HeadContext向后传播。

  触发异常事件的第二种情况是,当入站事件或冲洗事件在管道中传播时,通道手中的事件恢复方法是bistral的。用户可以在此处处理异常事件,并决定是否继续通过CTX向后传播异常事件。

  例如,当我们在ChannelInboundhandler中的ChannelRead回调中处理业务请求时,处理业务请求时将触发一个例外,并且它将触发ChannelInboundHandler的异常方法。

  另一个示例:当我们处理业务结果时,在ChannelOutBoundHandler中对业务进行冲洗后调整时,它还将触发Channeloutboundboundler的异常方法。

  我们可以在ChannelHandler中的例外情况下进行异常治疗,并决定是否继续通过CTX.FireexceptionCaught(原因)向后传播异常事件。

  尽管异常事件和入站事件正在从前面到向后传播,但每个人都关注区分这两个事件的差异。

  在入站活动的传播期间,找到了一个带有事件响应的ChannelInboundHandler。当您遇到ChannelOutBoundHandler时,您将直接跳过。

  异常事件是在哪种类型的ChannelHandler中触发的,它将从当前异常的ChannelHandler向后传播。ChanneinBoundHandler听起来可能异常事件,并且Channeloutboundhandler也可以听起来异常事件。

  因为无论是在ChannelInboundhandler中还是在ChannelOutBoundHandler中生产,异常的事件都将在管道中从正面到后传播,并且不关心ChannelHandler的类型。管道结束,以便可以因入站异常和出站级异常而被捕。

  本文涉及许多内容。通过Netty异步事件在管道中安排和传播的主要线路和传播的主要线路,我们等同于重新审查先前的文章内容并再次汇总。

  在本文中,我们详细介绍了管道的组成。它主要是由ChannelHandlerContext Node组成的两条链接列表。ChannelHandlerContext包含ChannelHandler执行上下文的信息,因此ChannelHandler只能关注遵循单个原则和开放和关闭的原则,并遵守IO事件的处理。。

  此外,管道结构还包含一个任务链接列表,以存储处理已有的恢复并在ChannelHandler的执行中存储处理的回调。PiPeline还包含对归属的引用。

  我们还介绍了Netty中不同步骤的分类:入站事件,出站事件,异常事件。并介绍每个分类下所有事件的触发时间和管道中的扩展路径。

  最后,管道结构,创建和初始化的过程以及管道相关操作的源代码。

  在中间,我们还介绍了ChannelHanderContext的结构,并介绍了ChannelHandlerContext的上下文信息,以封装ChannelHandler的执行。

  本文的内容在这里。谢谢您的观看。对于下一篇文章,请参阅~~~

  原始:https://juejin.cn/post/7098279739504197645