Netty 总结


简述:

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 的V为异步结果的返回类型

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操作完成,优点是容易实现顺序逻辑

你可能感兴趣的:(Netty 总结)