如何提高高性能服务器并发量

如何提高高性能服务器并发量,第1张

1、减少内存分配和释放

服务器在运行过程中,需要大量的内存容量来支撑,内存的分配和释放就尤为关键。用户在使用服务器的时候,可以通过改善数据结构以及算法制度来减少中间临时变量的内存分配和数据复制时间。

另外,可以选择使用共享内存模式来降低内存的分配和释放问题。共享内存在多处理器系统中,可以被不同的中央处理器访问,也可以有不同的进程共享,是一种非常快的进程通信方式。

2、使用持久链接

持久链接也被称为场链接,是通过TCP通信的一种方式。在一次TCP链接中持续发送多份数据而不断开连接。

从性能角度上来讲,建立TCP链接次数越少,越有利于性能的提升,尤其对于密集型图片或者网页等数据处理上来说有明显的加速作用。

3、改进I/O模型

I/O操作根据设备形式有不同的类型,例如我们常见的内存I/O,网络I/O,磁盘I/O。针对网络I/O和磁盘I/O, 它们的速度要慢很多,可以选择采用高带宽网络适配器可以提高网络I/O速度。

以上的I/O操作时需要CPU来调度的,这就需要CPU空出时间来等待I/O操作。如果在CPU调度上使用时间较少,也就能节约出CPU的处理时间,从这一点上来说也是提升高服务器并发处理能力的方式。

4、改进服务器并发数策略

服务器高并发策略的调整,是为了让I/O操作和CPU计算尽量重叠进行。一方面使CPU在I/O操作时等待时间内不要空闲,另一方面也是为了最大限度缩短等待时间。【感兴趣的话点击此处,了解一下】

1.同步阻塞IO(Blocking IO)

首先,解释一下这里的阻塞与非阻塞:阻塞IO,指的是需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。阻塞指的是用户空间程序的执行状态。传统的IO模型都是同步阻塞IO。在Java中,默认创建的socket都是阻塞的。其次,解释一下同步与异步:同步IO,是一种用户空间与内核空间的IO发起方式。同步IO是指用户空间的线程是主动发起IO请求的一方,内核空间是被动接受方。异步IO则反过来,是指系统内核是主动发起IO请求的一方,用户空间的线程是被动接受方.

 example:

   在Java应用程序进程中,默认情况下,所有的socket连接的IO操作都是同步阻塞IO(Blocking IO)。在阻塞式IO模型中Java应用程序从IO系统调用开始,直到系统调用返回,在这段时间内,Java进程是阻塞的。返回成功后,应用进程开始处理用户空间的缓存区数据。同步阻塞IO的具体流程,如图2-2所示。

举个例子,在Java中发起一个socket的read读操作的系统调用,

流程大致如下:

(1)从Java启动IO读的read系统调用开始,

用户线程就进入阻塞状态。

(2)当系统内核收到read系统调用,就开始准备数据。一开始,数据可能还没有到达内核缓冲区(例如,还没有收到一个完整的socket数据包),这个时候内核就要等待。

(3)内核一直等到完整的数据到达,就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到用户缓冲区中的字节数)。

(4)直到内核返回后,用户线程才会解除阻塞的状态,重新运行起来。总之,阻塞IO的特点是:在内核进行IO执行的两个阶段,用户线程都被阻塞了。

阻塞IO的优点是:应用的程序开发非常简单;在阻塞等待数据期间,用户线程挂起。在阻塞期间,用户线程基本不会占用CPU资源。阻塞IO的缺点是:一般情况下,会为每个连接配备一个独立的线程;反过来说,就是一个线程维护一个连接的IO操作。在并发量小的情况下,这样做没有什么问题。但是,当在高并发的应用场景下,需要大量的线程来维护大量的网络连接,内存、线程切换开销会非常巨大。因此,基本上阻塞IO模型在高并发应用场景下是不可用的

2.同步非阻塞IO(Non-blocking IO)

