架构师玄学之路-I/O模型
架构师是一个很神圣的职业,充满玄学之道,并不是每一个人都能够成为架构师,架构师需要具备很强的技术思维和业务思维以及产品思维,当然也很考验计算机功底,操作系统也是计算机功底的一部分。
缓冲I/O和直接I/O
在操作系统中,缓冲I/O是针对c语言,而直接I/O是针对Linux的系统API。
内存映射文件与零拷贝
内存映射文件是指应用程序虽然读写的是自己的内存,但是内存只是一个逻辑地址,实际读取的是内核缓冲区
网络I/O模型
- 阻塞和非阻塞是从函数调用角度来说的,而同步和异步是从"读写是谁完成的"角度来说的
- 阻塞:如果读写没有就绪或者读写没有完成,则该函数一直等待。
- 非阻塞:函数立即返回,然后让应用程序轮询、
- 同步:读写游应用程序完成
- 异步:读写由操作系统完成,完成之后,回调或者事件通知应用程序
- 异步I/O一定是非阻塞I/O,不存在既是异步I/O,又是阻塞的;同步I/O可能是阻塞的,也可能是非阻塞的。
- I/O多路复用(select、poll、epoll)都是同步I/O,因为read和write函数操作都是应用程序完成的,同时也是阻塞I/O,因为select、read、write的调用都是阻塞的。
Reactor模式与Proactor模式
- Reactor模式
主动模式,应用程序不断的轮询,询问操作系统或者网络框架、I/O是否就绪。Linux系统下的select、poll、epoll就属于主动模式,需要应用程序中有一个循环一直轮询;Java中的NIO也是属于这种模式,在这种模式下,实际的I/O操作还是应用程序执行的
- Proactor模式
被动模式,应用程序把read和write函数操作全部交给操作系统或者网络框架,实际的I/O操作由操作系统或者网络框架完成,之后再回调应用程序。asio库就是典型的Proactor模式
epoll的LT和ET模式
epoll支持两种模式:LT(水平触发)和ET(边缘触发)。水平触发又称为条件触发,边缘触发又称为状态触发。
水平触发:读缓冲区只要不为空,就会一直触发读事件;写缓冲区只要不满,就会一直触发写事件。
边缘触发:读缓冲区的状态,从空转为非空的时候触发一次;写缓冲区的状态,从满转为非满的时候粗发一次。比如用户发送一个大文件,把写缓冲区塞满了,之后缓存区可以写了,就会发生一次从满到不满的切换。
针对LT模式,要避免写的死循环问题,针对ET模式要避免short read问题。
在实际开发中,一般都倾向于LT模式,JAVA NIO用的就是epoll的LT模式。因为ET模式容易漏事件,一次触发如果没有处理好,就没有第二次机会了。
服务器编程的1+N+M模型
- 监听线程:负责accept事件的注册和处理。和每一个新进来的客户端建立socket连接,然后把socket连接交给I/O线程,完成任务,继续监听新的客户端
- I/O线程:负责每个socket连接上面的read/write事件的注册和实际的socket的读写。把读到的Request放入Request队列,交由Worker线程处理。
- Worker线程:纯碎的业务线程,没有socket的读写操作。对Request队列进行处理,生成Response队列,然后写入Response队列,由I/O线程再回复给客户端。
对于编程服务器程序,无论用epoll,还是Java NIO,或者基于Netty等网络框架,大体都是按照1+N+M的思路来做。
针对实际的应用场景我们了解哪些框架的实际的I/O模型呢?
- 比如Dubbo
- 比如Netty
- 比如Tomcat
- 比如Jetty