浅谈Unix I/O模型
关于I/O模型的文章比较多,参考多篇后理解上仍然不太满意,终需自己整理一次,也是编写高吞吐量高性能网络接口模块的基础。这里所说的主要针对网络I/O,近几年面对越来越大的用户请求量,如何优化这些步骤直接影响接口用户体验。
一、前言
I/O模型有几个名词的解释 (比较容易混淆):
阻塞与非阻塞:区别在于调用函数时,是否立即返回还是让线程等待。阻塞模型需要等待操作完成,而非阻塞模型则是立即返回(未准备好则返回一个错误码)。
同步与非同步:区别在于网络数据从内核拷贝到用户空间时是否需要用户线程参与等待。
UNIX 网络I/O模型分类有几种 (参考 UNIX Network Programming Volume.1.3rd.Ed):
1. Blocking I/O
2. Nonblocking I/O
3. I/O multiplexing (select and poll)
4. Signal driven I/O (SIGIO)
5. Asynchronous I/O (the POSIX aio_functions)
它们有两个独特的阶段差别 :
1. 等待数据准备好. (阻塞/非阻塞的差异)
2. 数据从内核复制到用户空间. (同步/非同步的差异)
理解上面2个阶段,对后面的解释就很容易明白。
二、I/O 模型详解
2.1 Blocking I/O
下图以UDP服务端调用 recvfrom为例描述线程等待过程 (TCP稍微复杂):
主处理线程一直阻塞到有用户数据,并且数据从内核拷贝到用户空间,也就是说主处理线程同一时间只能处理一个用户请求:
伪代码类似如下:
while (1)
{
Socket clientSock = serverSock.accept();
processRequest(clientSock);
} void processRequest(Socket clientSock)
{
read(...);
write(...);
}
在复杂的网络环境中,经常出现一个“慢速”客户端。也就是说这个客户端数据到达很慢,在TCP网络编程中,如果以此方式等待足够的数据(根据协议定义),则会严重影响到其他客户端的处理等待时间。
由此进行改善的模型就是使用线程池,伪代码如下:
while (1)
{
Socket clientSock = serverSock.accept();
threadPool.execute(new Task(processRequest(clientSock));
} void processRequest(Socket clientSock)
{
read(...);
write(...);
}
线程池模式使得主线程能处理更多的客户端,N个客户端使用M个线程(N:M),主线程不会被一个慢客户端阻塞,但是处理能力仍然是比较有限的。
2.2 Nonblocking I/O
将socket设置为非阻塞模式,告诉内核如果操作不能立即完成则返回一个错误码而不是等待,描述图如下:
如下图,主线程一直尝试调用网络函数,直到数据准备好,该模型的缺陷就是"忙等待",CPU空转浪费系统资源。此模式极少使用,在此仅用于介绍。
2.3 I/O multiplexing (多路复用)
Linux 中提供select/poll系统调用实现,线程阻塞在此方法上,监测多个socket fd(file descriptor)是否就绪,一旦有事件发生则顺序扫描具体是哪个就绪,但这样做会比较费时,模型如下:
在Linux kernel 2.6+提供了epoll实现,使用驱动方式替代顺序扫描,哪个fd有事件发生就返回哪一个(避免了select/poll的只要有一个发生就扫描全部找出是哪个,新的内核也可能对这模式实现做了一些优化)。
在ORACLE JDK有如下源码对内核做判断:
package sun.nio.ch;
import ...
public class DefaultSelectorProvider {
public static SelectorProvider create() {
...... if ("Linux".equals(str1)) {
String str2 = (String)AccessController.doPrivileged(new GetPropertyAction("os.version")); String[] arrayOfString = str2.split("\\.", 0);
if (arrayOfString.length >= 2) {
try {
int i = Integer.parseInt(arrayOfString[0]);
int j = Integer.parseInt(arrayOfString[1]);
if ((i > 2) || ((i == 2) && (j >= 6))) {
return new EPollSelectorProvider();
}
} catch (NumberFormatException localNumberFormatException) {}
}
}
return new PollSelectorProvider();
}
}
关于epoll的实现有专门文章详解,在此不做细说,另外一个epoll使用mmap内存映射避免内存复制损耗。
2.4 Asynchronous I/O (异步)
异步I/O告诉内核开始某个操作,在内核完成后(包括数据从内核拷贝到用户空间)通知我们,如下图:
在ORACLE JDK 1.7+提供异步I/O的实现AsynchronousChannel,相对于原Selector实现I/O复用模式简单很多。
在这几种模型中,只有异步I/O是用户线程不参与数据从内核拷贝到用户空间这个过程。
2.5 几种I/O模型的对比
如下图,它们在 [等待数据] 和 [从内核复制数据到用户空间] 的差异:
从上图看出,只有Asynchronous I/O不参与内核数据复制。
三、实例举例说明
3.1 针对BIO拒绝服务攻击
BIO中Boss线程处理请求后交给连接池Worker处理,但线程池有限,能轻易导致服务异常。比如针对Tomcat默认配置进行HTTP慢速攻击:
声明一个Content-Length为300的POST包,开启300个线程请求,每个请求中每秒发送1 byte。默认情况下Tomcat的200个线程将爆满,在持续攻击的这300秒内,都无法正常处理其他正常用户请求,造成服务异常。
改善方法:将连接器使用NIO处理,修改默认配置protocol为Http11NioProtocol,并且增大合适的线程数,对此类攻击有一定的缓解作用。
3.2 对外接口服务应用
如果使用Java开发对外接口服务,在对响应时延和吞吐量有一定要求的话,通常可以考虑集成Netty (NIO),如果遵循Servlet标准可考虑集成Jetty使用NIO连接器处理。在协议方面,内部服务可选择高效的私有协议和高压缩比的组件(如protobuf / kryo等),对外服务可选择HTTP+JSON。
WEB服务可考虑动静分离,html/css/js/image等文件使用Nginx (sendfile提供更高效的数据传输方式)处理,业务数据才透到后端服务中,后端服务加入缓存等方式减少响应时延。
3.3 关于JDK中一些名词解析
在JDK提供的NIO API与这里介绍的NIO(Nonblocking I/O)模型是不同的概念,JDK在1.7之前提供的Selector基于select/poll、epoll(Linux kernel > 2.6)实现I/O复用技术的非阻塞IO,JDK1.7以后提供的NIO2.0 API (如AsynchronousServerSocketChannel) 才是真正的异步I/O。
参考资料:
1. http://www.madwizard.org/programming/tutorials/netcpp/5
2. http://www.importnew.com/22019.html
3. 《UNIX Network Programming Volume.1.3rd.Edition》
4.《Netty权威指南》
浅谈Unix I/O模型的更多相关文章
- 浅谈 Unix I/O 模型
原文出处:http://miaoo.in/talk-about-unix-io-model.html 在实际应用中,数据操作通常分为输入和输出,那么以输入为例,在操作系统中,一个数据的输入通常分为以下 ...
- 浅谈 unix, linux, ios, android 区别和联系
浅谈 unix, linux, ios, android 区别和联系 网上的答案并不是很好,便从网上整理的相对专业的问答,本人很菜,大佬勿喷 UNIX 和 Linux UNIX 操作系统(尤尼斯) ...
- 浅谈Java的内存模型以及交互
本文的内存模型只写虚拟机内存模型,物理机的不予描述. Java内存模型 在Java中,虚拟机将运行时区域分成6中,如下图: 程序计数器:用来记录当前线程执行到哪一步操作.在多 ...
- 转:浅谈UNIX下Apache的MPM及httpd.conf配置文件中相关参数配置
为什么要并发处理 以Apache为代表的web服务器中,如果不支持并发,则在一个客户端连接的时候,如果该客户端的任务没有处理完,其他连接的客户端将会一直处于等待状态,这事不可想象的,好像没有为什么要不 ...
- 浅谈 css3 box盒子模型以及box-flex的使用
display:box;box-flex是css3新添加的盒子模型属性,它的出现可以解决我们通过N多结构.css实现的布局方式.经典的一个布局应用就是布局的垂直等高.水平均分.按比例划分. 一.使 ...
- 浅谈异步IO各模型优缺点
本文只讨论OverLapped I/O的三种异步模型及完成端口,像select.SWASelect不作讨论,讨论顺序从劣到优,方便于循序渐进地对比,更容易区分各模型之间的差别. 1. OverLapp ...
- 浅谈OSI七层模型及ICP/IP四层模型
1.OSI七层模型的概念 在网络历史的早期,国际标准化组织(ISO)和国际电报电话咨询委员会(CCITT)共同出版了开放系统互联的七层参考模型. 一台计算机操作系统中的网络过程包括从应用请求(在协议栈 ...
- 【转】从Mac/OS和iOS开放源码浅谈UNIX家谱
阅读数:1245 苹果公司在各类开源项目中长期贡献着自己的力量,但其UNIX系统技术一直都属于闭源阵营(这一点可以从NUX OS阵营和家谱图中得到答案).然而,以封闭闻名的苹果公司,2017年国庆期间 ...
- 【转】浅谈Node.js单线程模型
Node.js采用 事件驱动 和 异步I/O 的方式,实现了一个单线程.高并发的运行时环境,而单线程就意味着同一时间只能做一件事,那么Node.js如何利用单线程来实现高并发和异步I/O?本文将围绕这 ...
随机推荐
- Golang 任务队列策略 -- 读《JOB QUEUES IN GO》
Golang 在异步处理上有着上佳的表现.因为 goroutines 和 channels 是非常容易使用且有效的异步处理手段.下面我们一起来看一看 Golang 的简易任务队列 一种"非任 ...
- 在VirtualBox 虚拟机中安装CentOS7 64位实验基础系统
1.将CentOS-7-x86_64-Minimal ISO加载入虚拟机,选择安装CentOS 7 2.启动欢迎画面,保持默认,选择 继续 3.安装参数设置 3-1.设置时区以便同步时间,将时区更改为 ...
- 74、django之ajax补充
之前的ajax使用都是依据jquery来使用的,本篇会先分析ajax的原生的js代码实现,还有jsonp的介绍,与OMR的一些遗漏补充. 本篇导航: js实现的ajax 同源策略与Jsonp 一.js ...
- 编程语言的基础——搞定JavaIO
关键字:IO基础,JUnit生命周期,字节流,字符流,字符编码,对象流,序列化,反序列化 Java I/O 流是一组有顺序的,有起点和终点的字节集合.是对设备文件间数据传输的总称和抽象. 在IO中涉及 ...
- K-means 算法
本学习笔记参考自吴恩达老师机器学习公开课 聚类算法是一种无监督学习算法.k均值算法是其中应用最为广泛的一种,算法接受一个未标记的数据集,然后将数据聚类成不同的组.K均值是一个迭代算法,假设我们想要将数 ...
- P1373 小a和uim之大逃离
转自:http://www.cnblogs.com/CtsNevermore/p/6028138.html 题目背景 小a和uim来到雨林中探险.突然一阵北风吹来,一片乌云从北部天边急涌过来,还伴着一 ...
- 十招让Ubuntu 16.04用起来更得心应手(转)
ubuntu 16.04是一种长期支持版本(LTS),是Canonical承诺发布五年的更新版.也就是说,你可以让这个版本在电脑上运行五年! 这样一来,一开始就设置好显得特别重要.你应该确保你的软件是 ...
- ThreadLocal中的WeakReference
在一般的网站开发中,基于Java的Web 框架都使用了ThreadLocal来存储一些全局的参数,在拦截器\Filter中设置变量,让变量可以在任意地方被获取. 一早就了解到里面有用到WeakRefe ...
- 初识CSS
css解释 css样式: css是英文Cascading Style Sheets的缩写,称为层叠样式表,用于对页面进行美化,CSS的可以使页面更加的美观.基本上所有的html页面都或多或少的使用cs ...
- 【GISer&&Painter】GISer
基于上一篇OpenGL的渲染原理,这两周又陆续接触了一些关于WebGL绘图的一些内容,因为刚入门,很多东西又很晦涩,所以特意花了小半天的时间整理了一下,特此记录. 一 画布和画笔:创建Canvas ...