网路的几种IO模型以及Nginx基本原理

1 网络IO模型

1.1 网络IO基本概念理解

IO分别表示输入(input)和输出(output)。它描述的是计算机的数据流动的过程,所以IO第一大特征是有数据的流动;那么对于IO的整个过程大致上分为2个部分,第一个部分为IO的调用,第二个过程为IO的执行。IO的调用指的就是系统调用,IO的执行指的是在内核中相关数据的处理过程,这个过程是由操做系统完成的,与程序员无关。php

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,目前支持I/O多路复用的系统调用有selectpollepoll,I/O多路复用就是经过一种机制,一个进程能够监视多个描述符(socket),一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读写操做。java

描述符(socket)在windows中能够叫作句柄。咱们能够理解成一个文件对应的ID。IO其实就是对

1.2 对同步 异步 阻塞 非阻塞在网络中的理解

能够先看之前写的文章:对同步 异步 阻塞 非阻塞在网络中的理解linux

阻塞IO:请求进程一直等待IO准备就绪。
非阻塞IO:请求进程不会等待IO准备就绪。
同步IO操做:致使请求进程阻塞,直到IO操做完成。
异步IO操做:不致使请求进程阻塞。nginx

举个小例子来理解阻塞,非阻塞,同步和异步的关系,咱们知道编写一个程序能够有多个函数,每个函数的执行都是相互独立的;可是,对于一个程序的执行过程,每个函数都是必须的,那么若是咱们须要等待一个函数的执行结束而后返回一个结果(好比接口调用),那么咱们说该函数的调用是阻塞的,对于至少有一个函数调用阻塞的程序,在执行的过程当中,一定存在阻塞的一个过程,那么咱们就说该程序的执行是同步的,对于异步天然就是全部的函数执行过程都是非阻塞的。

这里的程序就是一次完整的IO,一个函数为IO在执行过程当中的一个独立的小片断。程序员

咱们知道在Linux操做系统中内存分为内核空间用户空间,而全部的IO操做都得得到内核的支持,可是因为用户态的进程没法直接进行内核的IO操做,因此内核空间提供了系统调用,使得处于用户态的进程能够间接执行IO操做,IO调用的目的是将进程的内部数据迁移到外部即输出,或将外部数据迁移到进程内部即输入。而在这里讨论的数据一般是socket进程内部的数据。web

1.3 五种IO模型

基本原理

image.png
在上图中,每个客户端会与服务端创建一次socket链接,而服务端获取链接后,对于全部的数据的读取都得通过操做系统的内核,经过系统调用内核将数据复制到用户进程的缓冲区,而后才完成客户端的进程与客户端的交互。那么根据系统调用的方式的不一样分为阻塞和非阻塞,根据系统处理应用进程的方式不一样分为同步和异步。segmentfault

模型1:阻塞式IO

image.png
每一次客户端产生的socket链接其实是一个文件描述符fd,而每个用户进程读取的实际上也是一个个文件描述符fd,在该时期的系统调用函数会等待网络请求的数据的到达和数据从内核空间复制到用户进程空间,也就是说,不管是第一阶段的IO调用仍是第二阶段的IO执行都会阻塞,那么就像图中所画的同样,对于多个客户端链接,只能开辟多个线程来处理。windows

模型2:非阻塞IO模型

image.png
对于阻塞IO模型来讲最大的问题就体如今阻塞2字上,那么为了解决这个问题,系统的内核所以发生了改变。在内核中socket支持了非阻塞状态。既然这个socket是不阻塞的了,那么就可使用一个进程处理客户端的链接,该进程内部写一个死循环,不断的询问每个链接的网络数据是否已经到达。此时轮询发生在用户空间,可是该进程依然须要本身处理全部的链接,因此该时期为同步非阻塞IO时期,也即为NIO。后端

模型3:IO多路复用

在非阻塞IO模型中,虽然解决了IO调用阻塞的问题,可是产生了新的问题,若是如今有1万个链接,那么用户线程会调用1万次的系统调用read来进行处理,在用户空间这种开销太大,那么如今须要解决这个问题,思路就是让用户进程减小系统调用,可是用户本身是实现不了的,因此这就致使了内核发生了进一步变化。在内核空间中帮助用户进程遍历全部的文件描述符,将数据准备好的文件描述符返回给用户进程。该方式是同步阻塞IO,由于在第一阶段的IO调用会阻塞进程。数组

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,目前支持I/O多路复用的系统调用有selectpollepoll
,I/O多路复用就是经过一种机制,一个进程能够监视多个描述符(socket),一旦某个描述符就绪(通常是读就绪或者写就绪),可以通知程序进行相应的读
写操做。

select/poll

