简单了解Java Netty Reactor三种线程模型


Posted in Python onApril 26, 2020

1. Reactor三种线程模型

1.1. 单线程模型

Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:

1)作为NIO服务端,接收客户端的TCP连接;
2)作为NIO客户端,向服务端发起TCP连接;
3)读取通信对端的请求或者应答消息;
4)向通信对端发送消息请求或者应答消息。

Reactor单线程模型示意图如下所示:

简单了解Java Netty Reactor三种线程模型

Reactor单线程模型

由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作。从架构层面看,一个NIO线程确实可以完成其承担的职责。例如,通过Acceptor类接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户线程可以通过消息编码通过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:
1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;
2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;
3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

为了解决这些问题,演进出了Reactor多线程模型,如下。

1.2. 多线程模型

Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操作,它的原理图如下:

简单了解Java Netty Reactor三种线程模型

Rector多线程模型

Reactor多线程模型的特点:

1)有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;
2)网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;
3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极个别特殊场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

1.3. 主从多线程模型

主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

主从多线程模型如下图所示:

简单了解Java Netty Reactor三种线程模型

主从多线程模型

利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。

它的工作流程总结如下:

1)从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接;Acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作;

2)步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理I/O的读写操作。

2. Netty线程模型

2.1. Netty线程模型分类

事实上,Netty的线程模型与1.2章节中介绍的三种Reactor线程模型相似,下面章节我们通过Netty服务端和客户端的线程处理流程图来介绍Netty的线程模型。

2.1.1. 服务端线程模型
一种比较流行的做法是服务端监听线程和IO线程分离,类似于Reactor的多线程模型,它的工作原理图如下:

简单了解Java Netty Reactor三种线程模型

2.1.2. 客户端线程模型

相比于服务端,客户端的线程模型简单一些,它的工作原理如下:

简单了解Java Netty Reactor三种线程模型

2.2. Reactor线程NioEventLoop

NioEventLoop是Netty的Reactor线程,它的职责如下:

  • 作为服务端Acceptor线程,负责处理客户端的请求接入;
  • 作为客户端Connecor线程,负责注册监听连接操作位,用于判断异步连接结果;
  • 作为IO线程,监听网络读操作位,负责从SocketChannel中读取报文;
  • 作为IO线程,负责向SocketChannel写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用于后续继续发送半包数据,直到数据全部发送完成;
  • 作为定时任务线程,可以执行定时任务,例如链路空闲检测和发送心跳消息等;
  • 作为线程执行器可以执行普通的任务线程(Runnable)。

2.3. NioEventLoop设计原理

我们知道当系统在运行过程中,如果频繁的进行线程上下文切换,会带来额外的性能损耗。多线程并发执行某个业务流程,业务开发者还需要时刻对线程安全保持警惕,哪些数据可能会被并发修改,如何保护?这不仅降低了开发效率,也会带来额外的性能损耗。

串行执行Handler链

为了解决上述问题,Netty采用了串行化设计理念,从消息的读取、编码以及后续Handler的执行,始终都由IO线程NioEventLoop负责,这就意外着整个流程不会进行线程上下文的切换,数据也不会面临被并发修改的风险,对于用户而言,甚至不需要了解Netty的线程细节,这确实是个非常好的设计理念,它的工作原理图如下:

简单了解Java Netty Reactor三种线程模型

一个NioEventLoop聚合了一个多路复用器Selector,因此可以处理成百上千的客户端连接,Netty的处理策略是每当有一个新的客户端接入,则从NioEventLoop线程组中顺序获取一个可用的NioEventLoop,当到达数组上限之后,重新返回到0,通过这种方式,可以基本保证各个NioEventLoop的负载均衡。一个客户端连接只注册到一个NioEventLoop上,这样就避免了多个IO线程去并发操作它。

