C10K问题是1999年一个叫Dan Kegel的美国人提出的概念,其中Cconcurrently, 10K指的是1万个网络连接, 结合起来意为如何能够做到并发处理1万个连接。

这里首先要澄清一下,并发(concurrency)和并行(parallel)虽然都是用来描述"同时"干多件事的名词,但他们是有本质区别的。并发指的是CPU通过在不同的线程之间快速切换来营造出一种多个线程同时在执行的假象, 而并行则是真正意义上的多个线程在同时运行。

对于现代操作系统来说,C10K问题的核心在于线程(或进程)无法随着连接数的增加而无休止的创建。对于web应用,我们平时最常用也是最熟悉的线程模型就是"一请求一线程"模型,但它难以解决C10K的原因是单个线程会占用较多的内存资源,内存的有限性就决定了线程的总数是有限制的。即便现在很多企业级服务器都是100G+的内存,线程切换所带来的开销也会随着线程数量的增加而增加,从而进一步限制了有效线程的数量。这样看来解决问题的唯一方法,就是想办法让单个线程能在不发生阻塞的前提下处理多个连接了。于是,在操作系统的支持下,reactor模式诞生。

Reactor模式

Reactor是一种基于事件驱动的设计模式,它可以做到只用少量的线程就能处理大量的I/O操作。简单来说,一个Reactor就是一个事件循环,执行这个循环的线程会阻塞在多路复用器的select()调用中,当感兴趣的I/O事件发生时,操作系统会让select()函数返回,同时告诉你发生了哪些事件,然后当前线程会将事件分发给事件对应的处理器(Handler)来进行处理, 处理完成后再进入下一次循环,以此类推。在这个过程中主要有以下三个核心组件:

  • Reactor

由一条线程执行的无限循环,其任务就是等待操作系统通知I/O ready事件的发生然后将这些事件分派给对应的处理器来处理。

  • Demultiplex

即多路复用器,其作用为让当前线程阻塞在等待事件发生的过程上,然后在事件发生时返回。其实多路复用起初是通讯工程中的术语,本意是让多种不同的信号在同一条物理线路上传输。在这里多路是指可同时监听多个I/O事件,复用是指对这些事件的处理复用同一条线程。前面我们在说Reactor的诞生有一个前提条件,即必须有操作系统的支持。在这里操作系统就扮演着通知应用程序的角色,具体的通知方式在不同的OS有着不同的实现,如epoll(Linux), k-queue(freeBSD), iocp(windows)等。

  • Handler

事件处理器。事件处理器首先要向多路复用器告知自己对哪些事件感兴趣,然后当这些事件发生时自身会被调用。其实Handler就是我们需要实现的业务逻辑,但需要注意的一点是,无论如何都不能阻塞Handler, 因为这会直接block整个事件循环。

现在我们来看一下Reactor是如何做到用少量线程来处理大量I/O的。假定现在已经建立了10个HTTP连接且我们想让服务器同时为这10个client服务而不是像排队一样完成前一个再处理下一个。在一请求一线程的模型下,我们需要启动10条线程来调用receive()方法并block在这个方法调用上, 这样只要有请求数据到来这些线程就会马上进行处理。不过在Reactor模式里,我们可以只使用一条线程先向Demultiplex注册一下我们对这10个连接的"读就绪"事件感兴趣并绑定对应的Handler,之后block在select()调用上; 当事件发生后(如这10个连接的读就绪事件同时发生),我们的主线程从select()中返回,主线程遍历所有的事件并调用对应的事件处理器完成业务逻辑。这样我们仅用一个线程就完成了先前10个线程才能完成的工作。当然,这里是有一些限制条件的,比如Handler中绝对不允许出现阻塞代码。但是业务逻辑难免会有像数据库查询这样的阻塞且耗时的调用, 该怎么办呢?答案是在Handler中只发起DB查询然后立即返回,发起后告知Demultiplex我们对DB查询完成的事件感兴趣, 当DB返回结果时再唤醒Handler进行后续处理。在Vert.x中,注册和等待事件ready的逻辑框架都会为我们代劳,我们只需要专注于编写Handler即可。

世面上各框架在实现Reactor时都会有很多变种,例如在Vert.x中,Reactor会有多个,其负责执行Reactor事件循环的NIO线程也会有多条, 而不是上面最简单的单Reactor模型。

从上面的讨论可以看到,使用Reactor模式并不能降低服务器对于单个请求处理的总耗时,而是能最大限度的减少线程阻塞(提高CPU利用率),从而大大减少了处理10K连接时需要的线程数量,进而提高了服务器的并发处理能力。不过这样也给程序员带来了麻烦,即我们需要为各种会block线程的操作注册各种回调,导致业务逻辑从先前线性的"一本道"变成了被迫分散在各个回调方法中。鱼和熊掌不可兼得,如果你真遇到了高并发问题,那么就只能牺牲一下代码了【Go语言除外 】。

与Proactor的区别

谈到reactor就不得不提一句proactor。这二者最根本的区别在于,Reactor中监听的是I/O就绪事件,此时数据还在操作系统的内核缓冲区,线程被唤醒后需要主动将数据从内核缓冲区读取到用户进程中; 而Proactor里用户进程监听的是I/O完成事件,即当线程被唤醒时,数据已经从内核转移到进程中了,这个过程是操作系统帮你完成的,你只需要在发起I/O时提供一个buffer, 等I/O完成时buffer已经被填满,而不需要你手动从read()中获取了。

Tomcat为什么"搞不定"高并发

