首页 > 编程知识 正文

netty源码剖析与应用,netty源码剖析与应用 csdn 下载

时间:2023-05-04 06:35:10 阅读:27727 作者:1171

1. Netty概述Netty是一个高性能、异步的事件驱动NIO框架,基于JAVA NIO提供的API实现。 支持TCP、UDP和文件传输。 另外,作为异步NIO框架,Netty的所有IO操作都是异步、无阻塞的,如果使用Future-Listener机制,用户可以容易主动获取,也可以使用通知机制获取IO操作的结果作为目前最流行的NIO框架,Netty广泛应用于互联网领域、大数据分布式计算领域、游戏行业、通信行业等,一些行业知名的开源组件也基于Netty的NIO框架

2. Netty线程模型在JAVA NIO方面为选择器为Reactor模式提供了基础,Netty结合选择器和Reactor模式设计了高效的线程模型。 首先,让我们看看Reactor模式:

2.1反射器模式

维基百科对Reactor模型进行了如下说明。 “thereactordesignpatternisaneventhandlingpatternforhandlingservicerequestsdeliveredconcurrentlybyoneormor uts.theservicehandehand mingrequestsanddispatchesthemsynchronouslytoassociatedrequesthandlers."。 首先,Reactor模式是一个事件驱动,有一个或多个并发输入源,有一个服务器处理程序和多个请求处理程序,该服务处理程序对同步输入的请求进行复用并对应如下图所示。

在结构上,它类似于生产者和消费者模型,一个或多个生产者将事件放入一个队列中,一个或多个消费者主动从该队列轮询和处理事件,而Reactor模式没有用于缓冲区的队列。 每次将某个事件输入到服务猎户时,服务猎户都会主动将该事件分发到相应的请求处理程序(针对每种类型的Evnent )进行处理。

2.2实现还原模式

关于Java NIO结构Reator模式,Doug lea在《Scalable IO in Java》中有很好的论述,这里通过切取PPT对Reator模式的实现进行说明

1 .第一种实现模式如下:

这是最简单的Reactor单线程模型。 由于Reactor模式使用异步无阻塞IO,所以所有IO操作都不会被阻塞,理论上一个线程可以独立处理所有IO操作。 此时,Reactor线程是负责套接字的多路分离、Accept的新连接、向处理链分发请求的多方面手段。

一些小容量APP场景可以使用单线程模型。 但是,对于高负荷,大规模的同时应用是不合适的,主要原因如下。

如果一个NIO线程同时处理成百上千个链路,则无法提供性能支持。 即使NIO线程的CPU负载达到100%,也无法完全处理消息。 如果NIO线程负载过高,处理速度往往会降低,大量客户端连接超时,超时后重新发送,导致NIO线程负载增加。 不可靠,如果一个线程猝死循环,整个通信系统就不可用,为了解决这些问题,出现了Reactor多线程模型。

2.Reactor多线程模型:

与前一种模式相比,该模型在处理链部分采用多线程(线程池)。

在大多数场景中,该模型能够满足性能需要。 但是,在特殊的APP应用程序场景(如服务器)中,客户端握手是安全验证的。 在这种情况下,只有一个Acceptor线程可能会导致性能不足。 为了解决这些问题,产生了第三个Reactor线程模型

3.Reactor主从模型

该模型与第二种模型相比,将Reactor分为两部分,主Reactor负责监听服务器套接字、接受器的新连接; 将创建的套接字分配给subReactor。 subReactor负责所连接的套接字的多路分离、网络数据的读写、业务处理功能,将其扔到工作器线程池中完成。 通常,子反应器的数量可以等于CPU的数量。

2.3自然模式

2.2中介绍了Reactor的3种模型,Netty是哪一种? 实际上,Netty的线程模型是Reactor模型的变种。 那是去除线程池的第三种形式的变种。 这也是Netty NIO的默认模式。 Netty的Reactor模式参与者主要具有以下组件:

selectoreventloopgroup/eventloopchannelpipelineselector是由NIO提供的可选择通道复用器,用作demultiplexer。 这里不说明。 介绍另外两个功能以及Netty在Reactor模式下的wxdwt。

3 .在3.EventLoopGroup/EventLoop系统运行时,频繁切换线程上下文会降低性能。 同时运行多线程业务流程,如果业务开发人员需要