非阻塞IO,指的是用户空间的程序不需要等待内核IO操作彻底完成,可以立即返回用户空间执行用户的操作,即处于非阻塞的状态,与此同时内核会立即返回给用户一个状态值。简单来说:阻塞是指用户空间(调用线程)一直在等待,而不能干别的事情;非阻塞是指用户空间(调用线程)拿到内核返回的状态值就返回自己的空间,IO操作可以干就干,不可以干,就去干别的事情。非阻塞IO要求socket被设置为NONBLOCK。强调一下,这里所说的NIO(同步非阻塞IO)模型,并非Java的NIO(New IO)库。

example:

socket连接默认是阻塞模式,在Linux系统下,可以通过设置将socket变成为非阻塞的模式(Non-Blocking)。使用非阻塞模式的IO读写,叫作同步非阻塞IO(None BlockingIO),简称为NIO模式。在NIO模型中,应用程序一旦开始IO系统调用,会出现以下两种情况:

(1)在内核缓冲区中没有数据的情况下,系统调用会立即返回,返回一个调用失败的信息。

(2)在内核缓冲区中有数据的情况下,是阻塞的,直到数据从内核缓冲复制到用户进程缓冲。复制完成后,系统调用返回成功,应用进程开始处理用户空间的缓存数据。

同步非阻塞IO的流程,如图2-3所示。

举个例子。发起一个非阻塞socket的read读操作的系统调用,流程如下:

(1)在内核数据没有准备好的阶段,用户线程发起IO请求时,立即返回。所以,为了读取到最终的数据,用户线程需要不断地发起IO系统调用。

(2)内核数据到达后,用户线程发起系统调用,用户线程阻塞。内核开始复制数据,它会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存),然后内核返回结果(例如返回复制到的用户缓冲区的字节数)。

(3)用户线程读到数据后,才会解除阻塞状态,重新运行起来。也就是说,用户进程需要经过多次的尝试,才能保证最终真正读到数据,而后继续执行。同步非阻塞IO的特点:应用程序的线程需要不断地进行IO系统调用,轮询数据是否已经准备好,如果没有准备好,就继续轮询,直到完成IO系统调用为止。同步非阻塞IO的优点:每次发起的IO系统调用,在内核等待数据过程中可以立即返回。用户线程不会阻塞,实时性较好。同步非阻塞IO的缺点:不断地轮询内核,这将占用大量的CPU时间,效率低下。总体来说,在高并发应用场景下,同步非阻塞IO也是不可用的。一般Web服务器不使用这种IO模型。这种IO模型一般很少直接使用,而是在其他IO模型中使用非阻塞IO这一特性。在Java的实际开发中,也不会涉及这种IO模型。这里说明一下,同步非阻塞IO,可以简称为NIO,但是,它不是Java中的NIO,虽然它们的英文缩写一样,希望大家不要混淆。Java的NIO(NewIO),对应的不是四种基础IO模型中的NIO(NoneBlockingIO)模型,而是另外的一种模型,叫作IO多路复用模型(IOMultiplexing)。

3.IO多路复用(IO Multiplexing)

即经典的Reactor反应器设计模式,有时也称为异步阻塞IO,Java中的Selector选择器和Linux中的epoll都是这种模型。

如何避免同步非阻塞IO模型中轮询等待的问题呢?这就是IO多路复用模型。在IO多路复用模型中,引入了一种新的系统调用,查询IO的就绪状态。在Linux系统中,对应的系统调用为select/epoll系统调用。通过该系统调用,一个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是内核缓冲区可读/可写),内核能够将就绪的状态返回给应用程序。随后,应用程序根据就绪的状态,进行相应的IO系统调用。目前支持IO多路复用的系统调用,有select、epoll等等。select系统调用,几乎在所有的操作系统上都有支持,具有良好的跨平台特性。epoll是在Linux2.6内核中提出的,是select系统调用的Linux增强版本。在IO多路复用模型中通过select/epoll系统调用,单个应用程序的线程,可以不断地轮询成百上千的socket连接,当某个或者某些socket网络连接有IO就绪的状态,就返回对应的可以执行的读写操作。举个例子来说明IO多路复用模型的流程。发起一个多路复用IO的read读操作的系统调用,流程如下:

(1)选择器注册。在这种模式中,首先,将需要read操作的目标socket网络连接,提前注册到select/epoll选择器中,Java中对应的选择器类是Selector类。然后,才可以开启整个IO多路复用模型的轮询流程。

(2)就绪状态的轮询。通过选择器的查询方法,查询注册过的所有socket连接的就绪状态。通过查询的系统调用,内核会返回一个就绪的socket列表。当任何一个注册过的socket中的数据准备好了,内核缓冲区有数据(就绪)了,内核就将该socket加入到就绪的列表中。当用户进程调用了select查询方法,那么整个线程会被阻塞掉。

(3)用户线程获得了就绪状态的列表后,根据其中的socket连接,发起read系统调用,用户线程阻塞。内核开始复制数据,将数据从内核缓冲区复制到用户缓冲区。

(4)复制完成后,内核返回结果,用户线程才会解除阻塞的状态,用户线程读取到了数据,继续执行。

IO多路复用模型的特点:IO多路复用模型的IO涉及两种系统调用(SystemCall),另一种是select/epoll(就绪查询),一种是IO操作。IO多路复用模型建立在操作系统的基础设施之上,即操作系统的内核必须能够提供多路分离的系统调用select/epoll。和NIO模型相似,多路复用IO也需要轮询。负责select/epoll状态查询调用的线程,需要不断地进行select/epoll轮询,查找出达到IO操作就绪的socket连接。IO多路复用模型与同步非阻塞IO模型是有密切关系的。对于注册在选择器上的每一个可以查询的socket连接,一般都设置成为同步非阻塞模型。仅是这一点,对于用户程序而言是无感知的。IO多路复用模型的优点:与一个线程维护一个连接的阻塞IO模式相比,使用select/epoll的最大优势在于,一个选择器查询线程可以同时处理成千上万个连接(Connection)。系统不必创建大量的线程,也不必维护这些线程,从而大大减小了系统的开销。Java语言的NIO(NewIO)技术,使用的就是IO多路复用模型。在Linux系统上,使用的是epoll系统调用。IO多路复用模型的缺点:本质上,select/epoll系统调用是阻塞式的,属于同步IO。都需要在读写事件就绪后,由系统调用本身负责进行读写,也就是说这个读写过程是阻塞的。如何彻底地解除线程的阻塞,就必须使用异步IO模型。

4.异步IO(Asynchronous IO)

异步IO,指的是用户空间与内核空间的调用方式反过来。用户空间的线程变成被动接受者,而内核空间成了主动调用者。这有点类似于Java中比较典型的回调模式,用户空间的线程向内核空间注册了各种IO事件的回调函数,由内核去主动调用.

异步IO模型(AsynchronousIO,简称为AIO)。AIO的基本流程是:用户线程通过系统调用,向内核注册某个IO操作。内核在整个IO操作(包括数据准备、数据复制)完成后,通知用户程序,用户执行后续的业务操作。在异步IO模型中,在整个内核的数据处理过程中,包括内核将数据从网络物理设备(网卡)读取到内核缓冲区、将内核缓冲区的数据复制到用户缓冲区,用户程序都不需要阻塞.

举个例子。发起一个异步IO的read读操作的系统调用,流程如下:

(1)当用户线程发起了read系统调用,立刻就可以开始去做其他的事,用户线程不阻塞。

(2)内核就开始了IO的第一个阶段:准备数据。等到数据准备好了,内核就会将数据从内核缓冲区复制到用户缓冲区(用户空间的内存)。

(3)内核会给用户线程发送一个信号(Signal),或者回调用户线程注册的回调接口,告诉用户线程read操作完成了。