image.png
为了让内核帮助用户进程完成文件描述符的遍历,内核增长了系统调用select/poll(select与poll本质上没有什么不一样,就是poll减小了文件描述符的个数限制),如今用户进程只须要调用select系统调用函数,而且将文件描述符所有传递给select就可让内核帮助用户进程完成全部的查询,而后将数据准备好的文件描述符再返回给用户进程,最后用户进程依次调用其余系统调用函数完成IO的执行过程。

epoll

在select实现的多路复用中依然存在一些问题。

一、用户进程须要传递全部的文件描述符,而后内核将数据准备好的文件描述符再次传递回去,这种数据的拷贝下降了IO的速度。
二、内核依然会执行复杂度为O(n)的主动遍历操做。

对于第一个问题,提出了一个共享空间的概念,这个空间为用户进程和内核进程所共享,而且提供了mmap系统调用,实现用户空间和内核空间到共享空间的映射,这样用户进程就能够将1万个文件描述符写到共享空间中的红黑树上,而后内核将准备就绪的文件描述符写入共享空间的链表中,而用户进程发现链表中有数据了就直接读取而后调用read执行IO便可。

对于第二个问题,内核引入了事件驱动机制(相似于中断),再也不主动遍历全部的文件描述符,而是经过事件驱动的方式主动通知内核该文件描述符的数据准备完毕了,而后内核就将其写入链表中便可。
image.png
对于epoll来讲在第一阶段的epoll_wait依然是阻塞的,故也是同步阻塞式IO。

模型4:信号驱动式IO

在IO执行的数据准备阶段,不会阻塞用户进程。当用户进程须要等待数据的时候,会向内核发送一个信号,告诉内核须要数据,而后用户进程就继续作别的事情去了,而当内核中的数据准备好以后,内核立马发给用户进程一个信号,用户进程收到信号以后,立马调用recvfrom,去查收数据。该IO模型使用的较少。
image.png

模型5:异步IO(AIO)

应用进程经过 aio_read 告知内核启动某个操做,而且在整个操做完成以后再通知应用进程,包括把数据从内核空间拷贝到用户空间。信号驱动 IO 是内核通知咱们什么时候能够启动一个 IO 操做,而异步 IO 模型是由内核通知咱们 IO 操做什么时候完成。是真正意义上的无阻塞的IO操做,可是目前只有windows支持AIO,linux内核暂时不支持。
image.png

总结

前四种模型的主要区别于第一阶段,由于他们的第二阶段都是同样的:在数据从内核拷贝到应用进程的缓冲区期间,进程都会阻塞。相反,异步 IO 模型在这两个阶段都不会阻塞,从而不一样于其余四种模型。
image.png

直接内存与零拷贝

直接内存并非虚拟机运行时数据区的一部分,也不是Java 虚拟机规范中农定义的内存区域。直接内存申请空间耗费更高的性能,直接内存IO读写的性能要优于普通的堆内存,对于java程序来讲,系统内核读取堆类的对象须要根据代码段计算其偏移量来获取对象地址,效率较慢,不太适合网络IO的场景,对于直接内存来讲更加适合IO操做,内核读取存放在直接内存中的对象较为方便,由于其地址就是裸露的进程虚拟地址,不须要jvm翻译。那么就可使用mmap开辟一块直接内存mapbuffer和内核空间共享,而且该直接内存能够直接映射到磁盘上的文件,这样就能够经过调用本地的put而不用调用系统调用write就能够将数据直接写入磁盘,RandomAccessFile类就是经过开辟mapbuffer实现的读写磁盘。

以消息队列Kafka来讲,有生产者和消费者,对于生产者,从网络发来一个消息msg而且被拷贝到内核缓冲区,该消息经过Kafka调用recvfrom将内核中的msg读到队列中,而后加上消息头head,再将该消息写入磁盘。若是没有mmap的话,就会调用一个write系统调用将该消息写入内核缓冲区,而后内核将该消息再写入磁盘。在此过程当中出现一次80中断和2次拷贝。但实际上Kafka使用的是mmap开辟了直接内存到磁盘的映射,直接使用put将消息写入磁盘。实际上也是经过内核访问该共享区域将该消息写入的磁盘。同时在Kafka中有一个概念叫segment,通常为1G大小。它会充分利用磁盘的顺序性,只追加数据,不修改数据。而mmap会直接开辟1G的直接内存,而且直接与segment造成映射关系,在segment满了的时候再开辟一个新的segment,清空直接内存而后在与新的segment造成映射关系。
clipboard.png

零拷贝描述的是CPU不执行拷贝数据从一个存储区域到另外一个存储区域的任务,这一般用于经过网络传输一个文件时以减小CPU周期和内存带宽。

