简单了解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益智游戏计算汉诺塔问题示例
Mar 05 Python
Python执行时间的计算方法小结
Mar 17 Python
Python元组及文件核心对象类型详解
Feb 11 Python
Python 装饰器实现DRY(不重复代码)原则
Mar 05 Python
Python简单实现两个任意字符串乘积的方法示例
Apr 12 Python
python pandas 组内排序、单组排序、标号的实例
Apr 12 Python
Python装饰器基础概念与用法详解
Dec 22 Python
详解django实现自定义manage命令的扩展
Aug 13 Python
python 操作hive pyhs2方式
Dec 21 Python
浅谈python 调用open()打开文件时路径出错的原因
Jun 05 Python
Python中return函数返回值实例用法
Nov 19 Python
解决python3输入的坑——input()
Dec 05 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
用PHP实现ODBC数据分页显示一例
2006/10/09 PHP
phpMyAdmin 链接表的附加功能尚未激活的问题
2010/08/01 PHP
PHP中文处理 中文字符串截取(mb_substr)和获取中文字符串字数
2011/11/10 PHP
PHP轻量级数据库操作类Medoo增加、删除、修改、查询例子
2014/07/04 PHP
JavaScript 检测浏览器和操作系统的脚本
2008/12/26 Javascript
JS自定义对象实现Java中Map对象功能的方法
2015/01/20 Javascript
JS+CSS实现的日本门户网站经典选项卡导航效果
2015/09/27 Javascript
图解JavaScript中的this关键字
2020/05/28 Javascript
jQuery插件ajaxFileUpload异步上传文件
2016/10/19 Javascript
RequireJS简易绘图程序开发
2016/10/28 Javascript
JavaScript中的toString()和toLocaleString()方法的区别
2017/02/15 Javascript
解决JS内存泄露之js对象和dom对象互相引用问题
2017/06/25 Javascript
Vuejs实现购物车功能
2017/11/05 Javascript
AngularJS基于http请求实现下载php生成的excel文件功能示例
2018/01/23 Javascript
vue+SSM实现验证码功能
2018/12/07 Javascript
layer.open提交子页面的form和layedit文本编辑内容的方法
2019/09/27 Javascript
vue自定义指令实现仅支持输入数字和浮点型的示例
2019/10/30 Javascript
javascript实现下拉菜单效果
2021/02/09 Javascript
[01:14:12]2018DOTA2亚洲邀请赛4.7 总决赛 LGD vs Mineski 第二场
2018/04/09 DOTA
tornado框架blog模块分析与使用
2013/11/21 Python
Python之Web框架Django项目搭建全过程
2017/05/02 Python
Numpy 将二维图像矩阵转换为一维向量的方法
2018/06/05 Python
Python之修改图片像素值的方法
2019/07/03 Python
用python爬取历史天气数据的方法示例
2019/12/30 Python
python 如何快速复制序列
2020/09/07 Python
详解HTML5通讯录获取指定多个人的信息
2016/12/20 HTML / CSS
Ellos丹麦:时尚和服装在线
2016/09/19 全球购物
武汉英思工程科技有限公司–ORACLE面试测试题目
2012/04/30 面试题
编程实现去掉XML的重复结点
2014/05/28 面试题
绿色城市实施方案
2014/03/19 职场文书
音乐节策划方案
2014/06/09 职场文书
2014年小学教师工作总结
2014/11/10 职场文书
2015年世界粮食日演讲稿
2015/03/20 职场文书
行政上诉状范文
2015/05/23 职场文书
Windows 11要来了?微软文档揭示Win11太阳谷 / Win10有两个不同版本
2021/11/21 数码科技
SpringCloud Feign请求头删除修改的操作代码
2022/03/20 Java/Android