刻对线程安全保持警惕,哪些数据可能会被并发修改,如何保护?这不仅降低了开发效率,也会带来额外的性能损耗。

为了解决上述问题,Netty采用了串行化设计理念,从消息的读取、编码以及后续Handler的执行,始终都由IO线程EventLoop负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险。这也解释了为什么Netty线程模型去掉了Reactor主从模型中线程池。

EventLoopGroup是一组EventLoop的抽象,EventLoopGroup提供next接口,可以总一组EventLoop里面按照一定规则获取其中一个EventLoop来处理任务,对于EventLoopGroup这里需要了解的是在Netty中,在Netty服务器编程中我们需要BossEventLoopGroup和WorkerEventLoopGroup两个EventLoopGroup来进行工作。通常一个服务端口即一个ServerSocketChannel对应一个Selector和一个EventLoop线程,也就是说BossEventLoopGroup的线程数参数为1。BossEventLoop负责接收客户端的连接并将SocketChannel交给WorkerEventLoopGroup来进行IO处理。

EventLoop的实现充当Reactor模式中的分发(Dispatcher)的角色。

4.ChannelPipeline

ChannelPipeline其实是担任着Reactor模式中的请求处理器这个角色。

ChannelPipeline的默认实现是DefaultChannelPipeline,DefaultChannelPipeline本身维护着一个用户不可见的tail和head的ChannelHandler,他们分别位于链表队列的头部和尾部。tail在更上层的部分,而head在靠近网络层的方向。在Netty中关于ChannelHandler有两个重要的接口,ChannelInBoundHandler和ChannelOutBoundHandler。inbound可以理解为网络数据从外部流向系统内部,而outbound可以理解为网络数据从系统内部流向系统外部。用户实现的ChannelHandler可以根据需要实现其中一个或多个接口,将其放入Pipeline中的链表队列中,ChannelPipeline会根据不同的IO事件类型来找到相应的Handler来处理,同时链表队列是责任链模式的一种变种,自上而下或自下而上所有满足事件关联的Handler都会对事件进行处理。

ChannelInBoundHandler对从客户端发往服务器的报文进行处理,一般用来执行半包/粘包,解码,读取数据,业务处理等;ChannelOutBoundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码,发送报文到客户端。

下图是对ChannelPipeline执行过程的说明:

关于Pipeline的更多知识可参考:浅谈管道模型(Pipeline)

5.Buffer

Netty提供的经过扩展的Buffer相对NIO中的有个许多优势,作为数据存取非常重要的一块,我们来看看Netty中的Buffer有什么特点。

1.ByteBuf读写指针

在ByteBuffer中,读写指针都是position,而在ByteBuf中,读写指针分别为readerIndex和writerIndex,直观看上去ByteBuffer仅用了一个指针就实现了两个指针的功能,节省了变量,但是当对于ByteBuffer的读写状态切换的时候必须要调用flip方法,而当下一次写之前,必须要将Buffe中的内容读完,再调用clear方法。每次读之前调用flip,写之前调用clear,这样无疑给开发带来了繁琐的步骤,而且内容没有读完是不能写的,这样非常不灵活。相比之下我们看看ByteBuf,读的时候仅仅依赖readerIndex指针,写的时候仅仅依赖writerIndex指针,不需每次读写之前调用对应的方法,而且没有必须一次读完的限制。

2.零拷贝

Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

3.引用计数与池化技术

在Netty中,每个被申请的Buffer对于Netty来说都可能是很宝贵的资源,因此为了获得对于内存的申请与回收更多的控制权,Netty自己根据引用计数法去实现了内存的管理。Netty对于Buffer的使用都是基于直接内存(DirectBuffer)实现的,大大提高I/O操作的效率,然而DirectBuffer和HeapBuffer相比之下除了I/O操作效率高之外还有一个天生的缺点,即对于DirectBuffer的申请相比HeapBuffer效率更低,因此Netty结合引用计数实现了PolledBuffer,即池化的用法,当引用计数等于0的时候,Netty将Buffer回收致池中,在下一次申请Buffer的没某个时刻会被复用。 总结

Netty其实本质上就是Reactor模式的实现,Selector作为多路复用器,EventLoop作为转发器,Pipeline作为事件处理器。但是和一般的Reactor不同的是,Netty使用串行化实现,并在Pipeline中使用了责任链模式。

Netty中的buffer相对有NIO中的buffer又做了一些优化,大大提高了性能。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。