简述:
Netty 是一个基于JAVA NIO 类库的异步通信框架,用于创建异步非阻塞、基于事件驱动、高性能、高可靠性和高可定制性的网络客户端和服务端。
特点:
异步、非阻塞、基于事件驱动的NIO框架
经典的ChannelFuture-listener机制,所有的异步IO操作都可以设置listener 进行监听和获取操作结果
基于ChannelPipeline-ChannelHandler 的责任链模式,可以方便的自定义业务拦截器用于业务逻辑定制
Netty原理与逻辑:
第一层:
Reactor 通信调度层,它由一系列辅助类组成,包括Reactor 线程NioEventLoop以及其父类、NioSocketChannel/NioServerSocketChannel 以及其父类、ByteBuffer 以及其衍生出来的各种Buffer、UnSafe 以及其衍生的各种内部子类等
Reactor模型 主要由多路复用器(Acceptor)、事件分发器(Dispatcher)、事件处理器(Handler)组成
1、单线程模型:所有I/O操作都由一个线程完成,即多路复用、事件分发和处理都是在一个Reactor线程上完成的。
2、多线程模型:
3、主从多线程模型:采用多个Reactor,每个Reactor都在自己单独的线程里执行。
Netty 的线程模型可以通过创建不同的EventLoopGroup 实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型
第二层:
责任链ChannelPipeline,它负责调度事件在责任链中的传播,支持动态的编排责任链,责任链可以选择性的截取自己关心的事件,对于其他IO操作和事件忽略,Handler 同时支持inbound和outbound事件。
第三层:
业务逻辑编排层:一类是纯粹的业务逻辑编排,还有一类是应用层协议插件,用于协议相关的编解码和链路管理,例如CMPP协议插件。
Zero-copy
Netty提供了CompositeByteBuf 类,它可以将多个ByteBuf 合并为一个逻辑上的ByteBuf,避免了各个ByteBuf 之间的拷贝。
ByteBuf header = ...
ByteBuf body = ...
CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
compositeByteBuf.addComponents(true, header, body);
通过wrap 操作,可以将byte[]数组、ByteBuf、ByteBuffer 等包装成一个Netty ByteBuf 对象,进而避免了拷贝操作。
byte[] bytes = ...
ByteBuf byteBuf = Unpooled.wrappedBuffer(bytes);
ByteBuf 支持slice 操作,可以将ByteBuf分解成多个共享同一个存储区域的ByteBuf,避免内存拷贝
ByteBuf byteBuf = ...
ByteBuf header = byteBuf.slice(0, 5);
ByteBuf body = byteBuf.slice(5, 10);
通过FileRegion 包装的FileChannel.tranferTo实现文件传输,可以直接将文件缓冲区的数据发送到目标Channel,避免传统通过循环write 方式导致的内存拷贝问题。
publicstaticvoidcopyFileWithFileChannel(String srcFileName, String destFileName) throwsException {
RandomAccessFile srcFile = newRandomAccessFile(srcFileName, "r");
FileChannel srcFileChannel = srcFile.getChannel();
RandomAccessFile destFile = newRandomAccessFile(destFileName, "rw");
FileChannel destFileChannel = destFile.getChannel();
longposition = 0;
longcount = srcFileChannel.size();
srcFileChannel.transferTo(position, count, destFileChannel);
}
NIO 中的Selector Bug
若Selector 的轮询结果为空,也没有wakeup或新消息处理,则发生空轮询,CPU使用率100%
Netty解决方法
1、对Selector的select操作进行周期统计,每完成一次空的select操作进行一次计数
2、若在某个周期内连续发生N次空轮询,则触发了epoll死循环bug
3、重建Selector,判断是否是其他线程发起的重建请求,若不是则将原SocketChannel从旧的Selector上去除注册,重新注册到新的Selector上,并将原来的Selector关闭。
Future:
非阻塞模型:Promise,Future和Callback
其中的Future表示一个可能还没有实际完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后做出对应的操作,而Promise交由任务执行者,任务执行者通过Promise可以标记任务完成或者失败。
特性:
1、Feture
2、getNow是无阻塞调用,返回异步执行结果,如果未完成那么返回null
3、await是阻塞调用,等到异步执行完成
4、isSuccess执行成功是否
5、sync阻塞调用,等待这个future直到isDone返回true;如果该future失败,重新抛出失败的原因。和await区别就是返回结果不同,它返回一个Future对象,通过这个Future知道任务执行结果。
6、添加GenericFutureListener,执行完成(future可能由于正常终止、异常或取消完成)后调用该监听器。
ScheduleFutureTask:该类是定时任务返回的,ChannelFuture是这个结构中最重要的类,表示通道异步执行的结果:在netty中所有的IO操作都是异步的。这意味着所有的IO调用都会立即返回,且不保证IO操作完成。
IO调用会返回一个ChannelFuture的实例,通过该实例可以查看IO操作的结果和状态,ChannelFuture有完成和未完成两种状态,当IO操作开始,就会创建一个ChannelFuture的实例,该实例初始是未完成状态,它不是成功,失败,或者取消,因为IO操作还没有完成,如果IO操作完成了那么就会有成功,失败,和取消状态。
该类提供了很多方法用来检查IO操作是否完成,等待完成,和接受IO操作的结果。还可以添加ChannelFutureListener的监听器,这样IO操作完成时就可以得到提醒
1、强烈建议使用addListener而不是await
2、addListener是非阻塞的,它简单的添加指定的ChannelFutureListener到ChannelFuture中
3、IO线程将在当绑定在这个future的IO操作完成时,触发这个触发器,优点是提高效率和资源的利用率
4、await()是一个阻塞方法,一旦调用,调用线程将会阻塞直到IO操作完成,优点是容易实现顺序逻辑