Netty Reactor模型

转载 2015年05月10日 22:12:11
无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件。

1、Reactor三种线程模型

1.1. 单线程模型

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


1)作为NIO服务端,接收客户端的TCP连接;


2)作为NIO客户端,向服务端发起TCP连接;


3)读取通信对端的请求或者应答消息;


4)向通信对端发送消息请求或者应答消息。


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




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


对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:


1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;


2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;


3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。


为了解决这些问题,演进出了Reactor多线程模型,下面我们一起学习下Reactor多线程模型。


1.2. 多线程模型

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

Reactor多线程模型的特点:


1)有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;


2)网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;


3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。


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





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


它的线程模型如下图所示:






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


它的工作流程总结如下:


从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接;
Acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作;
步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理I/O的读写操作。




2. Netty线程模型

2.1. Netty线程模型分类

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


2.1.1. 服务端线程模型

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






2.1.2. 客户端线程模型

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






2.2. Reactor线程NioEventLoop

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


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




2.3. NioEventLoop设计原理


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


串行执行Handler链


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




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


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

Netty高性能之Reactor线程模型

Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用...
  • king866
  • king866
  • 2017年01月14日 18:05
  • 3451

[netty]--Reactor线程模型以及在netty中的应用

说道netty的线程模型,我们第一反应就是经典的Reactor线程模型,下面我们就来一起探讨一下三种经典的Reactor线程模型: 这里我们需要理解的一点是Reactor线程模型是基于同步非...
  • u010853261
  • u010853261
  • 2017年02月19日 17:16
  • 1980

Netty教程-Reactor模式

Reactor模式是什么?Reactor是用于处理多个客户端的请求的设计模式。应用程序提供的每一种服务都可能包括多个方法,并且有必要为这每一个服务分配独立的请求处理器(也可以说是 event hand...
  • woaixiaopangniu521
  • woaixiaopangniu521
  • 2017年04月20日 15:49
  • 273

netty

NIO网络编程: 开发出高质量的NIO程序并不是一件简单的事情,除去NIO固有的复杂性和BUG不谈,作为一个NIO服务端需要能够处理网络的闪断、客户端的重复接入、客户端的安全认证、消息的编解码、...
  • qq_34034791
  • qq_34034791
  • 2017年03月07日 09:25
  • 398

Netty那点事(四)Netty与Reactor模式

一:Netty、NIO、多线程? 时隔很久终于又更新了!之前一直迟迟未动也是因为积累不够,后面比较难下手。过年期间@李林锋hw发布了一个Netty5.0架构剖析和源码解读 http://vdis...
  • zhangrongchao_
  • zhangrongchao_
  • 2015年05月29日 10:02
  • 1340

Netty与Reactor模式

一:Netty、NIO、多线程? 时隔很久终于又更新了!之前一直迟迟未动也是因为积累不够,后面比较难下手。过年期间@李林锋hw发布了一个Netty5.0架构剖析和源码解读 ,看完也是收获不少。前...
  • wwwxxdddx
  • wwwxxdddx
  • 2016年02月16日 11:02
  • 302

Netty系列之Netty 服务端创建

背景 1.1. 原生NIO类库的复杂性在开始本文之前,我先讲一件自己亲身经历的事:大约在2011年的时候,周边的两个业务团队同时进行新版本开发,他们都需要基于NIO非阻塞特性构建高性能、异步和高可靠性...
  • limuzi13
  • limuzi13
  • 2016年05月21日 20:02
  • 1211

Netty原理剖析

1Netty简介Netty是一个高性能、异步事件驱动的NIO框架,基于JAVA NIO提供的API实现。本文简单聊一下Netty的实现原理。...
  • ExcellentYuXiao
  • ExcellentYuXiao
  • 2016年11月29日 11:10
  • 2942

netty原理详解

NIO客户端序列图 声明:本文是《Netty 权威指南》的样章,感谢博文视点授权并发编程网站发布样章,禁止以任何形式转载此文。 步骤一:打开SocketChannel,绑定客户端本地地址(可选,...
  • wanbf123
  • wanbf123
  • 2017年09月22日 10:07
  • 505

netty学习笔记(一)—结合reactor模式探索netty对网络io的处理机制

Reactor与Proactor简介 reactor、proactor常见的翻译是反应器(堆)、前摄器,这名字听着总让人一头雾水的,抓不着本质。后来看看对应形容词的英文释义,再结合技术角度的描述,总算...
  • u011090495
  • u011090495
  • 2014年03月19日 00:16
  • 4339
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Netty Reactor模型
举报原因:
原因补充:

(最多只允许输入30个字)