(4)用户线程读取用户缓冲区的数据,完成后续的业务操作。异步IO模型的特点:在内核等待数据和复制数据的两个阶段,用户线程都不是阻塞的。用户线程需要接收内核的IO操作完成的事件,或者用户线程需要注册一个IO操作完成的回调函数。正因为如此,异步IO有的时候也被称为信号驱动IO。异步IO异步模型的缺点:应用程序仅需要进行事件的注册与接收,其余的工作都留给了操作系统,也就是说,需要底层内核提供支持。理论上来说,异步IO是真正的异步输入输出,它的吞吐量高于IO多路复用模型的吞吐量。就目前而言,Windows系统下通过IOCP实现了真正的异步IO。而在Linux系统下,异步IO模型在2.6版本才引入,目前并不完善,其底层实现仍使用epoll,与IO多路复用相同,因此在性能上没有明显的优势。大多数的高并发服务器端的程序,一般都是基于Linux系统的。因而,目前这类高并发网络应用程序的开发,大多采用IO多路复用模型。大名鼎鼎的Netty框架,使用的就是IO多路复用模型,而不是异步IO模型。

本资料收集于Netty、Redis、Zookeeper高并发实战,希望大家购买正版图书地址:https://www.jd.com/zxnews/6997df6e93bc8192.html

Nginx才短短几年,就拿下了web服务器大笔江山,众所周知,Nginx在处理大并发静态请求方面,效率明显高于httpd,甚至能轻松解决C10K问题。下面我们就来聊聊Web服务器背后的一些原理。

进程是具有一定独立功能的,在计算机中已经运行的程序的实体。在早期系统中(如linux 2.4以前),进程是基本运作单位,在支持线程的系统中(如windows,linux2.6)中,线程才是基本的运作单位,而进程只是线程的容器。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。若干进程有可能与同一个程序相关系,且每个进程皆可以同步(循序)或异步(平行)的方式独立运行。现代计算机系统可在同一段时间内以进程的形式将多个程序加载到存储器中,并借由时间共享(或称时分复用),以在一个处理器上表现出同时(平行性)运行的感觉。同样的,使用多线程技术(多线程即每一个线程都代表一个进程内的一个独立执行上下文)的操作系统或计算机架构,同样程序的平行线程,可在多 CPU 主机或网络上真正同时运行(在不同的CPU上)。

Web服务器要为用户提供服务,必须以某种方式,工作在某个套接字上。一般Web服务器在处理用户请求是,一般有如下三种方式可选择:多进程方式、多线程方式、异步方式。Web服务器要为用户提供服务,必须以某种方式,工作在某个套接字上。一般Web服务器在处理用户请求是,一般有如下三种方式可选择:多进程方式、多线程方式、异步方式。多进程方式:为每个请求启动一个进程来处理。由于在操作系统中,生成进程、销毁进程、进程间切换都很消耗CPU和内存,当负载高是,性能会明显降低。优点: 稳定性!由于采用独立进程处理独立请求,而进程之间是独立的,单个进程问题不会影响其他进程,因此稳定性最好。缺点: 资源占用!当请求过大时,需要大量的进程处理请求,进程生成、切换开销很大,而且进程间资源是独立的,造成内存重复利用。多线程方式:一个进程中用多个线程处理用户请求。由于线程开销明显小于进程,而且部分资源还可以共享,因此效率较高。优点:开销较小!线程间部分数据是共享的,且线程生成与线程间的切换所需资源开销比进程间切换小得多。缺点:稳定性!线程切换过快可能造成线程抖动,且线程过多会造成服务器不稳定。异步方式:使用非阻塞方式处理请求,是三种方式中开销最小的。但异步方式虽然效率高,但要求也高,因为多任务之间的调度如果出现问题,就可能出现整体故障,因此使用异步工作的,一般是一些功能相对简单,但却符合服务器任务调度、且代码中没有影响调度的错误代码存在的程序。优点:性能最好!一个进程或线程处理多个请求,不需要额外开销,性能最好,资源占用最低。缺点:稳定性!某个进程或线程出错,可能导致大量请求无法处理,甚至导致整个服务宕机。

