@dungan
2018-11-15T08:20:40.000000Z
字数 5611
阅读 67
Nginx
nginx是一个十分轻量级的 HTTP 服务器,同时也是一个 IMAP/POP3/SMTP 代理服务器,其特点是占有内存少,并发能力强!
nginx 以事件驱动的方式编写,所以有非常好的性能,能够提供非常高效的反向代理、负载平衡。其拥有匹配 Lighttpd 的性能,同时还没有 Lighttpd 的内存泄漏问题!
相比 apache,nginx 使用更少的资源,支持更多的并发连接,能够支持高达 50000 个并发连接数的响应,体现更高的效率
nginx是异步的,多个连接(万级别)可以对应一个进程,而apache是同步多进程模型,一个连接对应一个进程;
处理请求是异步非阻塞的,负载能力比 apache 高很多,而 apache 则是阻塞型的。在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象
apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache 在处理动态请求有优势,一般 cpu 密集型的应用适合 apache 去做
传统上基于进程或线程模型架构的web服务通过每进程或每线程处理并发连接请求,这势必会在网络和I/O操作时产生阻塞,其另一个必然结果则是对内存或CPU的利用率低下。生成一个新的进程/线程需要事先备好其运行时环境,这包括为其分配堆内存和栈内存,以及为其创建新的执行上下文等。这些操作都需要占用CPU,而且过多的进程/线程还会带来线程抖动或频繁的上下文切换,系统性能也会由此进一步下降
nginx 的工作模式
上文也谈到由于基于多进程和多线程的设计或多或少会带来性能问题,受启发于多种操作系统设计中基于“事件”的高级处理机制,nginx采用了模块化、事件驱动、内存映射,基于磁盘的异步IO、单线程及非阻塞的架构,并大量采用了多路复用及事件通知机制,使得 nginx 能够轻松应对高并发的业务,但话又说回来,IO 如果处理的连接数不是很高的话,使用 select/epoll 的 web server 不一定比使用 multi-threading + blocking I/O 的 web server 性能更好,可能延迟还更大!
apache 三种工作模式分别为prefork、worker、event。
- prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。
- worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select不过可以接受更多的请求,系统资源的开销要小于基于进程的服务器!
- event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的,在这个过程中,进程本身一直处于空闲状态,可以一直接收用户请求,可以实现一个进程程响应多个用户请求。支持持海量并发连接数,消耗更少的资源!
由于我们的应用有 io 密集型和 cpu密集型区别,同时要想做到 低延迟 和 高吞吐 是互相矛盾的 ,不同场景下有不同的选择而已,apache 和 nginx 各有好处!
更为通用的方案是,前端负载均衡用 nginx 抗并发,后端 apache 集群,这是典型应用场景,当然由于两套服务器软件会导致维护成本!
多进程 : 为每个请求启动一个进程来处理。由于在操作系统中,生成进程、销毁进程、进程间切换都很消耗CPU和内存,当负载高时,性能会明显降低!
多线程方式:一个进程中用多个线程处理用户请求。由于线程开销明显小于进程,而且部分资源还可以共享,因此效率较高!
异步:异步是用来解决IO阻塞的场景的有效手段, 无需多线程,一个进程在同一时间就能维持成千上万个连接,这就是所谓的"并发",使用非阻塞方式处理请求,是三种方式中开销最小的。但异步方式虽然效率高,但要求也高,因为多任务之间的调度如果出现问题,就可能出现整体故障,因此使用异步工作的,一般是一些功能相对简单,但却符合服务器任务调度、且代码中没有影响调度的错误代码存在的程序!
接下来看下操作系统对于 web 请求的处理,有助于理解各种 IO 模型!
我们知道工作在用户空间的web服务器进程是无法直接操作IO的,需要通过系统调用进行,其关系如下:
进程向内核进行系统调用申请IO,内核将资源从IO调度到内核的buffer中(kernel wait for data阶段),内核还需将数据从内核buffer中复制(copy data from kernel to user)到 web 服务器进程所在的用户空间,才算完成一次IO调度
根据 wait 和 copy 阶段的处理等待的机制不同,可将 I/O 动作分为如下五种模式
同步与异步,阻塞与非阻塞
阻塞IO
非阻塞I/O
nonblocking IO 的特点是用户进程需要不断的主动询问kernel数据好了没有
I/O复用
也叫 event driven I/O,Linux select/epoll 的好处就在于单个 process 就可以同时处理多个网络连接的 I/O(即一个进程响应多个请求)。它的基本原理就是 select会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程
I/O 复用轮询轮询是所有的 socket(轮询队列),而非阻塞 I/O 是对某个 socket 不断轮询(不断轮询一个),即 I/O 复用发现某个 sokcet 没有数据返回则立马会去检测别的 socket 有没有数据,不会在一棵树上吊死,所以可以把 I/O 复用看做是对非阻塞 I/O 的优化
信号驱动I/O(SIGIO)
首先,我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据
异步I/O(aio)
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过 signal 通知调用者
从 kernel 的角度,当它受到一个 asynchronous read 之后,首先它会立刻返回,所以不会对用户进程产生任何 block。然后,kernel 会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个 signal,告诉它 read 操作完成了
根据上面的描述,按照消息通知的类型,我们又可以将这 IO 模型分为两类
上述各种 IO 模型的实现方式有
epoll(Linux实现)、kqueue(FreeBSD实现)、/dev/poll(Solaris实现)是Reacor模式,IOCP是Proactor模式,其中select和 poll 是标准的工作模型,kqueue 和 epoll 是高效的工作模型,Apache 2.2.9之前只支持select模型,2.2.9之后支持epoll模型!
select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作 !
epoll 通过epoll_ctl() 注册一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似 call back 的回调机制,迅速激活这个文件描述符,epoll_wait()便会得到通知,调用一次epoll_wait()获得就绪文件描述符时,返回的并不是实际的描述符,而是一个代表就绪描述符数量的值,拿到这些值去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里使用内存映射(mmap)技术, 避免了复制大量文件描述符带来的开销!