一、Netty介绍
1、Netty简介
Netty是由JBOSS提供的一个java开源框架,现为 Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
“快速”和“简单”并不用产生维护性或性能上的问题。Netty 是一个吸收了多种协议(包括FTP、SMTP、HTTP等各种二进制文本协议)的实现经验,并经过相当精心设计的项目。最终,Netty 成功的找到了一种方式,在保证易于开发的同时还保证了其应用的性能,稳定性和伸缩性。
2、Netty优势
1、API使用简单,开发门槛低;
2、功能强大,预置了多种编解码功能,支持多种主流协议;
3、定制能力强,可以通过ChannelHandler对通信框架进行灵活地扩展;
4、性能高,通过与其他业界主流的NIO框架对比,Netty的综合性能最优;
5、成熟、稳定,Netty修复了已经发现的所有JDK NIO BUG,业务开发人员不需要再为NIO的BUG而烦恼;
6、社区活跃,版本迭代周期短,发现的BUG可以被及时修复,同时,更多的新功能会加入;
7、经历了大规模的商业应用考验,质量得到验证。
3、为什么Netty使用NIO而不是AIO?
Netty不看重Windows上的使用,在Linux系统上,AIO的底层实现仍使用EPOLL,没有很好实现AIO,因此在性能上没有明显的优势,而且被JDK封装了一层不容易深度优化。
AIO还有个缺点是接收数据需要预先分配缓存, 而不是NIO那种需要接收时才需要分配缓存, 所以对连接数量非常大但流量小的情况, 内存浪费很多。
而且Linux上AIO不够成熟,处理回调结果速度跟不上处理需求。
二、Netty程序组成
1、Bootstrap、EventLoop(Group)、Channel
Bootstrap是Netty框架的启动类和主入口类,分为客户端类Bootstrap和服务器类ServerBootstrap两种。
Channel是java NIO 的一个基本构造。它代表一个到实体(如一个硬件设备、一个文件、一个网络套接字或者一个能够执行一个或者多个不同的I/O操作的程序组件)的开放连接,如读操作和写操作。可以把Channel看作是传入(入站)或者传出(出站)数据的载体。因此,他可以被打开或者被关闭,连接或者断开连接。
EventLoop暂时可以看成一个线程、EventLoopGroup自然可以看成线程组。
2、事件和ChannelHandler、ChannelPipeline
Netty使用不同的事件来通知我们状态的改变或者是操作的状态。这使得我们能够基于已经发生的事件来触发适当的动作。
Netty事件是按照它们的入站或出站数据流的相关性分类的。
可能由入站数据或者相关的状态更改而触发的事件包括:连接已被激活或者连接失活;数据读取;用户事件;错误事件;
出站事件是未来将会触发的某个动作的操作结果,动作包括:打开或者关闭远程节点的连接;将数据写到或者冲刷到套接字。
每个事件都可以被分发给ChannelHandler类中的某个用户实现的方法,既然事件分为入站和出站,用来处理事件的ChannelHandler也被分为可以处理入站事件的Handler和出站事件的Handler,当然有些Handler既可以处理入站也可以处理出站。
Netty提供了大量预定义的可以开箱即用的ChannelHandler实现,包括用于各种协会(如HTTP和SSL/TLS)的CHannelHandler。
基于Netty的网络应用程序中根据业务需求会使用Netty已经提供的ChannelHandler或自行开发的ChannelHandler,这些ChannelHandler都放在ChannelPipeline中统一管理,事件就会在ChannelPipeline中流动,并被其中一个或者多个ChannelHandler处理。
3、ChannelFuture
Netty中所有的I/O操作都是异步的,我们知道“异步的意思就是不需要主动等待结果的返回,而是通过其他手段,比如:状态通知、回调函数等”,那就是说至少我们需要一种获得异步执行结果的方式。
JDK预置了interface java.util.concurrent.Future,Future 提供了一种在操作完成通知应用程序的方式。这个对象可以看做是一个异步操作的结果占位符,他将在未来的某个时刻完成,或者一直阻塞直到它完成,并提供对结果的访问,但是只允许手动检查对应的操作是否已经完成,或者一直阻塞直到他完成。这非常繁琐,所以Netty提供了他自己的实现ChannelFuture,用于执行异步操作的时候使用。
一般来说,每个Netty的出站I/O操作都将返回一个ChannelFuture。
三、Netty组件详解
EventLoop和EventLoopGroup
在一个while循环中select出事件,然后依次处理每种事件。我们可以把它称为事件循环,这就是EventLoop。interface io.netty.channel. EventLoop 定义了Netty 的核心抽象,用于处理网络连接的生命周期中所发生的事件。
io.netty.util.concurrent 包构建在JDK 的java.util.concurrent 包上。而io.netty.channel 包中的类,为了与Channel 的事件进行交互,扩展了这些接口/类。一个EventLoop 将由一个永远都不会改变的Thread 驱动,同时任务(Runnable 或者Callable)可以直接提交给EventLoop 实现,以立即执行或者调度执行。
线程的分配
服务于Channel的I/O和事件的EventLoop包含在ExentLoopGroup中。
异步传输实现只使用了少量的EventLoop(以及和它们相关联的Thread),而且在当前的线程模型中,它们可能会被多个Channel 所共享。这使得可以通过尽可能少量的Thread 来支撑大量的Channel,而不是每个Channel 分配一个Thread。EventLoopGroup 负责为每个新创建的Channel 分配一个EventLoop。在当前实现中,使用顺序循环(round-robin)的方式进行分配以获取一个均衡的分布,并且相同的EventLoop可能会被分配给多个Channel。
一旦一个Channel 被分配给一个EventLoop,它将在它的整个生命周期中都使用这个EventLoop(以及相关联的Thread)。
需要注意,EventLoop 的分配方式对ThreadLocal 的使用的影响。因为一个EventLoop 通常会被用于支撑多个Channel,所以对于所有相关联的Channel 来说,ThreadLocal 都将是一样的。这使得它对于实现状态追踪等功能来说是个糟糕的选择。然而,在一些无状态的上下文中,它仍然可以被用于在多个Channel 之间共享一些重度的或者代价昂贵的对象,甚至是事件。
线程管理
在内部,当提交任务到如果(当前)调用线程正是支撑EventLoop 的线程,那么所提交的代码块将会被(直接)执行。否则,EventLoop 将调度该任务以便稍后执行,并将它放入到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的那些任务/事件。