通过这样的一个复杂过程,一次请求就完成了。简单来说就是:用户请求-->送达到用户空间-->系统调用-->内核空间-->内核到磁盘上读取网页资源->返回到用户空间->响应给用户。上述简单的说明了一下,客户端向Web服务请求过程,在这个过程中,有两个I/O过程,一个就是客户端请求的网络I/O,另一个就是Web服务器请求页面的磁盘I/O。 下面我们就来说说Linux的I/O模型。

通过上面的对连接的处理分析,我们知道工作在用户空间的web服务器进程是无法直接操作IO的,需要通过系统调用进行,其关系如下:

即进程向内核进行系统调用申请IO,内核将资源从IO调度到内核的buffer中(wait阶段),内核还需将数据从内核buffer中复制(copy阶段)到web服务器进程所在的用户空间,才算完成一次IO调度。这几个阶段都是需要时间的。根据wait和copy阶段的处理等待的机制不同,可将I/O动作分为如下五种模式:

这里有必要先解释一下阻塞、非阻塞,同步、异步、I/O的概念。

阻塞和非阻塞指的是执行一个操作是等操作结束再返回,还是马上返回。比如餐馆的服务员为用户点菜,当有用户点完菜后,服务员将菜单给后台厨师,此时有两种方式:第一种:就在出菜窗口等待,直到厨师炒完菜后将菜送到窗口,然后服务员再将菜送到用户手中;第二种:等一会再到窗口来问厨师,某个菜好了没?如果没有先处理其他事情,等会再去问一次;第一种就是阻塞方式,第二种则是非阻塞的。

同步和异步又是另外一个概念,它是事件本身的一个属性。还拿前面点菜为例,服务员直接跟厨师打交道,菜出来没出来,服务员直接指导,但只有当厨师将菜送到服务员手上,这个过程才算正常完成,这就是同步的事件。同样是点菜,有些餐馆有专门的传菜人员,当厨师炒好菜后,传菜员将菜送到传菜窗口,并通知服务员,这就变成异步的了。其实异步还可以分为两种:带通知的和不带通知的。前面说的那种属于带通知的。有些传菜员干活可能主动性不是很够,不会主动通知你,你就需要时不时的去关注一下状态。这种就是不带通知的异步。对于同步的事件,你只能以阻塞的方式去做。而对于异步的事件,阻塞和非阻塞都是可以的。非阻塞又有两种方式:主动查询和被动接收消息。被动不意味着一定不好,在这里它恰恰是效率更高的,因为在主动查询里绝大部分的查询是在做无用功。对于带通知的异步事件,两者皆可。而对于不带通知的,则只能用主动查询。

回到I/O,不管是I还是O,对外设(磁盘)的访问都可以分成请求和执行两个阶段。请求就是看外设的状态信息(比如是否准备好了),执行才是真正的I/O操作。在Linux 2.6之前,只有“请求”是异步事件,2.6之后才引入AIO(asynchronous I/O )把“执行”异步化。别看Linux/Unix是用来做服务器的,这点上比Windows落后了好多,IOCP(Windows上的AIO,效率极高)在Win2000上就有了。所以学linux的别老觉得Windows这里不好那里不好(Windows的多线程机制也由于linux)。

根据以上分析,I/O可分为五种模型:

Linux上的前四种I/O模型的“执行”阶段都是同步的,只有最后一种才做到了真正的全异步。第一种阻塞式是最原始的方法,也是最累的办法。当然累与不累要看针对谁。应用程序是和内核打交道的。对应用程序来说,这种方式是最累的,但对内核来说这种方式恰恰是最省事的。还拿点菜这事为例,你就是应用程序,厨师就是内核,如果你去了一直等着,厨师就省事了(不用同时处理其他服务员的菜)。当然现在计算机的设计,包括操作系统,越来越为终端用户考虑了,为了让用户满意,内核慢慢的承担起越来越多的工作,IO模型的演化也是如此。非阻塞I/O ,I/O复用,信号驱动式I/O其实都是非阻塞的,当然是针对“请求”这个阶段。非阻塞式是主动查询外设状态。I/O复用里的select,poll也是主动查询,不同的是select和poll可以同时查询多个fd(文件句柄)的状态,另外select有fd个数的限制。epoll是基于回调函数的。信号驱动式I/O则是基于信号消息的。这两个应该可以归到“被动接收消息”那一类中。最后就是伟大的AIO的出现,内核把什么事都干了,对上层应用实现了全异步,性能最好,当然复杂度也最高。

说明:应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。这个不用多解释吧,阻塞套接字。下图是它调用过程的图示:(注,一般网络I/O都是阻塞I/O,客户端发出请求,Web服务器进程响应,在进程没有返回页面之前,这个请求会处于一直等待状态)

我们把一个套接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间,所有一般Web服务器都不使用这种I/O模型。具体过程如下图:

I/O复用模型会用到select或poll函数或epoll函数(Linux2.6以后的内核开始支持),这两个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。具体过程如下图:

首先,我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。具体过程如下图:

当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。具体过程如下图:

从上图中我们可以看出,可以看出,越往后,阻塞越少,理论上效率也是最优。其五种I/O模型中,前三种属于同步I/O,后两者属于异步I/O。

同步I/O:

异步I/O:

异步 I/O 和 信号驱动I/O的区别:

注,其中iocp是Windows实现的,select、poll、epoll是Linux实现的,kqueue是FreeBSD实现的,/dev/poll是SUN的Solaris实现的。select、poll对应第3种(I/O复用)模型,iocp对应第5种(异步I/O)模型,那么epoll、kqueue、/dev/poll呢?其实也同select属于同一种模型,只是更高级一些,可以看作有了第4种(信号驱动I/O)模型的某些特性,如callback机制。

答案是,他们无轮询。因为他们用callback取代了。想想看,当套接字比较多的时候,每次select()都要通过遍历FD_SETSIZE个Socket来完成调度,不管哪个Socket是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll、kqueue、/dev/poll做的。这样子说可能不好理解,那么我说一个现实中的例子,假设你在大学读书,住的宿舍楼有很多间房间,你的朋友要来找你。select版宿管大妈就会带着你的朋友挨个房间去找,直到找到你为止。而epoll版宿管大妈会先记下每位同学的房间号,你的朋友来时,只需告诉你的朋友你住在哪个房间即可,不用亲自带着你的朋友满大楼找人。如果来了10000个人,都要找自己住这栋楼的同学时,select版和epoll版宿管大妈,谁的效率更高,不言自明。同理,在高并发服务器中,轮询I/O是最耗时间的操作之一,select、epoll、/dev/poll的性能谁的性能更高,同样十分明了。

诚然,Windows的IOCP非常出色,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX下。而且正如上面所述,kqueue、epoll、/dev/poll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重,kqueue、epoll、/dev/poll 已经做得很优秀了。

只有IOCP(windows实现)是asynchronous I/O,其他机制或多或少都会有一点阻塞。select(Linux实现)低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善epoll(Linux实现)、kqueue(FreeBSD实现)、/dev/poll(Solaris实现)是Reacor模式,IOCP是Proactor模式。Apache 2.2.9之前只支持select模型,2.2.9之后支持epoll模型Nginx 支持epoll模型Java nio包是select模型

我们都知道Apache有三种工作模块,分别为prefork、worker、event。prefork:多进程,每个请求用一个进程响应,这个过程会用到select机制来通知。worker:多线程,一个进程可以生成多个线程,每个线程响应一个请求,但通知机制还是select不过可以接受更多的请求。event:基于异步I/O模型,一个进程或线程,每个进程或线程响应多个用户请求,它是基于事件驱动(也就是epoll机制)实现的。

