简单了解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进行数据科学工作的简单入门教程
Apr 01 Python
用Python写飞机大战游戏之pygame入门(4):获取鼠标的位置及运动
Nov 05 Python
浅谈Python 的枚举 Enum
Jun 12 Python
Numpy中stack(),hstack(),vstack()函数用法介绍及实例
Jan 09 Python
python3实现小球转动抽奖小游戏
Apr 15 Python
python在新的图片窗口显示图片(图像)的方法
Jul 11 Python
python dumps和loads区别详解
Feb 04 Python
Python3 元组tuple入门基础
Feb 09 Python
jupyter修改文件名方式(TensorFlow)
Apr 21 Python
详解Python中namedtuple的使用
Apr 27 Python
Mysql数据库反向生成Django里面的models指令方式
May 18 Python
Python之matplotlib绘制饼图
Apr 13 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 insert语法详解
2008/06/07 PHP
php 显示指定路径下的图片
2009/10/29 PHP
PHP中CURL方法curl_setopt()函数的参数分享
2013/01/19 PHP
PHP实现批量生成App各种尺寸Logo
2015/03/19 PHP
PHP自带方法验证邮箱是否存在
2016/02/01 PHP
PHP邮件群发机实现代码
2016/02/16 PHP
php实现的三个常用加密解密功能函数示例
2017/11/06 PHP
php链式操作的实现方式分析
2019/08/12 PHP
jQuery 1.3 和 Validation 验证插件1.5.1
2009/07/09 Javascript
JavaScript Event学习第五章 高级事件注册模型
2010/02/07 Javascript
javascript 事件处理、鼠标拖动效果实现方法详解
2012/05/11 Javascript
关于js遍历表格的实例
2013/07/10 Javascript
JS中typeof与instanceof之间的区别总结
2013/11/14 Javascript
js数组依据下标删除元素
2015/04/14 Javascript
javascript的变量、传值、传址、参数之间关系
2015/07/26 Javascript
cocos2dx骨骼动画Armature源码剖析(三)
2015/09/08 Javascript
js判断当前页面在移动设备还是在PC端中打开
2016/01/06 Javascript
headjs实现网站并行加载但顺序执行JS
2016/11/29 Javascript
Vue.js实战之组件的进阶
2017/04/04 Javascript
Bootstrap Table使用整理(一)
2017/06/09 Javascript
利用JavaScript对中文(汉字)进行排序实例详解
2017/06/18 Javascript
Nodejs实现文件上传的示例代码
2017/09/26 NodeJs
Vue2.0+Vux搭建一个完整的移动webApp项目的示例
2019/03/19 Javascript
Vue.js中的组件系统
2019/05/30 Javascript
python实现串口自动触发工作的示例
2019/07/02 Python
Python 中list ,set,dict的大规模查找效率对比详解
2019/10/11 Python
如何利用python web框架做文件流下载的实现示例
2020/06/02 Python
Python优秀开源项目Rich源码解析的流程分析
2020/07/06 Python
PyTorch中Tensor的数据类型和运算的使用
2020/09/03 Python
虚拟环境及venv和virtualenv的区别说明
2021/02/05 Python
美体小铺法国官方网站:The Body Shop法国
2020/06/04 全球购物
Bitiba意大利:在线宠物商店
2020/10/31 全球购物
机关作风建设剖析材料
2014/10/11 职场文书
高中生毕业评语
2014/12/30 职场文书
我们的节日重阳节活动总结
2015/03/24 职场文书
MYSQL(电话号码,身份证)数据脱敏的实现
2021/05/28 MySQL