netty ChannelPipeline的事件传输机制(入站和出站的方法)


原博客,点击这里

一、Netty的事件类型

从ChannelPipeline的传输的事件类型角度,Netty的事件可以分为Inbound和Outbound事件。

Inbound事件是一个通知事件,当某件事已经发生了,然后通过Inbound事件进行通知,Inbound通常发生在Channel的状态的改变或IO事件就绪

Outbound事件都是请求事件, 请求某件事情的发生, 然后通过Outbound事件进行通知。

相关的一些方法:
channelActive():当通道就绪就会触发该方法
channelRead0 :读取客户端数据

1、入站事件传播方法

1.1、ChannelHandlerContext#fireChannelRegistered()

作用:触发事件告知Inbound ChannelHandler:ChannelHandlerContext的Channel注册于其EventLoop,调用ChannelInboundHandler的channelRegistered

1.2、ChannelHandlerContext#fireChannelActive()

作用:触发事件告知Inbound ChannelHandler:ChannelHandlerContext的Channel现在处于活动状态,调用ChannelInboundHandler的channelActive…

socket通道建立时被触发

通道激活时触发,当客户端connect成功后,服务端就会接收到这个事件,从而可以把客户端的Channel记录下来,供后面复用

- 服务端: 当服务器端与客户端进行建立连接的时候会触发,如果没有触发读写操作,则客户端和客户端之间不会进行数据通信,也就是channelRead0不会执行
- 客户端:当通道连接的时候,触发channelActive方法向服务端发送数据触发服务器端的handler的channelRead0回调,然后服务端向客户端发送数据触发客户端的channelRead0,依次触发。

1.3、ChannelHandlerContext#fireChannelRead(Object)

作用:触发事件告知Inbound ChannelHandler:当前Channel正在从对等方读取消息,调用ChannelInboundHandler的channelRead。

1.4、ChannelHandlerContext#fireChannelReadComplete()

作用:触发事件告知Inbound ChannelHandler:当前读操作读取的最后一条消息被channelRead(ChannelHandlerContext, Object)}使用,调用ChannelInboundHandler的channelReadComplete。

1.5、ChannelHandlerContext#fireExceptionCaught(Throwable)

作用:触发事件告知Inbound ChannelHandler:抛出Throwable异常,调用ChannelInboundHandler的exceptionCaught。

1.6、ChannelHandlerContext#fireUserEventTriggered(Object)

作用:触发事件告知Inbound ChannelHandler:用户事件正在触发,调用ChannelInboundHandler的userEventTriggered。

1.7、ChannelHandlerContext#fireChannelWritabilityChanged()

作用:触发事件告知Inbound ChannelHandler:Channel的可写状态发生更改,调用ChannelInboundHandler的channelWritabilityChanged。

1.8、ChannelHandlerContext#fireChannelInactive()

作用:触发事件告知Inbound ChannelHandler:ChannelHandlerContext的Channel现在是不活动的,并已达到其生命周期的结束,调用ChannelInboundHandler的channelInactive

1.9、ChannelHandlerContext#fireChannelUnregistered()

作用:触发事件告知Inbound ChannelHandler:ChannelHandlerContext的Channel从其EventLoop注销,调用ChannelInboundHandler的channelUnregistered

2、出站事件传播方法:

2.1、ChannelHandlerContext#bind(SocketAddress, ChannelPromise)

作用:请求绑定到给定的SocketAddress,并在操作完成后通知ChannelFuture,原因可能是操作成功,也可能是错误,调用ChannelOutboundHandler的bind

2.2、ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)

作用:请求连接到给定的SocketAddress,并在操作完成后通知ChannelFuture,原因可能是操作成功,也可能是错误,调用ChannelOutboundHandler的connect

2.3、ChannelHandlerContext#write(Object, ChannelPromise)

作用:请求通过这个ChannelHandlerContext通过ChannelPipeline写入消息。此方法不会请求实际刷新,因此请确保在希望请求将所有挂起数据刷新到实际传输时调用flush()。

2.4、ChannelHandlerContext#flush()

作用:请求通过此ChannelOutboundInvoker刷新所有挂起的消息。

2.5、ChannelHandlerContext#read()

作用:请求从Channel读取数据到第一个入站缓冲区,如果读取数据,则触发ChannelInboundHandler#channelRead(ChannelHandlerContext, Object)事件,并触发ChannelInboundHandler#channelReadComplete(ChannelHandlerContext) channelReadComplete事件,以便处理程序可以决定继续读取。如果已经有一个挂起的读操作,这个方法什么也不做。