在Kafka的消费者读取数据的时候,若是当前消费者想读取的数据是否是当前直接内存所映射的segment怎么办?若是没有零拷贝的话,进程会先去调用read读取,而后数据会从磁盘被拷贝到内核,而后内核再拷贝到Kafka队列,进程再调用write将数据拷贝到内核缓冲区,最后再发送给消费者。实际上能够发现,数据没有必要读到Kafka队列,直接读到内核的缓冲区的时候发送给消费者就好了。实际上,linux内核中有一个系统调用就是实现了这种方式读取数据——sendfile,它有2个参数,一个是infd(读取数据的文件描述符),一个是outfd(客户端的socket文件描述符).消费者只需调用该函数,告诉它须要读取那个文件就能够不通过Kafka直接将数据读到内核,而后由内核写到消费者进程的缓冲区中。
clipboard.png

2 nginx原理了解

2.1 什么是nginx

Nginx 是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时也是一个IMAP、POP三、SMTP代理服务器; Nginx 能够做为一个HTTP服务器进行网站的发布处理,另外 Nginx 能够做为反向代理进行负载均衡的实现。

2.1.1 nginx的三个主要应用场景

一、静态资源服务(经过本地文件系统提供服务)
二、缓存、负载均衡服务器
三、API服务(OpenResty)
image.png

2.2 为何选择nginx?

  1. 更快
    这表如今两个方面:一方面,在正常状况下,单次请求会获得更快的响应;另外一方面, 在高峰期(若有数以万计的并发请求), Nginx 能够比其余Web服务器更快地响应请求。
  2. 高扩展性
    Nginx 的设计极具扩展性,它彻底是由多个不一样功能、不一样层次、不一样类型且耦合度极低的模块组成。所以,当对某一个模块修复 Bug 或进行升级时,能够专一于模块自身,无须在乎其余。
    并且在HTTP模块中,还设计了 HTTP 过滤器模块:一个正常的 HTTP 模块在处理完请求后,会有一串 HTTP 过滤器模块对请求的结果进行再处理。这样,当咱们开发一个新的 HTTP 模块时,不但可使用诸如 HTTP 核心模块、events模块、log模块 等不一样层次或者不一样类型的模块,还能够原封不动地复用大量已有的 HTTP 过滤器模块。
    这种低耦合度的优秀设计,造就了 Nginx 庞大的第三方模块,固然,公开的第三方模块也如官方发布的模块同样容易使用。
    Nginx 的模块都是嵌入到二进制文件中执行的,不管官方发布的模块仍是第三方模块都是如此。这使得第三方模块同样具有极其优秀的性能,充分利用 Nginx的高并发特性,所以,许多高流量的网站都倾向于开发符合本身业务特性的定制模块。
  3. 高可靠性
    高可靠性是咱们选择 Nginx 的最基本条件,由于 Nginx 的可靠性是你们有目共睹的,不少家高流量网站都在核心服务器上大规模使用 Nginx 。 Nginx 的高可靠性来自于其核心框架代码的优秀设计、模块设计的简单性;另外,官方提供的经常使用模块都很是稳定,每一个 worker 进程相对独立,master进程在1个worker进程出错时能够快速“拉起”新的 worker 子进程提供服务。
  4. 低内存消耗
    通常状况下,10000个非活跃的HTTP Keep-Alive链接在 Nginx 中仅消耗2.5MB的内存,这是 Nginx 支持高并发链接的基础。
  5. 单机支持10万以上的并发链接
    这是一个很是重要的特性!随着互联网的迅猛发展和互联网用户数量的成倍增加,各大公司、网站都须要应付海量并发请求,一个可以在峰值期顶住10万以上并发请求的Server, 无疑会获得你们的青睐。理论上,Nginx支持的并发链接上限取决于内存,固然,可以及时地处理更多的并发请求,是与业务特色紧密相关的。
  6. 热部署
    master管理进程与 worker 工做进程的分离设计,使得 worker 可以提供热部署功能,便可以在7×24小时不间断服务的前提下,升级 worker 的可执行文件。固然,它也支持不中止服务就更新配置项、更换日志文件等功能。
  7. 最自由的BSD许可协议
    这是 worker 能够快速发展的强大动力。BSD许可协议不仅是容许用户无偿使用 worker ,它还容许用户在本身的项目中直接使用或修改 worker 源码,而后发布。这吸引了无数开发者继续为 worker 贡献本身的智慧。

2.3 Nginx相关的开源版本

一、阿里巴巴Tengine Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了不少高级功能和特性。
二、openresty OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建可以处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

2.4 Nginx高效的缘由及原理解析

2.4.1 服务器的网络服务模型

