zoukankan      html  css  js  c++  java
  • reactor 反应器模式

    高性能网络编程都绕不开反应器模式,比如nginx,redis,netty等;

    什么是反应器模式呢?引用doug lea在文章<<scalable io in java>>中对反映其模式的定义: 反应器模式有reactor反应器线程,handler处理器量大角色组成 1) reactor反应器线程的职责: 负责响应IO事件,并且分发到handlers处理器 2)handlers处理器的职责:非阻塞的执行业务处理逻辑, 原文地址 http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

    从上面的反应器模式定义,看不出这种模式有什么神奇的地方,当然从简单到复杂,反应器模式也有很多版本,根据前面的定义仅仅是一个最简单的版本.

    如果需要彻底了解反应器模式,还需要从原始的OIO编程(同步阻塞模型)开始讲起.

      在java的oio编程中,最初和最原始的服务器程序,使用一个while循环,不断地监听端口是否有新的连接,如果有,那么就调用一个处理函数来处理,示例如下

    while(true){
        socket = accept() ; // 阻塞接受连接
        handle(socket); // 读取数据,业务处理,写入结果  
    }

    这种方法最大的问题是:如果前一个网络连接的handle(socket)没有处理完,那么后面的连接请求没法被接收,于是后面的请求通通会被阻塞住,服务器的吞吐量太低.

    为了解决这个严重的连接阻塞问题,出现了一个极为经典的模式: connect per thread 即一个线程处理一个连接模式,对于每一个新的网络连接都会分配一个线程,每个线程都独自处理自己负责的输入和输出,当然服务器的监听线程也是独立的任何的socket连接的输入和输出处理,不会阻塞后面新的socket连接的监听和建立,早起的tomcat服务器,就是这样实现的.

    connect per thread 模式的优点是: 解决了前面的新连接被严重阻塞的问题,在一定程度上极大地提高了服务器的吞吐量.

    这里有个问题,一个线程同时负责处理多个cocket连接的输入和输出行不行呢?看上去没有什么不可以,但是,实际上没有用,为什么呢?传统的OIO编程中,每一个socket的IO读写操作,都是阻塞的,在同一个时刻,一个线程里只能处理一个socket,前一个socket被阻塞了,后边连接的IO操作是无法被并发执行的,所以,不论怎么处理,OIO中一个线程也只能处理一个连接的IO操作

    connect per thread 模式的缺点是: 对应于大量的连接,需要耗费大量的线程资源,对线程资源要求太高.在系统中,线程是比较昂贵的资源,如果线程太多,系统无法承受,而且,线程的反复创建,销毁,切换也需要代价,因此高并发的的场景下,多线程OIO的缺陷是致命的.

    如何解决connection per thread模型的巨大缺陷呢? 一个有效路径是:使用reactor反应器模式,用反应器模式对线程数量进行控制,做到一个线程处理大量的连接,他是如果做到的呢,首先来看简单的版本-单线程的reactor反应器模式

    单线程reactor反应器模式

    总体来说,reactor反应器模式有点类似事件驱动模式.

    在事件驱动模式中,当有事件触发时,事件源会将事件dispatch分发到handler处理器进行事件处理,反应器模式中的反应器角色,类似于事件驱动模式中的dispatcher事件分发器角色.

    前面已经提到,在反应器模式中,有reactor反应器和handler处理器两个重要组件:

    1. reactor反应器:负责查询IO事件,当检测到一个IO事件,将其发送给相应的handler处理器去处理,这里的IO事件,就是NIO中选择器监控的通道IO事件
    2. handler处理器:与IO事件绑定,负责IO事件的处理.完成真正的连接建立,通道的读取,业务处理逻辑,负责将结果写出到通道等.

    什么是单线程版本的reactor反应器模式呢?简单的说,reacotr反应器和handlers处理器处于一个线程中执行,他是最简单的反应器模型

    单线程Reactor反应器模式的确定: 单线程Reactor反应器模式中Reactor反应器和handler处理器,都执行在同一条线程上,这样带来了一个问题:当其中某个handler阻塞是,会导致其他所有的handler都得不到执行.这种场景下,如果被阻塞的handler不仅仅负责输入和输出业务的处理,还包括负责连接监听的AcceptorHandler处理器,这是个非常严重的问题.因为一旦AccpetHandler处理器阻塞,会导致整个服务不能接受新的连接,是服务变得不可用.因为这个缺陷因此单线程反应器模型用的比较少.另外目前的服务器都是多核的,单线程反应器模型不能充分利用多核资源,总之,在高性能服务器应用场景中,单线程反应器模式实际使用的很少

    多线程reactor反应器模式

    多线程池Reactor反应器的演进,分为两个方面:

    1. 首先是升级handler处理器,既要使用多线程,又要尽可能的高效率,可以考虑使用线程池
    2. 其次是升级reactor反应器,可以考虑引入多个selector选择器,提示选择大量通道的能力

    总体来说,多线程池反应器的模式,大致如下:

    1. 将负责输入输出处理的IOHandler处理器的执行,放入独立的线程池中,这样业务处理期限错与负责服务器监听和IO事件查询的反应器线程相隔离,避免服务器的连接监听受到阻塞.
    2. 如果服务器为多核的cpu,可以将反应器线程拆分为多个子反应器(subreactor)线程,同事引入多个选择器,每一个subReactor子线程负责一个选择器,这样充分释放了系统资源的能力,也提高了反应器管理大量连接,提升选择大量通道的能力

      

    反应器模式总结

    反应器模式和生产消费模式对比

    • 相似之处:在一定程度上,反应器模式有点类似生产者消费者模式,在生产消费者模式中,一个或多个生产者事件加入到一个队列中,一个=或多个消费者主动地从这个队列中提取事件来处理
    • 不同之处:反应器模式是基于查询饿,没有专门的队列去缓冲存储IO事件,查询到IO事件之后,反应器会根据不同IO选择键讲起分发给对应的Handler处理器来处理

     反应器模式和观察者模式对比

    • 相似之处在于:在反应器模式中,当查询到IO事件后,服务处理程序使用单路/多路分发策略,同步的分发这些IO事件,观察者模式也被称作发布/订阅模式,它定义了一个依赖关系,让多个观察者同时监听某一个主题,这个主题对象在状态变化时,会通知所有观察者,他们能够执行响应的处理
    • 不同之处在于:在反应器模式中,handler处理器实例和IO事件(选择键)的订阅关系,基本上是一个事件绑定到一个handler处理器,每一个IO事件被查询后,反应器会将事件分发给所绑定的handler处理器,而在观察者模式中,同一个时刻,同一个主题可以被订阅过的多个观察者处理

     反应器模式的优点: 

    • 响应快,虽然同一个反应器线程本身是同步的,但不会被单个连接的同步IO所阻塞
    • 编程相对简单,最大程度避免了复杂的多线程同步,也避免了多线程各个线程之间切换的开销
    • 可拓展,可以方便的通过增加反应器线程的个数来充分利用cpu资源

     反应器模式的缺点:

    • 反应器增加了一定的复杂性,因而具有一定的门槛,而且不易于调试
    • 反应器模式需要操作系统底层的IO多路复用的支持,如linux中的epoll如果操作系统的底层不支持IO多路复用,反应器模式不会有那么高效
    • 同一handler业务线程中,如果出现一个长时间的数据读写,会影响这个反应器中其他通道的IO处理,例如在大文件传输时,IO操作就会影响其他客户端的响应时间,因而对于这种操作还需要进一步的对反应器模式进行改进
  • 相关阅读:
    基于nginx+tomcat部署商城系统并连接数据库
    nginx防DDOS、cc、爬虫攻击
    nginx企业级优化
    基于nginx结合openssl实现https
    nginx打包成rpm
    产品运营3部曲:引量、留存、活跃
    从赢利前和赢利后分析 提高美国市场APP安装量的技巧
    APP海外优质推广渠道(三):海外ASO服务/工具汇总
    APP海外优质推广渠道(二):海外广告联盟/平台汇总
    关于O2O项目的个人看法
  • 原文地址:https://www.cnblogs.com/xiaodu9499/p/13900525.html
Copyright © 2011-2022 走看看