2.6、ChannelHandlerContext#disconnect(ChannelPromise)

作用:请求断开与远程对等点的连接,并在操作完成后通知ChannelFuture,原因可能是操作成功,也可能是错误,调用ChannelOutboundHandler的disconnect

2.7、ChannelHandlerContext#close(ChannelPromise)

作用:请求关闭Channel,并在操作完成后通知ChannelFuture,原因可能是操作成功,也可能是错误,调用ChannelOutboundHandler的close。关闭后,就不可能再重用它了。

2.8、ChannelHandlerContext#deregister(ChannelPromise)

作用:请求从之前分配的EventExecutor取消注册,并在操作完成后通知ChannelFuture,原因可能是操作成功,也可能是错误,调用ChannelOutboundHandler的deregister。

3、下图描述了在ChannelPipeline中ChannelHandlers如何处理I/O事件

这个图是源码io.netty.channel.ChannelPipeline接口的备注部分提供的。
在这里插入图片描述

二、ChannelPipeline的结构

ChannelPipeline的数据结构是双向链表。
在这里插入图片描述

三、HeadContext的作用

Outbound事件最后都是通过HeadContext完成(HeadContext最后调用NioSocketChannelUnsafe响应的方法完成),Inbound事件是从HeadContext开始。

四、TailContext的作用

一个处理字节和消息的特殊的捕获全部事件的处理程序,Outbound事件是从tailContext开始,Inbound事件是在tailContext结束。

五、Inbound事件流

在Channel中调用DefaultChannelPipeline.fireChannelActive,接下来在Channel交给自己的DefaultChannelPipeline执行了,因为是Inbound事件,所以从HeadContext->TailContext。DefaultChannelPipeline中Inbound事件传递过程:

Context.fireChannelActive -> Connect.findContextInbound -> Connect.invokeChannelActive(final AbstractChannelHandlerContext next)-> next.invokeChannelActive -> nextHandler.channelActive -> nextContext.fireChannelActive

六、Outbound 事件流

6.1、Outbound事件流过程

以 Bootstrap.connect 的事件流为例,大致如下:

Bootstrap.connect -> Bootstrap.doResolveAndConnect-> Bootstrap.doResolveAndConnect0-> Bootstrap.doConnect-> AbstractChannel.connect->DefaultChannelPipeline.connect->NioSocketChannelUnsafe.connect->NioSocketChannel.doConnect->SocketChannel.connect

最后都是到了Channel,然后Channel交给自己的DefaultChannelPipeline执行,因为是 Outbound 事件,所以从TailContext ->HeadContext,HeadContext最后调用NioSocketChannelUnsafe响应的方法完成,事件流的NioSocketChannelUnsafe.connect->NioSocketChannel.doConnect->SocketChannel.connect部分就是在HeadContext里面执行的。

6.2、DefaultChannelPipeline中事件传递过程

Context.connect -> Context.findContextOutbound -> next.invokeConnect -> handler.connect -> Context.connect

一致循环从TailContext 将事件传递到HeadContext。

补充:

参考文章,点击这里

@ Sharable

netty使用一个成员变量added标识一个channel是否已经添加,如果当前要添加的Handler是非共享的,并且已经添加过,那就抛出异常,否则,标识该handler已经添加。
由此可见,一个Handler如果是sharable的,就可以无限次被添加到pipeline中,我们客户端代码如果要让一个Handler被共用,只需要加一个@Sharable标注即可,如下

@Sharable
public class BusinessHandler {
    
}

而如果Handler是sharable的,一般就通过spring的注入的方式使用,不需要每次都new 一个

isSharable() 方法正是通过该Handler对应的类是否标注@Sharable来实现的

FastThreadLocal

在Netty中使用一个 FastThreadLocal变量来缓存Handler的类和默认名称的映射关系,在生成name的时候,首先查看缓存中有没有生成过默认name(简单类名#0),如果没有生成,就调用generateName0()生成默认name,然后加入缓存

创建节点

netty创建默认name的规则为 简单类名#0

Channel的创建时机

以新连接创建为例,新连接创建的过程中创建channel,而在创建channel的过程中创建了该channel对应的pipeline,创建完pipeline之后,自动给该pipeline添加了两个节点,即ChannelHandlerContext,ChannelHandlerContext中有用pipeline和channel所有的上下文信息。

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页