如果不用“--with-mpm”显式指定某种MPM,prefork就是Unix平台上缺省的MPM.它所采用的预派生子进程方式也是 Apache1.3中采用的模式。prefork本身并没有使用到线程,2.0版使用它是为了与1.3版保持兼容性;另一方面,prefork用单独的子进程来处理不同的请求,进程之间是彼此独立的,这也使其成为最稳定的MPM之一。

相对于prefork,worker是2.0版中全新的支持多线程和多进程混合模型的MPM。由于使用线程来处理,所以可以处理相对海量的请求,而系统资源的开销要小于基于进程的服务器。但是,worker也使用了多进程,每个进程又生成多个线程,以获得基于进程服务器的稳定性,这种MPM的工作方 式将是Apache2.0的发展趋势。

一个进程响应多个用户请求,利用callback机制,让套接字复用,请求过来后进程并不处理请求,而是直接交由其他机制来处理,通过epoll机制来通知请求是否完成;在这个过程中,进程本身一直处于空闲状态,可以一直接收用户请求。可以实现一个进程程响应多个用户请求。支持持海量并发连接数,消耗更少的资源。

有几个基本条件:

刚好,Nginx 支持以上所有特性。所以Nginx官网上说,Nginx支持50000并发,是有依据的。

传统上基于进程或线程模型架构的web服务通过每进程或每线程处理并发连接请求,这势必会在网络和I/O操作时产生阻塞,其另一个必然结果则是对内存或CPU的利用率低下。生成一个新的进程/线程需要事先备好其运行时环境,这包括为其分配堆内存和栈内存,以及为其创建新的执行上下文等。这些操作都需要占用CPU,而且过多的进程/线程还会带来线程抖动或频繁的上下文切换,系统性能也会由此进一步下降。另一种高性能web服务器/web服务器反向代理:Nginx(Engine X),nginx的主要着眼点就是其高性能以及对物理计算资源的高密度利用,因此其采用了不同的架构模型。受启发于多种操作系统设计中基于“事件”的高级处理机制,nginx采用了模块化、事件驱动、异步、单线程及非阻塞的架构,并大量采用了多路复用及事件通知机制。在nginx中,连接请求由为数不多的几个仅包含一个线程的进程worker以高效的回环(run-loop)机制进行处理,而每个worker可以并行处理数千个的并发连接及请求。

Nginx会按需同时运行多个进程:一个主进程(master)和几个工作进程(worker),配置了缓存时还会有缓存加载器进程(cache loader)和缓存管理器进程(cache manager)等。所有进程均是仅含有一个线程,并主要通过“共享内存”的机制实现进程间通信。主进程以root用户身份运行,而worker、cache loader和cache manager均应以非特权用户身份运行。

主进程主要完成如下工作:

注:如果负载以CPU密集型应用为主,如SSL或压缩应用,则worker数应与CPU数相同;如果负载以IO密集型为主,如响应大量内容给客户端,则worker数应该为CPU个数的1.5或2倍。

Nginx的代码是由一个核心和一系列的模块组成, 核心主要用于提供Web Server的基本功能,以及Web和Mail反向代理的功能;还用于启用网络协议,创建必要的运行时环境以及确保不同的模块之间平滑地进行交互。不过,大多跟协议相关的功能和某应用特有的功能都是由nginx的模块实现的。这些功能模块大致可以分为事件模块、阶段性处理器、输出过滤器、变量处理器、协议、upstream和负载均衡几个类别,这些共同组成了nginx的http功能。事件模块主要用于提供OS独立的(不同操作系统的事件机制有所不同)事件通知机制如kqueue或epoll等。协议模块则负责实现nginx通过http、tls/ssl、smtp、pop3以及imap与对应的客户端建立会话。在Nginx内部,进程间的通信是通过模块的pipeline或chain实现的;换句话说,每一个功能或操作都由一个模块来实现。例如,压缩、通过FastCGI或uwsgi协议与upstream服务器通信,以及与memcached建立会话等。

