简单了解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 sqlobject(mysql)中文乱码解决方法
Nov 14 Python
在Python中使用判断语句和循环的教程
Apr 25 Python
python3中dict(字典)的使用方法示例
Mar 22 Python
python3之微信文章爬虫实例讲解
Jul 12 Python
python监控linux内存并写入mongodb(推荐)
Sep 11 Python
import的本质解析
Oct 30 Python
python基于递归解决背包问题详解
Jul 03 Python
python 实现提取log文件中的关键句子,并进行统计分析
Dec 24 Python
基于Python绘制美观动态圆环图、饼图
Jun 03 Python
Python request中文乱码问题解决方案
Sep 17 Python
Python爬虫Scrapy框架CrawlSpider原理及使用案例
Nov 20 Python
Python操作Excel的学习笔记
Feb 18 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下实现农历日历的代码
2007/03/07 PHP
Php-Redis安装测试笔记
2015/03/05 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
PHP实现链表的定义与反转功能示例
2018/06/09 PHP
Mootools 1.2教程 滑动效果(Slide)
2009/09/15 Javascript
JavaScript 函数replace深入了解
2013/03/14 Javascript
jquery xMarquee实现文字水平无缝滚动效果
2014/04/29 Javascript
Ubuntu中搭建Nodejs开发环境过程分享
2014/06/01 NodeJs
Javascript设计模式理论与编程实战之简单工厂模式
2015/11/03 Javascript
基于JavaScript实现移动端点击图片查看大图点击大图隐藏
2015/11/04 Javascript
JavaScript实现移动端滑动选择日期功能
2016/06/21 Javascript
jquery uploadify如何取消已上传成功文件
2017/02/08 Javascript
微信小程序图片横向左右滑动案例
2017/05/19 Javascript
通过jquery toggleClass()属性制作文章段落更改背景颜色
2018/05/21 jQuery
详解Angular6学习笔记之主从组件
2018/09/05 Javascript
JS数组求和的常用方法总结【5种方法】
2019/01/14 Javascript
使用vue-router在Vue页面之间传递数据的方法
2019/07/15 Javascript
原生js实现商品筛选功能
2019/10/28 Javascript
Vue $emit()不能触发父组件方法的原因及解决
2020/07/28 Javascript
JS实现炫酷轮播图
2020/11/15 Javascript
Python入门篇之字符串
2014/10/17 Python
python编程开发之类型转换convert实例分析
2015/11/13 Python
Python3中使用PyMongo的方法详解
2017/07/28 Python
Python爬虫小例子——爬取51job发布的工作职位
2020/07/10 Python
Django模型验证器介绍与源码分析
2020/09/08 Python
python之语音识别speech模块
2020/09/09 Python
通用的Django注册功能模块实现方法
2021/02/05 Python
荷兰之家英文站:Holland at Home
2016/10/26 全球购物
elf彩妆英国官网:e.l.f. Cosmetics英国(美国平价彩妆品牌)
2017/11/02 全球购物
DJI全球:DJI Global
2021/03/15 全球购物
Java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类
2012/02/06 面试题
开业庆典答谢词
2014/01/18 职场文书
工程材料采购方案
2014/05/18 职场文书
企业消防安全责任书
2014/07/23 职场文书
2014年工人工作总结
2014/11/25 职场文书
2019餐饮行业创业计划书!
2019/06/27 职场文书