首先,这不是tomcat的锅,而是servlet规范(3.0之前)和业务代码的问题。自Tomcat6开始就已经支持了JDK的NIO, 可以使用少量的线程处理大量的I/O事件,问题在于servlet和你的业务代码是同步的。也就是说,即便tomcat使用了某种黑魔法,仅用了一个线程就能搞定N个连接的创建和读写操作,但当tomcat调用servlet处理业务逻辑时仍然需要从维护的worker线程池中取一个线程来执行,这就又回到一请求一线程的模式了----只有同时启动10K条线程才能真正完成10K个请求的处理,否则后面的请求尽管已经完成了连接的建立和数据的接收,也只能是在一味的等待,等待前面的worker线程干完活才能来处理后面的请求。所以,只要servlet和你的业务代码也异步起来,Tomcat完全可以搞定C10K。只可惜,servlet3.0来的太晚了,异步编程的江山已经被Netty, Vert.x, Akka和Node.js这样的框架(工具)瓜分完毕了。

下一篇我们会介绍Vert.x中的核心组件,以及它是如何实现Reactor模式的。

swing的gui是通过何种模式进行事件响应与监听_【Vert.x准备篇2】C10K问题与Reactor模式...相关推荐

  1. history模式监听_面试题:VueRouter中的 hash 模式和 history 模式有什么区别

    面试题:VueRouter中的 hash 模式和 history 模式有什么区别 hash模式 hash 模式的路由中带有 # 号 hash 模式通过 window.onhashchange 方法监听 ...

  2. linux 无线网卡 监听模式,查看无线网卡是否支持监听模式

    查看无线网卡是否支持监听模式 在实施无线渗透测试时,通常需要将无线网卡设置为监听模式,来监听经过其网卡的所有流量.大学霸IT达人对于很多用户,都不知道如何确定自己的无线网卡是否支持监听,结果浪费大量时 ...

  3. reactor模式学习

    1.什么是reactor模式 2.reactor模式解决的问题是什么 3.reactor模式的实现方式有哪些 一. 什么是reactor模式 讲这个之前,先整体上看看,一般的服务端架构设计都是怎么做的 ...

  4. Java学习——复习 第八天 Swing程序设计、AWT绘图、事件监听

    16.Swing程序设计 1.Swing概述--Swing主要用来开发GUI程序,GUI即图形图像界面,他是应用程序提供给用户操作的图形界面,包括窗口.菜单.按钮等元素. 1.1Swing的特点--允 ...

  5. 【Netty】反应器 Reactor 模式 ( 单反应器 Reactor 单线程 | 单反应器 Reactor 多线程 )

    文章目录 一. 反应器 ( Reactor ) 模式 二. 反应器 ( Reactor ) 模式两大组件 三. 单反应器 ( Reactor ) 单线程 四. 单反应器 ( Reactor ) 单线程 ...

  6. Netty出现的原因以及多种Reactor模式

    一.原生NIO存在的问题 NIO的类库与API繁杂,需要熟练掌握Selector.ServerSocketChannel.SocketChannel.Bytebuffer等要求熟悉Java多线程编程和 ...

  7. kali linux wifi监听模式,无线渗透教程1:监听无线网络

    第一:配置管理无线网卡 1.1这里,我们使用tplink wn722n, kali linux插上即用,无需安装驱动. 1.2Vmare虚拟机配置如下: 如网卡插入到电脑后,先将虚拟机设置成桥接模式 ...

  8. 【转】第01课:生活中的监听模式——一坑爹的热水器

    用程序来模拟生活 从剧情中思考监听模式 监听模式 监听模式的模型抽象 代码框架 类图 基于框架的实现 模型说明 设计要点 推模型和拉模型 应用场景 [故事剧情] 刚刚大学毕业的 Tony 只身来到北京 ...

  9. EDA风格与Reactor模式

    来源:https://www.cnblogs.com/ivaneye/p/10129896.html 本文将探讨如下几个问题: Event-Driven架构风格的约束 EDA风格对架构属性的影响 Re ...

最新文章

  1. FPGA基础知识极简教程(9)七段数码管显示的Verilog简单设计
  2. 2019年春季学期第四周作业Compile Summarize
  3. C语言函数调用过程的汇编分析(停更)
  4. wxWidgets:创建一个 Frame
  5. Notepad++插件之ftp/sftp远程编辑功能,以及各种插件(转:http://blog.csdn.net/happy_wu/article/details/73302994)
  6. vh,vw单位你知道多少?
  7. 拷贝目录: 将D:\course拷贝到C盘根下.... 需要使用到: FileInputStream FileOutputStream
  8. 17 SD配置-企业结构-分配-分配允许的信用控制范围给公司码
  9. 使用React Router v4的嵌套路由
  10. Linux学习总结(69)——Linux 生成随机数的6种方法
  11. Java Web学习总结(32)——Java程序员最亲睐的Web框架
  12. Linux 命令(72)—— ulimit 命令(builtin)
  13. 如何对xgboost模型进行优化
  14. 计算机03年word做母亲节贺卡,怎样制作母亲节贺卡
  15. 【Python】wo ai ni python代码画画
  16. excel表格怎么恢复?常见的恢复方法
  17. 对于2019全国高速公路视频联网工作实施方案的理解:视频上云网关与省级视频云平台
  18. 第1章 开篇-为什么要做CI/CD?
  19. Swift 函数的定义及调用
  20. AprilTag视觉定位系统

热门文章

  1. 反弹模型(bounce model)----adot, H, Hdot变化图
  2. 从源码分析DEARGUI之交互添加和删除组件
  3. 手机归属地和ip定位
  4. blast | diamond 输出结果选择和解析 | 比对
  5. HDU 5836 Rubik's Cube BFS
  6. Entity Framework 6 Recipes 2nd Edition(9-1)译-用Web Api更新单独分离的实体
  7. 11g RMAN Restore archivelog用法
  8. C 文件操作库函数总结
  9. NOD32: 教育网、公网超级 病毒更新服务器
  10. MapReduce开发总结