线程模型

线程模型有传统阻塞I/O服务器模型和Reactor模型(网络主流模型)

根据Reactor的数量和处理资源池线程的数量不同,有3种典型的实现:

  • 单Reactor单线程
  • 单Reactor多线程
  • 主从Reactor多线程

Netty基于主从Reactor多线程模型,并做了一些改进,其中主从Reactor多线程有多个Reactor

单线程Reactor模型

Java的NIO模式的Selector网络通讯,其实就是一个简单的Reactor模型。可以说是Reactor模型的朴素原型

实际上的Reactor模式,是基于Java NIO的,在他的基础上,抽象出来两个组件——ReactorHandler两个组件:

(1)Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理;新的事件包含连接建立就绪、读就绪、写就绪等。

(2)Handler:将自身(handler)与事件绑定,负责事件的处理,完成channel的读入,完成处理业务逻辑后,负责将结果写出channel。

可以将上图的accepter,看做是一种特殊的handler。

class Reactor implements Runnable
{final Selector selector;final ServerSocketChannel serverSocket;Reactor(int port) throws IOException{ //Reactor初始化selector = Selector.open();serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(port));//非阻塞serverSocket.configureBlocking(false);//分步处理,第一步,接收accept事件SelectionKey sk =serverSocket.register(selector, SelectionKey.OP_ACCEPT);//attach callback object, Acceptorsk.attach(new Acceptor());}public void run(){try{while (!Thread.interrupted()){selector.select();Set selected = selector.selectedKeys();Iterator it = selected.iterator();while (it.hasNext()){//Reactor负责dispatch收到的事件dispatch((SelectionKey) (it.next()));}selected.clear();}} catch (IOException ex){ /* ... */ }}void dispatch(SelectionKey k){Runnable r = (Runnable) (k.attachment());//调用之前注册的callback对象if (r != null){r.run();}}// inner classclass Acceptor implements Runnable{public void run(){try{SocketChannel channel = serverSocket.accept();if (channel != null)new Handler(selector, channel);} catch (IOException ex){ /* ... */ }}}
}
class Handler implements Runnable
{final SocketChannel channel;final SelectionKey sk;ByteBuffer input = ByteBuffer.allocate(SystemConfig.INPUT_SIZE);ByteBuffer output = ByteBuffer.allocate(SystemConfig.SEND_SIZE);static final int READING = 0, SENDING = 1;int state = READING;Handler(Selector selector, SocketChannel c) throws IOException{channel = c;c.configureBlocking(false);// Optionally try first read nowsk = channel.register(selector, 0);//将Handler作为callback对象sk.attach(this);//第二步,注册Read就绪事件sk.interestOps(SelectionKey.OP_READ);selector.wakeup();}boolean inputIsComplete(){/* ... */return false;}boolean outputIsComplete(){/* ... */return false;}void process(){/* ... */return;}public void run(){try{if (state == READING){read();}else if (state == SENDING){send();}} catch (IOException ex){ /* ... */ }}void read() throws IOException{channel.read(input);if (inputIsComplete()){process();state = SENDING;// Normally also do first write now//第三步,接收write就绪事件sk.interestOps(SelectionKey.OP_WRITE);}}void send() throws IOException{channel.write(output);//write完就结束了, 关闭select keyif (outputIsComplete()){sk.cancel();}}
}

单线程模式的缺点:

1、 当其中某个 handler 阻塞时, 会导致其他所有的 client 的 handler 都得不到执行, 并且更严重的是, handler 的阻塞也会导致整个服务不能接收新的 client 请求(因为 acceptor 也被阻塞了)。 因为有这么多的缺陷, 因此单线程Reactor 模型用的比较少。这种单线程模型不能充分利用多核资源,所以实际使用的不多。

2、因此,单线程模型仅仅适用于handler 中业务处理组件能快速完成的场景。

多线程线程Reactor模型

基于线程池的改进

在线程Reactor模式基础上,做如下改进:

(1)将Handler处理器的具体执行放入线程池,多线程进行业务处理

(2)而对于Reactor而言,可以仍为单个线程。如果服务器为多核的CPU,为充分利用系统资源,可以将Reactor拆分为两个线程。

class MthreadHandler implements Runnable
{final SocketChannel channel;final SelectionKey selectionKey;ByteBuffer input = ByteBuffer.allocate(SystemConfig.INPUT_SIZE);ByteBuffer output = ByteBuffer.allocate(SystemConfig.SEND_SIZE);static final int READING = 0, SENDING = 1;int state = READING;ExecutorService pool = Executors.newFixedThreadPool(2);static final int PROCESSING = 3;MthreadHandler(Selector selector, SocketChannel c) throws IOException{channel = c;c.configureBlocking(false);// Optionally try first read nowselectionKey = channel.register(selector, 0);//将Handler作为callback对象selectionKey.attach(this);//第二步,注册Read就绪事件selectionKey.interestOps(SelectionKey.OP_READ);selector.wakeup();}boolean inputIsComplete(){/* ... */return false;}boolean outputIsComplete(){/* ... */return false;}void process(){/* ... */return;}public void run(){try{if (state == READING){read();}else if (state == SENDING){send();}} catch (IOException ex){ /* ... */ }}synchronized void read() throws IOException{// ...channel.read(input);if (inputIsComplete()){state = PROCESSING;//使用线程pool异步执行pool.execute(new Processer());}}void send() throws IOException{channel.write(output);//write完就结束了, 关闭select keyif (outputIsComplete()){selectionKey.cancel();}}synchronized void processAndHandOff(){process();state = SENDING;// or rebind attachment//process完,开始等待write就绪selectionKey.interestOps(SelectionKey.OP_WRITE);}class Processer implements Runnable{public void run(){processAndHandOff();}}}

Reactor 类没有大的变化,参考前面的代码。

优点:

  • 多线程可以充分利用多核CPU的处理能力
  • 采用线程池复用线程,减少创建和销毁线程带来的性能开销

缺点:

  • reactor处理所有事件的监听和响应,在单线程运行,高并发场景下容易出现性能瓶颈
  • 多线程数据共享和访问比较复杂

多Reactor多线程模型

方案说明:

  • Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件
  • 当Acceptor处理连接事件后,MainReactor将连接分配给SubReactor
  • SubReactor将连接加入到连接队列进行监听,并创建handler进行各种事件处理
  • 当有新事件发生时,SubReactor就会调用对应的handler进行处理
  • handler通过Read读取数据,分发给后面的worker线程处理
  • worker线程池会分配独立的worker线程进行业务处理,并返回结果
  • handler收到响应的结果后,再通过send将结果返回给client
  • MainReactor主线程可以关联多个SubReactor子线程

mainReactor负责监听server socket,用来处理新连接的建立,将建立的socketChannel指定注册给subReactor。

subReactor维护自己的selector, 基于mainReactor 注册的socketChannel多路分离IO读写事件,读写网 络数据,对业务处理的功能,另其扔给worker线程池来完成。

就是上一篇的Boss和Worker

Netty基础 NIO SocketChannel 网络编程 自制小型服务器,多线程优化*_清风拂来水波不兴的博客-CSDN博客

class MthreadReactor implements Runnable
{//subReactors集合, 一个selector代表一个subReactorSelector[] selectors=new Selector[2];int next = 0;final ServerSocketChannel serverSocket;MthreadReactor(int port) throws IOException{ //Reactor初始化selectors[0]=Selector.open();selectors[1]= Selector.open();serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(port));//非阻塞serverSocket.configureBlocking(false);//分步处理,第一步,接收accept事件SelectionKey sk =serverSocket.register( selectors[0], SelectionKey.OP_ACCEPT);//attach callback object, Acceptorsk.attach(new Acceptor());}public void run(){try{while (!Thread.interrupted()){for (int i = 0; i <2 ; i++){selectors[i].select();Set selected =  selectors[i].selectedKeys();Iterator it = selected.iterator();while (it.hasNext()){//Reactor负责dispatch收到的事件dispatch((SelectionKey) (it.next()));}selected.clear();}}} catch (IOException ex){ /* ... */ }}void dispatch(SelectionKey k){Runnable r = (Runnable) (k.attachment());//调用之前注册的callback对象if (r != null){r.run();}}class Acceptor { // ...public synchronized void run() throws IOException{SocketChannel connection =serverSocket.accept(); //主selector负责acceptif (connection != null){new Handler(selectors[next], connection); //选个subReactor去负责接收到的connection}if (++next == selectors.length) next = 0;}}
}

6.1. 优点

1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;

2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;

3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;

4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;

6.2. 缺点

1)相比传统的简单模型,Reactor增加了一定的复杂性,因而有一定的门槛,并且不易于调试。

2)Reactor模式需要底层的Synchronous Event Demultiplexer支持,比如Java中的Selector支持,操作系统的select系统调用支持,如果要自己实现Synchronous Event Demultiplexer可能不会有那么高效。

3) Reactor模式在IO读写数据时还是在同一个线程中实现的,即使使用多个Reactor机制的情况下,那些共享一个Reactor的Channel如果出现一个长时间的数据读写,会影响这个Reactor中其他Channel的相应时间,比如在大文件传输时,IO操作就会影响其他Client的相应时间,因而对这种操作,使用传统的Thread-Per-Connection或许是一个更好的选择,或则此时使用改进版的Reactor模式如Proactor模式。

Netty线程模型 Reactor模型相关推荐

