四、Netty模块组件
1、功能特性
(1)、传输服务:支持BIO和NIO。
(2)、容器集成:支持OSGI、JBossMC、Spring、Guice容器。
(3)、协议支持:HTTP、Protobuf、二进制、文本、WebSocket等一系列常见协议都支持,还支持通过实行编码解码逻辑来实现自定义协议。
(4)、Core核心:可扩展事件模型、通用通信API、支持零拷贝的ByteBuf缓冲对象。

2、Bootstrap、ServerBootstrap
(1)、概述
①、Bootstrap意思是引导,一个Netty应用通常由一个Bootstrap开始,主要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动引导类,ServerBootstrap是服务端启动引导类。
②、类Bootstrap、类ServerBootstrap均继承抽象类AbstractBootstrap。
(2)、常用方法
①、public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {...}
该方法用于服务器端,用来设置两个EventLoop,parentGroup负责处理连接请求,childGroup负责处理和客户端相关的业务。
②、public B group(EventLoopGroup group) {...}
该方法是抽象类AbstractBootstrap的方法,用于客户端,用来设置EventLoop。
③、public B channel(Class extends C> channelClass) {...}
该方法是抽象类AbstractBootstrap的方法,用来设置一个服务器端、客户端的通道实现。
④、public
该方法是抽象类AbstractBootstrap的方法,用来给ServerChannel 添加配置。
⑤、public
用来给接收到的通道添加配置。
⑥、public ServerBootstrap childHandler(ChannelHandler childHandler) {...}
该方法用来设置业务处理类(自定义的handler),对应childGroup。
⑦、public B handler(ChannelHandler handler) {...}
该方法是抽象类AbstractBootstrap的方法,用来设置服务端处理类(自定义的handler),对应parentGroup。
⑧、public ChannelFuture bind(int inetPort) {...}
该方法是抽象类AbstractBootstrap的方法,用于服务器端,用来设置占用的端口号。
⑨、public ChannelFuture connect(String inetHost, int inetPort) {...}
该方法用于客户端,用来连接服务器端。
3、ChannelFuture
接口ChannelFuture继承Netty的Future接口,Netty的Future接口继承JUC的Future接口。
(1)、概述
①、异步和同步相对,当一个异步调用发出后,调用者不能立刻得到结果。实际处理这个调用的组件在完成后,通过状态、通知和回调来通知调用者。
②、Netty中的I/O操作是异步的,包括Bind、Write、Connect等操作会首先返回一个ChannelFuture对象。
③、因为调用者并不能立刻获得结果,而是通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。
④、Netty的异步模型是建立在future和callback之上的,callback就是回调。
(2)、Future-Listener机制
①、当Future对象刚刚创建时,处于非完成状态,调用者可以通过返回的ChannelFuture来获取操作执行的状态,注册监听函数来执行完成后的操作。
②、假设一个方法fun,计算过程可能非常耗时,等待fun返回显然不合适。那么可以在调用fun的时候,立马返回一个Future,后续可以通过Future去监控方法fun的处理过程。
(3)、Future常用方法
①、Channel channel();
ChannelFuture的方法,返回当前正在进行IO操作的通道。
②、ChannelFuture sync() throws InterruptedException;
ChannelFuture的方法,阻塞等待异步操作执行完毕,重写Netty的Future接口的方法。
③、addListener方法
注册监听器,当操作已完成(isDone方法返回完成),将会通知指定的监听器;如果Future对象已完成,则通知指定的监听器。
④、removeListener方法
移除监听。
⑤、isDone方法
判断当前操作是否完成。
⑥、isSuccess方法
判断已完成的当前操作是否成功。
⑦、isCancelled方法
判断已完成的当前操作是否被取消。
⑧、getCause方法
获取已完成的当前操作失败的原因。
4、Channel
(1)、概述
①、Netty网络通信的组件,能够用于执行网络I/O操作。
②、通过Channel可获得当前网络连接的通道的状态。
③、通过Channel可获得网络连接的配置参数(例如接收缓冲区大小)。
④、Channel提供异步的网络I/O操作(如建立连接,读写,绑定端口),异步调用意味着任何I/O调用都将立即返回,并且不保证在调用结束时所请求的I/O操作已完成。调用立即返回一个ChannelFuture实例,通过注册监听器到ChannelFuture上,可以I/O操作成功、失败或取消时回调通知调用方。
⑤、支持关联I/O操作与对应的处理程序。
(2)、Channel类型
不同协议、不同的阻塞类型的连接都有不同的Channel类型与之对应,这些Channel涵盖了UDP和TCP网络IO以及文件IO。
①、NioSocketChannel:异步的客户端TCP Socket连接。
②、NioServerSocketChannel:异步的服务器端TCP Socket连接。
③、NioDatagramChannel:异步的UDP连接。
④、NioSctpChannel:异步的客户端Sctp连接。
⑤、NioSctpServerChannel:异步的Sctp服务器端连接。
5、Selector
(1)、概述
①、Netty基于Selector对象实现I/O多路复用,通过Selector,一个线程可以监听多个连接的Channel事件。
②、当向一个Selector中注册Channel后,Selector内部的机制就可以自动不断地查询(select)这些注册的Channel是否有已就绪的I/O事件(例如可读、可写、网络连接完成等),这样程序就可以很简单地使用一个线程高效地管理多个Channel。
6、NioEventLoop
(1)、概述
①、NioEventLoop中维护了一个线程和任务队列,支持异步提交执行任务,线程启动时会调用NioEventLoop的run方法,执行I/O任务和非I/O任务。两种任务的执行时间比由变量ioRatio控制,默认为50,则表示允许非IO任务执行的时间与IO任务的执行时间相等。
②、NioEventLoop表示一个不断循环执行处理任务的线程,每个NioEventLoop都有一个Selector,用于监听绑定在其上的socket网络通道。
③、NioEventLoop内部采用串行化设计,从消息的读取->解码->处理->编码->发送,始终由IO线程NioEventLoop负责。
(2)、任务类型
①、I/O任务:即selectionKey中ready的事件,如accept、connect、read、write等,由processSelectedKeys方法触发。
②、非IO任务:添加到taskQueue中的任务,如register0、bind0等任务,由runAllTasks方法触发。
(3)、TaskQueue3种典型使用场景
①、自定义的普通任务,放入taskQueue队列中
②、自定义定时任务,放入scheduledTaskQueue队列中
③、非当前reactor线程调用channel的各种方法
(4)、与其他组件的对应关系
①、NioEventLoopGroup下包含多个NioEventLoop
②、每个 NioEventLoop 中包含有一个Selector,一个taskQueue
③、每个 NioEventLoop的Selector上可以注册监听多个NioChannel
④、每个NioChannel只会绑定在唯一的NioEventLoop上
⑤、每个NioChannel都绑定有一个自己的ChannelPipeline
7、NioEventLoopGroup
(1)、概述
NioEventLoopGroup是接口EventLoopGroup的实现类。
①、EventLoopGroup是一组EventLoop的抽象,NioEventLoopGroup主要管理EventLoop的生命周期,可以理解为一个线程池,内部维护了一组线程,每个线程(NioEventLoop)维护着一个Selector实例,负责处理多个Channel上的事件,而一个Channel只对应于一个线程。
②、EventLoopGroup提供next接口,可以从组里面按照一定规则获取其中一个EventLoop来处理任务。Netty为了更好的利用多核CPU资源,一般会有多个EventLoop同时工作,在Netty服务器端编程中,我们一般都需要提供两个EventLoopGroup,例如:BossEventLoopGroup和WorkerEventLoopGroup。
③、通常一个服务端口即一个ServerSocketChannel对应一个Selector和一个EventLoop线程。BossEventLoop负责接收客户端的连接并将SocketChannel交给WorkerEventLoopGroup来进行IO处理。
(2)、过程
①、BossEventLoopGroup通常是一个单线程的EventLoop,EventLoop维护着一个注册了ServerSocketChannel的Selector实例,BossEventLoopGroup不断轮询Selector将连接事件分离出来。
②、通常是OP_ACCEPT事件,然后将接收到的SocketChannel交给WorkerEventLoopGroup
③、WorkerEventLoopGroup会由next选择其中一个EventLoop来将这个SocketChannel住蹙到其维护的Selector并对其后续的IO事件进行处理。
(3)、常用方法
①、public NioEventLoopGroup()
构造方法
②、public Future> shutdownGracefully() {...}
断开连接,关闭线程,AbstractEventExecutorGroup中实现了EventExecutorGroup的方法。
(3)、
8、ChannelHandler
ChannelHandler是一个接口,处理I/O事件或拦截I/O操作,并将其转发到其ChannelPipeline(业务处理链)中的下一个处理程序。
(1)、子类
ChannelHandler本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类或者使用适配器类。
①、ChannelInboundHandler:用于处理入站I/O事件。
②、ChannelOutboundHandler:用于处理出站I/O操作。
(2)、适配器
ChannelHandler本身并没有提供很多方法,因为这个接口有许多的方法需要实现,方便使用期间,可以继承它的子类或者使用适配器类。
①、ChannelInboundHandlerAdapter:用于处理入站I/O事件。
②、ChannelOutboundHandlerAdapter:用于处理出站I/O操作。
③、ChannelDuplexHandler:用于处理入站和出站事件。
(3)、ChannelInboundHandlerAdapter常用方法
①、public void channelRegistered(ChannelHandlerContext ctx) throws Exception {...}
通道注册事件。
②、public void channelActive(ChannelHandlerContext ctx) throws Exception {...}
通道就绪事件。
③、public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {...}
通道读取数据事件。
④、public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {...}
数据读取完毕事件。
⑤ 、public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {...}
通道发生异常事件。
⑥、public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {...}
监听事件,并做相应处理。例:IdleStateEvent
(4)、ChannelHandler类型
①、IdleStateHandler
是Netty空闲处理状态的处理器,可实现心跳检测机制。当IdleStateEvent触发后,就会传递给管道的下一个handler去处理,会调用下一个handler的userEventTiggered方法, 在该方法中去处理IdleStateEvent(读空闲,写空闲,读写空闲)。
a、readerIdleTime : 表示多长时间没有读, 就会发送一个心跳检测包检测是否连接。
b、writerIdleTime : 表示多长时间没有写, 就会发送一个心跳检测包检测是否连接。
c、allIdleTime : 表示多长时间没有读写, 就会发送一个心跳检测包检测是否连接。
9、ChannelHandlerContext
保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。
(1)、概述
①、保存Channel相关的所有上下文信息,同时关联一个ChannelHandler对象。
②、ChannelHandlerContext中包含一个具体的事件处理器ChannelHandler,同时ChannelHandlerContext中也绑定了对应的pipeline和Channel的信息,方便对ChannelHandler进行调用。

