Netty笔记6:Netty组件
Netty笔记1:线程模型
Netty笔记2:零拷贝
Netty笔记3:NIO编程
Netty笔记4:Epoll
Netty笔记5:Netty开发实例
Netty笔记6:Netty组件
Netty笔记7:ChannelPromise通知处理
Netty笔记8:ByteBuf使用介绍
Netty笔记9:粘包半包
Netty笔记10:LengthFieldBasedFrameDecoder
Netty笔记11:编解码器
Netty笔记12:模拟Web服务器
Netty笔记13:序列化
文章目录
- EventLoop
- ChannelHandler生命周期
- ChannelPiple与ChannelHandler
- ChannelHandlerContext
- ChannelHandler
- ChannelInboundHandler
- ChannelOutboundHandler
- HandlerAdapter
- 共享Handler
Bootstrap
:是Netty框架的启动类和主入口类,分为客户端(Bootstrap)和服务器端(ServerBootstrap)两种。
EventLoop
:可以看成一个线程,我们的那些操作都是在这个上面执行;
EventLoopGroup
:线程组,管理EventLoop
的;
NioSocketChannel/NioServerSocketChannel
:channel
是NIO中的基本构造,所以在Netty
中,自然也有;
ChannelHandler
:事件用于通知Netty
改变状态,触发对应处理器,如连接激活、读取、错误等这些事件,都会分给ChannelHander
处理,我们只需要实现对应的方法,就行;Netty
也定义了一些预定义好的实现,包括各种协议(如Http,SSL/TLS)的ChannelHandler
;
ChannelPiple
:每个Channel
都有自己的ChannelPiple
,作为ChannelHandler
的容器,从网络到业务处理,再到网络,也就是从入站到出站,业务处理都是在piple
中流转,Handler
执行的顺序也是他们被添加的顺序。
ChannelFuture
:Netty
中的所有操作都是异步的,它实现于JDK中的Future
;
EventLoop
在当前的线程模型中,它可能有多个Channel
共享,这使得可以通过尽可能少的Thread
支撑大量的Channel
;
EventLoopGroup
负责每个新创建的Channel
分配一个EventLoop
,一旦Channel
被分配给了EventLoop
,那么他们关系会延续到这个Channel
关闭销毁;
需要注意的一点:因为多个Channel
共享一个EventLoop
,也就是一个线程,那么ThreadLocal
遍历也是共享。
ChannelHandler生命周期
ChannelHandler
被添加到ChannelPiple
中或者被从ChannelPiple
中移除会调用下面的方法,并且都会接受一个ChannelHandlerContext
的参数:
handlerAdded
:channelHandler添加时被调用;handlerRemoved
:被移除时调用;exceptionCaught
:产生错误时调用;
ChannelPiple与ChannelHandler
ChannelPiple
其实是以双向链表的形式进行维护和管理。
之前提到过出站入站,就是数据从网络到达piple
,为入站,数据从piple
出去到网络,为出站,出入站均有对应的事件handler
处理,并且,如果业务没有特殊要求,可以不用区分出入站的handler
。
ChannelHandlerContext
在ChannelHandler
和ChannelPiple
传递的上下文对象,更像是链表中的node
对象,它对数据进行了保证,并且包含pre
和next
传递Handler
。
当添加ChannelHandle
时,ChannelPiple
都会创建一个ChannelHandlerContext
,而handler
呈链表形式,并不是handler
与handler
间是链表结构,它本身是处理器,不应有链表的形式,所以,就有我们的ChannelHandlerContext
来管理链表,它具有pre
和next
,所以只要通过context
就可以找到下一个handler
。
ChannelHandler
我们的业务就写在这ChannelHandler
下;
它有两个重要的子接口:
- ChannelInboundHandler:处理入站数据以及各种状态变化,从
handler
列表头部开始执行,addLast
的顺序 - ChanneloutboundHandler:处理出站数据并且允许拦截所有的操作,从
handler
列表尾部开始执行,addLast
的逆序;
注意:他们的执行顺序,他们通过addLast
添加的方式一样,但是执行顺序是相反的。
ChannelInboundHandler
// 当channel已经注册到EventLoop,并能够处理IO时调用
void channelRegistered(ChannelHandlerContext var1) throws Exception;
// 当Channel从它的EventLoop注销并且无法处理任何IO时被调用
void channelUnregistered(ChannelHandlerContext var1) throws Exception;
// 当channel处于活动状态时被调用;channel已经连接/绑定并且已经就行;
void channelActive(ChannelHandlerContext var1) throws Exception;
// 当channel离开活动状态并且不再连接它的原出处节点时被调用
void channelInactive(ChannelHandlerContext var1) throws Exception;
// 当channel读取数据时调用
void channelRead(ChannelHandlerContext var1, Object var2) throws Exception;
// 当channel的读操作完成时调用
void channelReadComplete(ChannelHandlerContext var1) throws Exception;
// 当方法fireUserEvnetTriggered()被调用时调用
void userEventTriggered(ChannelHandlerContext var1, Object var2) throws Exception;
// 当channel可写状态发生改变时被调用,可通过调用channel的isWriteable()方法检测channel是否可写
void channelWritabilityChanged(ChannelHandlerContext var1) throws Exception;
// 当出现异常时调用
void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception;
ChannelOutboundHandler
// 当请求将channel绑定到本地地址时被调用
void bind(ChannelHandlerContext var1, SocketAddress var2, ChannelPromise var3) throws Exception;
// 当将channel连接到远程节点时被调用
void connect(ChannelHandlerContext var1, SocketAddress var2, SocketAddress var3, ChannelPromise var4) throws Exception;
// 当将channel从远程节点断开时被调用
void disconnect(ChannelHandlerContext var1, ChannelPromise var2) throws Exception;
// 当请求关闭channel时被调用
void close(ChannelHandlerContext var1, ChannelPromise var2) throws Exception;
// 当请求将channel从EventLoop注销时被调用
void deregister(ChannelHandlerContext var1, ChannelPromise var2) throws Exception;
// 当从channel读取数据时被调用
// 注意,他是发出读数据的请求,不是读数据
void read(ChannelHandlerContext var1) throws Exception;
// 当请求通过channel写数据到远程节点时被调用
void write(ChannelHandlerContext var1, Object var2, ChannelPromise var3) throws Exception;
// 当请求通过channel将入队数据冲刷到远程节点时被调用
void flush(ChannelHandlerContext var1) throws Exception;
为什么OutHandler
会有read
方法,出站需要读取数据吗?
这里它并不是读取数据,而是读取数据的请求操作,Netty会将其打包成一个事件,有了事件在模式循环下被识别到,进而触发inboundHnadler
,属于出站事件。(可以参考Reactor模型理解)
HandlerAdapter
对应出站入站,有ChannelOutboundHandlerAdapter
和ChannelInboundHandlerAdapter
,是Netty为我们实现的抽象类,我们可以继承它扩展我们自己的业务;
共享Handler
如果需要将handler
置为全局的,也就是共享的,那么需要加上注解:@ChannelHandler.Sharable
。
这里要注意的是注释说明:
Indicates that the same instance of the annotated ChannelHandler can be added to one or more ChannelPipelines multiple times without a race condition.
If this annotation is not specified, you have to create a new handler instance every time you add it to a pipeline because it has unshared state such as member variables.
This annotation is provided for documentation purpose, just like the JCIP annotations .
表明同一个被注解的ChannelHandler实例可以无竞态条件地被多次添加到一个或多个ChannelPipeline中。
如果未指定此注解,则每次将其添加到管道中时都必须创建一个新的处理器实例,因为它具有未共享的状态,例如成员变量。
此注解仅用于文档目的,就像JCIP注解一样。
通过他的注释可以知道,添加这个共享注解,是可以在线程无竞争的条件下被多次使用,也就是需要保证线程安全,这是因为,如果被该注解标注的ChannelHandler中存在非线程安全的变量被使用,那么多个线程并发时,就无法保证该Channel能够正常的输出结果。
然后就是没有Sharable
注解的话,每次添加都是一个新的实例,不会有问题。