  1. 简单深入理解高性能网络编程(Netty)中的Reactor模型(图文+代码)

    文章目录 定义 传统网络交互方式 Reactor 模型组成 Netty中`Reactor`模型的实现 Reactor 单线程模式 非主从Reactor模式(单Reactor多线程模型) 主从React ...

  2. IO线程模型Reactor模型

    概述 前言: 要对IO模型.多路复用.java NIO网络编程有一定的理解才能看懂. 不同的线程模型,对程序的性能影响很大,要搞钱Netty的线程模型,就要了解一下各种线程模型. 目前存在的线程模型有 ...

  3. 基础服务器 IO 模型 Proactor 模型 Reactor 模型 IO 多路复用 异步 IO 模型 Linux 服务器开发 网络编程服务器模型

    本文主要记录服务器的 IO 模型的类型(从多路复用,异步 IO 讲到 Proactor Reactor 模型),包括 Real World nginx 和 apache ,kafka 等分析,配备自洽 ...

  4. IO多路复用和Reactor模型

    目录 1.同步阻塞式IO - BIO 2.同步非阻塞式IO - NIO 2.1 Selector 选择器 2.1.1 SelectionKey 2.1.2 Selector注册事件类型 2.2 Cha ...

  5. Reactor模型详解

    文章目录 前言 一.经典的同步阻塞模型 1.下面看传统的BIO代码: 2.client端代码: 3.因此,我们要引出我们今日的主角: reactor 二.单reactor单线程设计 代码示例 三.单r ...

