目录
一、基础知识
应用程序的IO操作实际上不是物理设备级别的读写,而是缓存的复制。
应用程序的IO = socket IO + 文件IO,在内核缓存区和进程缓冲区进行数据交换。
数据在内核缓冲区和读写设备(网卡、磁盘)之间的交换是由操作系统内核完成的。
1.用户态和内核态
操作系统虚拟内存 = 内核空间+用户空间
用户态进程通过系统调用,让内核态进程完成调用系统资源之类的操作。
2.系统调用流程图
二、四种主要IO模型
阻塞:用户程序(发起IO请求的进程或线程)的执行状态,java 默认创建的socket是阻塞IO。
同步:用户空间主动发起IO请求。
异步:系统内核主动发起IO请求。
1.同步阻塞IO(Blocking IO)
用户空间主动发起,需要等待内核IO操作彻底完成才能返回用户空间的IO操作。在IO操作过程中,发起IO请求的用户进程或线程处于阻塞状态。
2.同步非阻塞(Non-BlockingIO, NIO)
用户空间的程序不需要等待内核IO操作彻底完成,发起IO请求后获得内核返回的状态值,就可以立即返回用户空间去执行后续指令。在IO操作过程中,发起IO请求的用户进程或线程处于非阻塞状态。
注:在Java中,把socket设置为NONBLOCK模式就是非阻塞IO。和Java的NIO不同,Java的NIO组件是基于操作系统的IO多路复用实现的。
3.IO多路复用
支持IO多路复用基于操作系统的IO操作的系统调用和select/epoll就绪查询系统调用。单个应用程序或者线程可以轮询成百上千个socket连接的就绪状态,当某个socket网络连接有IO就绪状态时就可以根据socket连接发起的read系统调用,用户线程进入阻塞状态,内核开始将数据从内核缓冲区复制到用户缓冲区,复制完成后,内核返回结果,用户线程解除阻塞读到数据,继续执行。
4.异步IO
用户线程通过系统调用向内核注册某个IO操作,内核在整个IO操作(包括数据准备、数据复制)完成后通知用户程序,用户执行后续的业务操作。在内核处理数据的过程中(包括将数据从网卡读到内核缓冲区,将内核缓冲区数据复制到用户缓冲区),用户程序都不需要阻塞。
异步IO需要操作系统内核提供支持,windows通过IOCP实现了真正的异步IO,Linux2.6也支持异步IO,但是JDK目前还不完善,Java的高并发网络应用程序大多采用的IO多路复用模型实现的,Netty框架就是。
三、支持百万级并发连接的Linux系统配置
root用户下执行“ulimit -n 1000000”命令,表示可以打开百万个文件句柄,默认时1024。只在当前用户环境下有效,断开用户会话或者用户退出linux,则变回默认值1024。
永久修改可编辑/etc/rc.local开机启动文件,添加一行“ulimit -SHn 1000000”。
要彻底解除Linux系统的最大文件打开数量的限制,可编辑/etc/security/limits.conf,加入“soft nofile 1000000 \n hard nofile 1000000", 一般在ElasticSearch和Netty安装的环境中都要这样修改。