web服务器和客户端是一对多的关系,Web服务器必须有能力同时为多个客户端提供服务。通常来讲完成并行处理请求工做有三种方式:

2.4.1.1 单进程阻塞的网络服务器

image.png
说明:

一、建立一个socket,绑定服务器端口(bind),监听端口(listen),在PHP中用stream_socket_server一个函数就能完成上面3个步骤
二、进入while循环,阻塞在accept操做上,等待客户端链接进入。此时程序会进入睡眠状态,直到有新的客户端发起connect到服务器,操做系统会唤
醒此进程。accept函数返回客户端链接的socket
三、利用fread读取客户端socket当中的数据收到数据后服务器程序进行处理而后使用fwrite向客户端发送响应。长链接的服务会持续与客户端交互,
而短链接服务通常收到响应就会close。
缺点: 一次只能处理一个链接,不支持多个长链接同时处理

2.4.1.2 多进程方式

多进程方式指,服务器每当收到一个客户端请求时,就有服务器主进程生成一个子进程出来和客户端创建链接进行交互,直到链接断开该子进程就结束了。
image.png
说明:

前面流程一致就不补充了
一、程序启动后就会建立N个进程。每一个子进程进入 Accept,等待新的链接进入。当客户端链接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,而且再也不接受新的TCP链接。
    当链接关闭时,子进程会释放,从新进入 Accept,参与处理新的链接。
    这个模型的优点是彻底能够复用进程,不须要太多的上下文切换,好比php-fpm基于此模型来处理解析php.
多进程方式的优势是设计简单,各个子进程相对独立,处理客户端请求时彼此不受干扰;

缺点是操做系统生成一个子进程须要进行内存复制等操做,在资源和时间上会产生必定的开销;当有大量请求时,会致使系统性能降低;
例如:即时聊天程序,一台服务器可能要维持数十万的链接,那么就要启动数十万的进程来维持。这显然不可能

2.4.1.3 多线程方式

多线程方式指每当服务器接收到一个请求后,会由服务器主进程派生出一个线程出来和客户端进行交互。因为操做系统产生出一个线程的开销远远小于一个进程的开销。故多线程方式在很大程度上减轻了Web服务器对系统资源的要求。

缺点:稳定性!假设某个进程忽然关闭会形成整个进程中的全部线程都崩溃。

基于上面的模式咱们发现单个进程每次只能经过每次(accept)处理单个请求,有没有办法一次性链接多个请求,须要的时候再处理呢?

2.4.1.4 单进程IO复用方式

image.png
说明:

一、保存全部的socket,经过select模型,监听socket描述符的可读事件
二、Select会在内核空间监听一旦发现socket可读,会从内核空间传递至用户空间,在用户空间经过逻辑判断是服务端socket可读,仍是客户端
的socket可读
三、若是是服务端的socket可读,说明有新的客户端创建,将socket保留到监听数组当中
四、若是是客户端的socket可读,说明当前已经能够去读取客户端发送过来的内容了,经过fread读取socket内容,而后fwrite响应给客户端。
优势:性能最好!一个进程或线程处理多个请求,不须要额外开销,性能最好,资源占用最低。

缺点:稳定性!某个进程或线程出错,可能致使大量请求没法处理,甚至致使整个服务宕机,单进程对于大量任务处理乏力。

2.4.1.5 多进程的master-workerIO复用方式

2.4.1.5.1 nginx的基本架构

image.png

1.Nginx启动后,会产生一个主进程,主进程执行一系列的工做后会产生一个或者多个工做进程
2.在客户端请求动态站点的过程当中,Nginx服务器还涉及和后端服务器的通讯。Nginx将接收到的Web请求经过代理转发到后端服务器,由后端服务器进行
数据处理和组织;
3.Nginx为了提升对请求的响应效率,下降网络压力,采用了缓存机制,将历史应答数据缓存到本地。保障对缓存文件的快速访问

master进程主要用来管理 worker 进程,具体包括如下主要功能:

(1)接收来自外界的信号。
(2)处理配置文件读取。
(3)建立,绑定和关闭套接字
(4)启动,终止和维护配置的工做(worker)进程数
(5)当woker进程退出后(异常状况下),会自动从新启动新的woker进程

image.png
worker进程的主要任务是完成具体的任务逻辑。其主要关注点是与客户端或后端真实服务器(此时 worker做为中间代理)之间的数据可读/可写等I/O交互事件。

(1)接收客户端请求;
(2)将请求一次送入各个功能模块进行过滤处理;
(3)与后端服务器通讯,接收后端服务器处理结果;
(4)数据缓存;
(5)响应客户端请求;

资料来源:
[1] https://segmentfault.com/a/11...[2] 六星教育

相关文章
相关标签/搜索