  6. Reactor模型 介绍

    以下内容转载自 https://www.toutiao.com/i6808729994718609934/ 要无障碍阅读本文,需要对NIO有一个大概的了解,起码要可以写一个NIO的Hello Worl ...

  7. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  8. Reactor三种线程模型与Netty线程模型

    一.Reactor三种线程模型 1.1.单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常.关闭等等.单线程Reactor模型基于同步事件分离器来分发事件,这个同步事件 ...

  9. 为什么说Netty是性能之王,因为它用了 Reactor 模型啊

    点击关注公众号,Java干货及时送达 本文将介绍基于进程/线程模型,服务器如何处理请求.值得说明的是,具体选择线程还是进程,更多是与平台及编程语言相关. 例如 C 语言使用线程和进程都可以(例如 Ng ...

最新文章

  1. Xiki:一个开发人员寻求增强命令行界面的能力
  2. 最老程序员创业札记:全文检索、数据挖掘、推荐引擎应用29
  3. python dataframe数据类型_python-Pandas DataFrame,1、2、3和NaN值的默认数据类型
  4. 基于Delphi的Socket I/O模型全接触
  5. python基础知识面试题-基础篇--【python】面试题汇总
  6. 微服务网关Kong 1.0正式发布!提供100+项功能
  7. [html] webp与jpg、png比较,它有什么优劣势?如何选择?
  8. 【POJ - 1087】A Plug for UNIX(建图,网络流最大流)
  9. Verilog hdl 宏定义编译报错
  10. 微信小程序中相机api_微信小程序 Image API实例详解
  11. RestFul的学习 2021-04-25
  12. 跳转html时请求头怎么取,如何获取a链接的请求头信息?
  13. linux内核nvme驱动程序,Linux中nvme驱动详解
  14. 155款安卓开源项目源码整理+20个Android必备第三方框架
  15. 四种JAVA架构演进史,程序员能学会最后一种就非常厉害了,至少50k
  16. 海洋cms v6.53 v6.54版本漏洞复现
  17. jtopo node.text换行_JTopo 使用
  18. odac与oracle,适用于 Windows 的 64 位 Oracle Data Access Components (ODAC) | Oracle 中国
  19. Mysql按时间区段(每隔30分钟)统计数据并展示
  20. Ubuntu安装Gcc时,显示“无法解析域名cn.archive.ubuntu.com”,如下方式可解决

热门文章

  1. Linux Command date 显示时间
  2. Android性能测试手段和工具
  3. 7、破解windows系统密码
  4. 一个好的IT博客是怎么选择出来的???
  5. CSS Modules和Styled Components
  6. CF 1716 C. Robot in a Hallway 思维 2000
  7. redis快速启动方法
  8. intel realsense 深度视野范围与模组硬件说明
  9. Linux的网络命令
  10. css滑动门技术的应用,DIV+CSS滑动门技术简介