处理静态文件,索引文件以及自动索引;反向代理加速(无缓存),简单的负载均衡和容错;FastCGI,简单的负载均衡和容错;模块化的结构。过滤器包括gzipping, byte ranges, chunked responses, 以及 SSI-filter 。在SSI过滤器中,到同一个 proxy 或者 FastCGI 的多个子请求并发处理;SSL 和 TLS SNI 支持;

使用外部 HTTP 认证服务器重定向用户到 IMAP/POP3 后端;使用外部 HTTP 认证服务器认证用户后连接重定向到内部的 SMTP 后端;认证方法:POP3: POP3 USER/PASS, APOP, AUTH LOGIN PLAIN CRAM-MD5IMAP: IMAP LOGINSMTP: AUTH LOGIN PLAIN CRAM-MD5SSL 支持;在 IMAP 和 POP3 模式下的 STARTTLS 和 STLS 支持;

FreeBSD 3.x, 4.x, 5.x, 6.x i386FreeBSD 5.x, 6.x amd64Linux 2.2, 2.4, 2.6 i386Linux 2.6 amd64Solaris 8 i386Solaris 9 i386 and sun4uSolaris 10 i386MacOS X (10.4) PPCWindows 编译版本支持 windows 系列操作系统

一个主进程和多个工作进程,工作进程运行于非特权用户;kqueue (FreeBSD 4.1+), epoll (Linux 2.6+), rt signals (Linux 2.2.19+), /dev/poll (Solaris 7 11/99+), select, 以及 poll 支持;kqueue支持的不同功能包括 EV_CLEAR, EV_DISABLE (临时禁止事件), NOTE_LOWAT, EV_EOF, 有效数据的数目,错误代码;sendfile (FreeBSD 3.1+), sendfile (Linux 2.2+), sendfile64 (Linux 2.4.21+), 和 sendfilev (Solaris 8 7/01+) 支持;输入过滤 (FreeBSD 4.1+) 以及 TCP_DEFER_ACCEPT (Linux 2.4+) 支持;10,000 非活动的 HTTP keep-alive 连接仅需要 2.5M 内存。最小化的数据拷贝操作;

基于IP 和名称的虚拟主机服务;Memcached 的 GET 接口;支持 keep-alive 和管道连接;灵活简单的配置;重新配置和在线升级而无须中断客户的工作进程;可定制的访问日志,日志写入缓存,以及快捷的日志回卷;4xx-5xx 错误代码重定向;基于 PCRE 的 rewrite 重写模块;基于客户端 IP 地址和 HTTP 基本认证的访问控制;PUT, DELETE, 和 MKCOL 方法;支持 FLV (Flash 视频);带宽限制;

在高连接并发的情况下,Nginx是Apache服务器不错的替代品: Nginx在美国是做虚拟主机生意的老板们经常选择的软件平台之一. 能够支持高达 50,000 个并发连接数的响应, 感谢Nginx为我们选择了 epoll and kqueue 作为开发模型。Nginx作为负载均衡服务器: Nginx 既可以在内部直接支持 Rails 和 PHP 程序对外进行服务, 也可以支持作为 HTTP代理 服务器对外进行服务. Nginx采用C进行编写, 不论是系统资源开销还是CPU使用效率都比 Perlbal 要好很多。作为邮件代理服务器: Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮件代理服务器), Last.fm 描述了成功并且美妙的使用经验.Nginx 安装非常的简单 , 配置文件非常简洁(还能够支持perl语法),Bugs 非常少的服务器: Nginx 启动特别容易, 并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动. 你还能够 不间断服务的情况下进行软件版本的升级 。Nginx 的诞生主要解决C10K问题


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/268433.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-18
下一篇2023-04-18

发表评论

登录后才能评论

评论列表(0条)

    保存