(2)、ChannelHandlerContext常用方法
ChannelHandlerContext接口继承ChannelInboundInvoker接口和ChannelOutboundInvoker接口,默认实现类DefaultChannelHandlerContext。
①、ChannelFuture close();
关闭通道。
②、ChannelOutboundInvoker flush();
刷新通道。
③、ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
将数据写到ChannelPipeline中,当前ChannelHandler的下一个ChannelHandler开始处理(出站)。
10、ChannelPipline
(1)、概述
①、ChannelPipeline是一个 Handler的集合,它负责处理和拦截inbound或者outbound的事件和操作,相当于一个贯穿Netty的链。(也可以这样理解:ChannelPipeline是保存ChannelHandler的集合,用于处理或拦截Channel的入站事件和出站操作)。
②、ChannelPipeline实现了一种高级形式的拦截过滤器模式,使用户可以完全控制事件的处理方式,以及Channel中各个的ChannelHandler如何交互。
(2)、Channel和ChannelPipeline对应关系
①、在Netty中每个Channel都有且仅有一个ChannelPipeline与之对应。
②、一个Channel包含了一个ChannelPipeline,而ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表,并且每个ChannelHandlerContext中又关联着一个ChannelHandler。
③、入站事件和出站事件在一个双向链表中,入站事件会从链表head往后传递到最后一个入站的handler,出站事件会从链表tail往前传递到最前一个出站的handler,两种类型的handler互不干扰。

