简单了解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的一些补充
Jun 09 Python
简洁的十分钟Python入门教程
Apr 03 Python
python学习之编写查询ip程序
Feb 27 Python
python使用fcntl模块实现程序加锁功能示例
Jun 23 Python
tensorflow建立一个简单的神经网络的方法
Feb 10 Python
Django model反向关联名称的方法
Dec 15 Python
详解爬虫被封的问题
Apr 23 Python
django基于cors解决跨域请求问题详解
Aug 06 Python
基于tensorflow指定GPU运行及GPU资源分配的几种方式小结
Feb 03 Python
python DES加密与解密及hex输出和bs64格式输出的实现代码
Apr 13 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
Apr 26 Python
pandas中关于apply+lambda的应用
Feb 28 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
WinXP + Apache +PHP5 + MySQL + phpMyAdmin安装全功略
2006/07/09 PHP
PHP中实现进程间通讯
2006/10/09 PHP
phpMyAdmin 安装教程全攻略
2007/03/19 PHP
php实现批量修改文件名称的方法
2016/07/23 PHP
总结PHP删除字符串最后一个字符的三种方法
2016/08/30 PHP
jQuery dialog 异步调用ashx,webservice数据的代码
2010/08/03 Javascript
新浪微博字数统计 textarea字数统计实现代码
2011/08/28 Javascript
js中return false(阻止)的用法
2013/08/14 Javascript
jquery实现select下拉框美化特效代码分享
2015/08/18 Javascript
JS+CSS实现六级网站导航主菜单效果
2015/09/28 Javascript
谈谈PHP中相对路径的问题与绝对路径的使用
2016/08/16 Javascript
可输入文字查找ajax下拉框控件 ComBox的实现方法
2016/10/25 Javascript
jQuery 的 ready()的纯js替代方法
2016/11/20 Javascript
Extjs让combobox写起来简洁又漂亮
2017/01/05 Javascript
jQuery实现鼠标悬停3d菜单展开动画效果
2017/01/19 Javascript
JS实现求数组起始项到终止项之和的方法【基于数组扩展函数】
2017/06/13 Javascript
JavaScript门面模式详解
2017/10/19 Javascript
javascript 通过键名获取键盘的keyCode方法
2017/12/31 Javascript
vuejs前后端数据交互之从后端请求数据的实例
2018/08/11 Javascript
基于vue和react的spa进行按需加载的实现方法
2018/09/29 Javascript
vue中可编辑树状表格的实现代码
2020/10/31 Javascript
解决Python 命令行执行脚本时,提示导入的包找不到的问题
2019/01/19 Python
python爬取内容存入Excel实例
2019/02/20 Python
python/Matplotlib绘制复变函数图像教程
2019/11/21 Python
Python字符串三种格式化输出
2020/09/17 Python
用HTML5的canvas实现一个炫酷时钟效果
2016/05/20 HTML / CSS
The North Face北面英国官网:美国著名户外品牌
2017/12/13 全球购物
Omio葡萄牙:全欧洲低价大巴、火车和航班搜索和比价
2019/02/09 全球购物
澳大利亚二手奢侈品网站:Modsie
2019/09/23 全球购物
如何查找和删除数据库中的重复数据
2014/11/05 面试题
毕业生实习鉴定
2013/12/11 职场文书
二手房购房意向书范本
2014/04/01 职场文书
物业管理工作方案
2014/05/10 职场文书
优秀中职教师事迹材料
2014/08/26 职场文书
MySQL如何使用使用Xtrabackup进行备份和恢复
2021/06/21 MySQL
SQL实现LeetCode(197.上升温度)
2021/08/07 MySQL