首页 > 编程知识 正文

netty线程模型详解,netty的异步实现原理

时间:2023-05-06 11:04:45 阅读:27675 作者:480

Netty概述Netty是一个基于Java NIO提供的API的高性能异步事件驱动NIO框架,包括TCP、UDP、 和支持文件传输的所有Netty操作都是异步、无阻塞的,使用Future-Lisener机制的用户可以轻松主动获取,也可以使用通知机制获取IO操作的结果,从而实现分布式通信行业的Netty线程模型Netty将JavaNIO的选择器和Reactor模型相结合,设计了高效的线程模型

Reactor模式基于事件驱动

一个或多个并发输入源一个Server Handler多个请求处理程序http://www.Sina.com /

Reactor模式实现了Reactor单线程模型1,允许单个线程独立处理所有I/o操作

2、负责套接字的多路分离和Accept的新连接,并将请求下发到处理链

3、一些小容量APP场景可以用于单线程机型,但对于高负载,大容量并发APP应用不合适的原因是

如果一个NIO线程同时处理数百、数千个链路,则即使性能不够,NIO线程的CPU负载达到100%,也无法完全处理消息

如果NIO线程负载过高,处理速度往往会变慢,许多客户端连接超时,超时后重新发送,导致NIO线程负载更高。

不可靠,一个线程意外循环,导致整个通信系统不可用

Reactor多线程模型此模型可解决上述问题

该模型在处理链部分采用了多线程(线程池)

在大多数场景中,该模型可以满足性能要求

但是,有一个特殊的APP应用程序场景,包括服务器安全地验证客户端握手

在这种情况下,只有一个Acceptor线程可能会导致性能不足

为了解决这些问题,产生了第三个Reactor线程模型

Reactor主从模型a、Reactor分为两部分,主Reactor拦截服务器套接字、接受器的新连接

将创建的套接字分配给subReactor

b、subReactor负责所连接插座的多路分离

c、工作器线程池完成网络数据读写、业务处理功能

d、辅助reactor的个数存在与CPU的个数同等的弊端

在系统运行时频繁切换线程上下文会降低性能

要考虑多线程安全问题

Netty机型每当一个事件输入到Service Handler之后,该Service Handler会主动根据不同的Evnent类型将其分发给对应的Request Handler来处理

为了解决上述问题

Netty采用了串行化设计理念

读取、编码消息和从后续处理程序执行消息

始终由IO线程的事件loop负责

出乎意料的是,整个进程不切换线程上下文

也不存在数据同时更改的风险

核心组件的选择器是NIO提供的选择通道复用器

起着demultiplexer的作用

事件loopgroup/event loop http://www.Sina.com /

EventLoopGroup是事件loop的一组抽象的EventLoopGroup,提供next界面。 根据一定的规则,从事件loop组中检索事件loop之一以处理任务。 在EventLoopGroup中,Netty需要了解Netty服务器编程需要boseventty。通常,有两个eventloopgroup:op group和WorkerEventLoopGroup进行工作,一个也就是说,一个服务器套接字通道对应于一个选择器和一个事件loop线程,也就是说,BossEventLoopGroup的线程数参数是1 BossEventLoop接受来自客户端的连接

通道pipeline的缺省实现是DefaultChannelPipeline。

DefaultChann

elPipeline本身维护着一个用户不可见的tail和head的ChannelHandler,他们分别位于链表队列的头部和尾部。tail在更上层的部分,而head在靠近网络层的方向

在Netty中关于ChannelHandler有两个重要的接口,ChannelInBoundHandler和ChannelOutBoundHandler

inbound可以理解为网络数据从外部流向系统内部而outbound可以理解为网络数据从系统内部流向系统外部

用户实现的ChannelHandler可以根据需要实现其中一个或多个接口,将其放入Pipeline中的链表队列中,ChannelPipeline会根据不同的IO事件类型来找到相应的Handler来处理,同时链表队列是责任链模式的一种变种,自上而下或自下而上所有满足事件关联的Handler都会对事件进行处理

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

ChannelOutBoundHandler对从服务器发往客户端的报文进行处理,一般用来进行编码,发送报文到客户端

Buffer ByteBuf读写指针 在ByteBuffer中,读写指针都是position
而在ByteBuf中,读写指针分别为readerIndex和writerIndex
直观看上去ByteBuffer仅用了一个指针就实现了两个指针的功能,节省了变量
但是当对于ByteBuffer的读写状态切换的时候必须要调用flip方法而当下一次写之前,必须要将Buffe中的内容读完,再调用clear方法
每次读之前调用flip,写之前调用clear,这样无疑给开发带来了繁琐的步骤
而且内容没有读完是不能写的,这样非常不灵活
相比之下我们看看ByteBuf
读的时候仅仅依赖readerIndex指针,写的时候仅仅依赖writerIndex指针
不需每次读写之前调用对应的方法
而且没有必须一次读完的限制
零拷贝 1、Netty的接收和发送ByteBuffer采用DIRECT BUFFERS
使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝
如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写
JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中
相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

2、Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象
用户可以像操作一个Buffer那样方便的对组合Buffer进行操作
避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer
Netty的文件传输采用了transferTo方法
它可以直接将文件缓冲区的数据发送到目标Channel
避免了传统通过循环write方式导致的内存拷贝问题。
引用计数与池化技术 在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中使用了责任链模式

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