④、I/O事件由ChannelInboundHandler或ChannelOutboundHandler处理,并通过调用ChannelHandlerContext中定义的事件传播方法(例如ChannelHandlerContext.fireChannelRead(Object)和ChannelOutboundInvoker.write(Object))转发到其最近的处理程序。
a、入站事件由自下而上方向通过入站处理程序处理,如图左侧所示。入站Handler处理程序通常处理由图底部的I/O线程生成的入站数据,通常通过实际输入操作(例如SocketChannel.read(ByteBuffer))从远程读取入站数据。
b、出站事件由自上而下方向处理,如图右侧所示。 出站Handler处理程序通常会生成或转换出站传输,例如write请求,I/O线程通常执行实际的输出操作,例如SocketChannel.write(ByteBuffer)。

(3)、常用方法
ChannelPipeline接口继承ChannelInboundInvoker接口和ChannelOutboundInvoker接口,默认实现类DefaultChannelPipeline。
①、ChannelPipeline addFirst(String name, ChannelHandler handler);
把一个业务处理类(handler)添加到双向链的第一个位置。
②、ChannelPipeline addLast(String name, ChannelHandler handler);
把一个业务处理类(handler)添加到双向链表中的最后一个位置。
11、ChannelOption
(1)、概述
Netty在创建Channel实例后,一般都需要设置ChannelOption参数。
(2)、参数
①、ChannelOption.SO_BACKLOG
对应TCP/IP协议listen函数中的backlog参数,用来初始化服务器可连接队列大小。服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理,backlog参数指定了队列的大小。
②、ChannelOption.SO_KEEPALIVE
一直保持连接活动状态。
12、Unpooled类
(1)、概述
Netty提供一个专门用来操作缓冲区(即 Netty 的数据容器ByteBuf)的工具类
(2)、常用方法
①、public static ByteBuf copiedBuffer(byte[] array) {...}
通过给定的数据和字符编码返回一个Netty的ByteBuf对象。
13、ByteBuf类
(1)、JAVA NIO ByteBuffer缺点
JAVA NIO提供了ByteBuffer等七种容器来提升传输时的效率,但是在使用时比较复杂,并且要进行读写切换。
①、ByteBuffer长度固定,一旦分配完成,它的容量不能动态扩展和收缩。
②、ByteBuffer只有一个标识位置的指针position,读写的时候需要手工调用flip()和rewind()等。
③、ByteBuffer的API功能有限,一些高级和实用的特性它不支持,需要使用者自己编程实现。
(2)、特点
①、读和写使用不同的索引readerIndex和writerIndex。
②、读和写可以随意的切换,不需要调用flip()方法。
③、容量能够被动态扩展和StringBuilder一样。
④、用其内置的复合缓冲区可实现透明的零拷贝。
⑤、支持方法链。
⑥、支持引用计数。count == 0,release。
⑦、支持池。
(3)、读写操作
①、ByteBuf通过两个位置指针来协助缓冲区的读写操作,分别是读操作(使用readerIndex)、写操作(使用writerIndex)。
②、readerIndex和writerIndex的取值一开始都是0,随着数据的写入writerIndex会增加,读取数据会使readerIndex增加,但是它不会超过writerIndex。
注:buffer.readByte()方法会使readerIndex增加,而buffer.getByte(i)方法不会。
③、在读取之后,0~readerIndex的就被视为discard的,调discardReadBytes方法,可以释放这部分空间,它的作用类似ByteBuffer的compact方法。
④、readerIndex和writerIndex之间的数据是可读取的,等价于ByteBuffer position和limit之间的数据。
⑤、writerIndex和capacity之间的空间是可写的,等价于ByteBuffer limit和capacity之间的可用空间。
⑥、由于写操作不修改readerIndex指针,读操作不修改writerIndex指针,因此读写之间不再需要调整位置指针,这极大地简化了缓冲区的读写操作,避免了由于遗漏或者不熟悉flip()操作导致的功能异常。
- +—————————————————————————————————————————————————————+
- | discardable bytes | readable bytes | writable bytes |
- | | (CONTENT) | |
- +—————————————————————————————————————————————————————+
- | | | |
- 0 <= readerIndex <= writerIndex <= capacity
参考资料记录:Netty基础概念_L_D_Y_K的博客-CSDN博客