Netty是什么
Netty 是由 JBOSS 提供的一个Java开源框架。Netty提供异步的、基于事件驱动的网络应用程序框架,用以疾速开发高性能、高可靠性的网络 IO 程序,是目前最风行的NIO框架,Netty在互联网畛域、大数据分布式计算畛域、游戏行业、通信行业等取得了宽泛的利用,出名的Elasticsearch 、Dubbo框架外部都采纳了 Netty。
为什么应用Netty
JavaNIO 的缺点
NIO的类库和API繁冗,应用麻烦,你须要熟练掌握Selector、 ByteBuffer ServerSocketChannel、SocketChannel 等。
须要具备其它的额定技能做铺垫,例如相熟Java多线程编程,因为NIO编程波及到Reactor模式,你必须对多线程和网络编程十分相熟,能力编写出高质量的NIO程序。
可靠性能力补齐,开发工作量和难度都十分大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异样码流的解决等等,NIO编程的特点是性能开发绝对容易,然而可靠性能力补齐工作量和难度都十分大。
JDK NIO的BUG,例如epoll bug,它会导致Selector空轮询,最终导致CPU 100%。官网宣称在JDK1.6版本的update18修复了该问题,然而直到JDK1.7版本该问题仍旧存在,只不过该bug产生概率升高了一些而已,它并没有被基本解决。
Netty的长处从以下几个方面形容
应用简略:封装了NIO的很多细节,应用更简略;
功能强大:预置了多种编解码性能,反对多种支流协定;
定制能力强:能够通过ChannelHandler对通信框架进行灵便的扩大;
性能高:通过与其余业界支流的NIO框架比照,Netty的综合性能最优;
稳固:Netty修复了曾经发现的NIO的bug,让开发人员能够专一于业务自身;
社区沉闷:Netty是沉闷的开源我的项目,版本迭代周期短,bug修复速度快。
Netty 的线程模型
Netty采纳的线程模型是Reactor线程模型。Reactor模型,是指通过一个或多个输出同时传递给服务处理器的服务申请的事件驱动解决模式。服务端程序处理传入多路申请,并将它们同步分派给申请对应的解决线程。
Reactor模式也叫Dispatcher模式,即I/O多了复用对立监听事件,收到事件后散发(Dispatch)给某过程,是编写高性能网络服务器的必备技术之一。
Reactor模型中有2个要害组件
Reactor在一个独自的线程中运行,负责监听和散发事件,分发给适当的处理程序来对IO事件做出反馈。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人。
Handlers处理程序执行I/O事件要实现的理论事件,相似于客户想要与之交谈的公司中的理论官员。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作。
Netty的Reactor的具体实现:
Netty通过Reactor模型基于多路复用器接管并解决用户申请,外部实现了两个线程池,boss线程池和work线程池,其中boss线程池的线程负责解决申请的accept事件,当接管到accept事件的申请时,把对应的socket封装到一个NioSocketChannel 中,并交给work线程池,其中work线程池负责申请的read和write事件,由对应Handler解决。其中MainReactor负责客户端的连贯申请,并将申请转交给SubReactor。SubReactor负责相应通道的IO读写申请。非IO申请(具体逻辑解决)的工作则会间接写入队列,期待worker threads进行解决。
Netty的组件
Bootstrap、ServerBootstrap:Bootstrap意思是疏导,一个Netty利用通常由一个Bootstrap开始,次要作用是配置整个Netty程序,串联各个组件,Netty中Bootstrap类是客户端程序的启动疏导类,ServerBootstrap是服务端启动疏导类。Future、ChannelFuture:在Netty中所有的IO操作都是异步的,不能立即得悉音讯是否被正确处理,然而能够过一会等它执行实现或者间接注册一个监听,具体的实现就是通过Future和ChannelFutures,他们能够注册一个监听,当操作执行胜利或失败时监听会主动触发注册的监听事件。Selector :Netty基于Selector对象实现I/O多路复用,通过 Selector,一个线程能够监听多个连贯的Channel事件,当向一个Selector中注册Channel 后,Selector 外部的机制就能够主动一直地查问(select) 这些注册的Channel是否有已就绪的I/O事件(例如可读,可写,网络连接实现等),这样程序就能够很简略地应用一个线程高效地治理多个 Channel 。
ChannelHandler:是一个接口,解决I/O事件或拦挡I/O操作,并将其转发到其ChannelPipeline中的下一个处理程序。ChannelHandler自身并没有提供很多办法,因为这个接口有许多的办法须要实现,方便使用期间能够继承它的子类,如下:
ChannelInboundHandler用于解决入站I/O事件;
ChannelOutboundHandler用于解决出站I/O操作。
或者应用以下适配器类:
ChannelInboundHandlerAdapter用于解决入站I/O事件;
ChannelOutboundHandlerAdapter用于解决出站I/O操作。
ChannelHandlerContext:保留Channel相干的所有上下文信息,同时关联一个ChannelHandler对象;ChannelPipline:保留ChannelHandler的List,用于解决或拦挡Channel的入站事件和出站操作。ChannelPipeline实现了一种高级模式的拦挡过滤器模式,使用户能够齐全管制事件的解决形式,以及Channel中各个的ChannelHandler如何互相交互。
read操作从head往tail,write操作从tail往head
**ByteBuf组件
JavaNIO提供了缓存容器(ByteBuffer),然而应用简单。因而Netty引入缓存ButeBuf,一串字节数组形成。ByteBuf的个性:
池化 – 能够重用池中ByteBuf实例,更节约内存,缩小内存溢出的可能;
读写指针拆散,不须要像 ByteBuffer 一样切换读写模式;
能够主动扩容;
反对链式调用,应用更晦涩;
很多中央体现零拷贝,例如 slice、duplicate、CompositeByteBuf。
ByteBuf提供了两个指针变量来反对程序读取和写入操作—-读取操作的readerIndex和写入操作的writerIndex。
下图显示了如何通过两个指针将缓冲区分为三个区域:
| 可抛弃字节 | 可读字节 | 可写入字节 | (内容) |
0 <= readerIndex <= writerIndex <= 容量可读字节 (理论内容)通过调用discardReadBytes()来抛弃读取的字节以回收未应用的区域。能够应用mark和reset重置readIndex索引和writerIndex索引,来反复读取目标。扩容规定:如果写入后数据大小未超过 512,则抉择下一个 16 的整数倍,例如写入后大小为 12 ,则扩容后 capacity 是 16;如果写入后数据大小超过 512,则抉择下一个 2^n-1之前的容量,扩容不能超过max capacity会报错。ByteBuf依据内存类型分类:基于间接内存的ByteBuf(默认)和基于堆内存的ByteBuf。ByteBuf buffer = ByteBufAllocator.DEFAULT.heapBuffer(10);//基于堆内存ByteBuf buffer = ByteBufAllocator.DEFAULT.directBuffer(10);//基于堆外内存ByteBuf实现池化:没有池化,每次都创立新的 ByteBuf 实例,这个操作对间接内存代价低廉,就算是堆内存,也会减少 GC 压力。
通过重用 ByteBuf 实例,并且采纳了与 jemalloc 相似的内存调配算法晋升调配效率,高并发时,池化性能更节约内存,缩小内存溢出的可能。池化性能是否开启,能够通过上面的零碎环境变量来设置-Dio.netty.allocator.type={unpooled|pooled} //4.1当前对于非安卓平台默认池化,安卓平台非池化。
对于堆外内存的回收
Netty 这里采纳了援用计数法来管制回收内存,每个 ByteBuf 都实现了 ReferenceCounted 接口,每个ByteBuf对象的初始计数为 1:
调用release办法计数减 1,如果计数为 0,ByteBuf内存被回收;
调用retain办法计数加 1,示意调用者没用完之前,其它handler即便调用了release也不会造成回收;
当计数为 0 时,底层内存会被回收,这时即便 ByteBuf 对象还在,其各个办法均无奈失常应用。
本期分享就到这里,关注咱们定期更新更多精彩内容~作者:数新网络链接:https://juejin.cn/post/7236668847835037752起源:稀土掘金著作权归作者所有。商业转载请分割作者取得受权,非商业转载请注明出处。
发表回复