Netty通过串行化设计理念降低了用户的开发难度,提升了处理性能。利用线程组实现了多个串行化线程水平并行执行,线程之间并没有交集,这样既可以充分利用多核提升并行处理能力,同时避免了线程上下文的切换和并发保护带来的额外性能损耗。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现探测socket和web服务示例
Mar 28 Python
Python算法之栈(stack)的实现
Aug 18 Python
用Python实现命令行闹钟脚本实例
Sep 05 Python
python对DICOM图像的读取方法详解
Jul 17 Python
python数据结构之列表和元组的详解
Sep 23 Python
Python扩展内置类型详解
Mar 26 Python
python 从文件夹抽取图片另存的方法
Dec 04 Python
Python类和对象的定义与实际应用案例分析
Dec 27 Python
在vscode中配置python环境过程解析
Sep 28 Python
tensorflow 只恢复部分模型参数的实例
Jan 06 Python
python实现logistic分类算法代码
Feb 28 Python
详解Django ORM引发的数据库N+1性能问题
Oct 12 Python
Python Selenium截图功能实现代码
Apr 26 #Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
Apr 26 #Python
Python实现密钥密码(加解密)实例详解
Apr 26 #Python
Python基于QQ邮箱实现SSL发送
Apr 26 #Python
Eclipse配置python默认头过程图解
Apr 26 #Python
2020最新pycharm汉化安装(python工程狮亲测有效)
Apr 26 #Python
在服务器上安装python3.8.2环境的教程详解
Apr 26 #Python
You might like
Joomla简单判断用户是否登录的方法
2016/05/04 PHP
PHP call_user_func和call_user_func_array函数的简单理解与应用分析
2019/11/25 PHP
JavaScript DOM学习第六章 表单实例
2010/02/19 Javascript
jQuery实现渐变下拉菜单的简单方法
2015/03/11 Javascript
javascript+HTML5的Canvas实现Lab单车动画效果
2015/08/07 Javascript
JS判断当前页面是否在微信浏览器打开的方法
2015/12/08 Javascript
全国省市二级联动下拉菜单 js版
2016/05/10 Javascript
实用jquery操作表单元素的简单代码
2016/07/04 Javascript
功能强大的Bootstrap组件(结合js)
2016/08/03 Javascript
jQuery插件EasyUI设置datagrid的checkbox为禁用状态的方法
2016/08/05 Javascript
JavaScript SHA-256加密算法详细代码
2016/10/06 Javascript
基于JavaScript实现右键菜单和拖拽功能
2016/11/28 Javascript
JS 拦截全局ajax请求实例解析
2016/11/29 Javascript
jQuery插件Echarts实现的双轴图效果示例【附demo源码下载】
2017/03/04 Javascript
基于Nodejs的Tcp封包和解包的理解
2018/09/19 NodeJs
JavaScript基于数组实现的栈与队列操作示例
2018/12/22 Javascript
[36:29]2018DOTA2亚洲邀请赛 4.1 小组赛 A组加赛 LGD vs TNC
2018/04/02 DOTA
[52:26]完美世界DOTA2联赛决赛 FTD vs Phoenix 第一场 11.08
2020/11/11 DOTA
Python help()函数用法详解
2014/03/11 Python
python文件操作整理汇总
2014/10/21 Python
python中引用与复制用法实例分析
2015/06/04 Python
Python中print函数简单使用总结
2019/08/05 Python
Python 批量读取文件中指定字符的实现
2020/03/06 Python
Anaconda和ipython环境适配的实现
2020/04/22 Python
Html5移动端获奖无缝滚动动画实现示例
2018/06/25 HTML / CSS
印尼披萨外送专家:Domino’s Pizza印尼
2017/12/28 全球购物
年会活动策划方案
2014/01/23 职场文书
中秋节礼品促销方案
2014/02/02 职场文书
委托公证书范本
2014/04/03 职场文书
对祖国的寄语大全
2014/04/11 职场文书
竞选学生会主席演讲稿
2014/04/24 职场文书
共产党员岗位承诺书
2014/05/29 职场文书
北大自主招生自荐信
2015/03/04 职场文书
社区青年志愿者活动总结
2015/05/06 职场文书
python unittest单元测试的步骤分析
2021/08/02 Python
JavaScript设计模式之原型模式详情
2022/06/21 Javascript