精华内容
下载资源
问答
  • 前言: 以下是自己整理的关于ngnix服务器相关知识点。从一个请求由浏览器发出,域名解析(这部分下篇整理),到达指定...1)Nginx启动后,会产生一个主进程,主进程执行一系列的工作后会产生一个或者个工作进程; ...

    前言:

    以下是自己整理的关于ngnix服务器相关知识点。从一个请求由浏览器发出,域名解析(这部分下篇整理),到达指定服务器(这里用ngnix)。

     

    Nginx:

    1、是一款免费的、开源的、高性能、模块化、轻量级的HTTP服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器

     

    1)Nginx启动后,会产生一个主进程,主进程执行一系列的工作后会产生一个或者多个工作进程;

    2)在客户端请求动态站点的过程中,Nginx服务器还涉及和后端服务器的通信。Nginx将接收到的Web请求通过代理转发到后端服务器,由后端服务器进行数据处理和组织;

    3)Nginx为了提高对请求的响应效率,降低网络压力,采用了缓存机制,将历史应答数据缓存到本地。保障对缓存文件的快速访问;

    2. Nginx的模块化

    高度模块化的设计是 Nginx 的架构基础。Nginx严格遵循“高内聚,低耦合”的原则,将服务器设计为多个模块,每个模块就是一个独立的功能模块,只负责自身的功能。

    这5个模块从上到下重要性依次递减。

    (1)核心模块

    核心模块是Nginx服务器正常运行必不可少的模块,如同操作系统的内核。它提供了Nginx最基本的核心服务。像进程管理、权限控制、错误日志记录等;

    (2)标准HTTP模块

    标准HTTP模块支持标准的HTTP的功能;

    (3)可选HTTP模块

    可选HTTP模块主要用于扩展标准的HTTP功能,让Nginx能处理一些特殊的服务;

    (4)邮件服务模块

    邮件服务模块主要用于支持Nginx的邮件服务;

    (5)第三方模块

    第三方模块是为了扩展Nginx服务器应用,完成开发者想要的功能;

    3.Nginx的Web请求处理机制

    从架构设计上说,Nginx服务器是与众不同的。其一:在于它的模块化设计;其二也是更重要的一点在于它对与客户端请求的处理机制上。 Nginx请求处理机制结合多进程机制和异步非阻塞机制

    1)多进程

    多进程方式指服务器每当收到一个客户端请求时就由服务器主进程生成一个子进程出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。多进程方式的优点是设计简单,各个子进程相对独立,处理客户端请求时彼此不受干扰;缺点是操作系统生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的开销;当有大量请求时,会导致系统性能下降;

    2)异步非阻塞

    发送方向接收方发送请求后,不用等待响应,可以继续其他工作;接收方处理请求时进行的IO操作如果不能马上得到结果,也不必等待,而是马上返回去去做其他事情。当IO操作完成以后,将完成状态和结果通知接收方,接收方再响应发送方。

    4. Nginx服务器的事件驱动模型

    从上面我们可以知道,Nginx服务器的工作进程调用IO后,就取进行其他工作了;当IO调用返回后,会主动通知工作进程。像select/poll/epoll等这样的系统调用就是用来支持这种解决方案的。这些系统调用也常被称为事件驱动模型,他们提供了一种机制就只让进程同时处理多个并发请求,不用关心IO调用的具体状态。IO调用完全由事件驱动模型来管理。

    如上图所示,Nginx的事件驱动模型由事件收集器、事件发送器和事件处理器三部分基本单元组成。

    select,poll,epoll都是IO多路复用的机制I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。

    5. Nginx服务器Master-Worker进程处理模型

    Nginx服务器在使用Master-Worker模型时,会涉及到主进程和工作进程的交互和工作进程之间的交互。这两类交互都依赖于管道机制。

    Master-Worker交互

    这条管道与普通的管道不同,它是由主进程指向工作进程的单向管道,包含主进程向工作进程发出的指令,工作进程ID等;同时主进程与外界通过信号通信;

    Worker-Worker交互

    这种交互是和Master-Worker交互是基本一致的,但是会通过主进程。工作进程之间是相互隔离的,所以当工作进程W1需要向工作进程W2发指令时,首先找到W2的进程ID,然后将正确的指令写入指向W2的通道。W2收到信号采取相应的措施。

     

    参考文档:Nginx服务器架构揭秘

    另外一篇关于ngnix I/O多路复用Nginx服务器的高性能原理之IO复用

    展开全文
  • Nginx服务器web请求处理机制  从设计架构来说,Nginx服务器是与众不同。不同之处一方面体现在它模块化...一般来说,完成并发处理请求工作有三种方式可供选择、多进程、多线程、异步方式。 多进程方式  ...

    Nginx服务器web请求处理机制

      从设计架构来说,Nginx服务器是与众不同的。不同之处一方面体现在它的模块化设计,另一方面,也是最重要的一方面,体现在它对客户端请求的处理机制上。

          Web服务器和客户端是一对多的关系,Web服务器必须有能力同时为多个客户端提供服务。一般来说,完成并发处理请求工作有三种方式可供选择、多进程、多线程、异步方式。

     

    多进程方式

      多进程方式是指,服务器每当接收到一个客户端时,就由服务器主进程生成一个子进程出来和该客户端建立连接进行交互,直到连接断开,改子进程就结束了。

      多进程方式的优点在于,设计和实现相对简单,各个子进程之间相互独立,处理客户端请求的过程彼此不受到干扰,并且当一个子进程产生问题时,不容易将影响蔓延到其他进程中,这保证了提供服务的稳定性。当子线程退出时,其占用资源会被操作系统回收,也不会留下任何垃圾。而其缺点也很明显。操作系统中生成一个子进程需要进行内存复制等操作,在资源和时间上回产生一定的额外开销,因此,如果Web服务器接收大量并发请求,就会对系统资源造成压力,导致系统性能下降。

      初期的Apache服务器就是采用这种方式对外提供服务的。为了应对大量并发请求,Apache服务器采用“预生成进程”的机制对多进程方式进行改进,“预生成进程”的工作方式很好理解。它将生成子进程的时机提前,在客户端请求还没有到来之前就预先生成好,当请求到来时,主进程分配一个子进程和该客户端进行交互,交互完成之后,该进程也不结束,而被主进程管理起来等待下一个客户端请求的到来,改进的多进程方式在一定程度上缓解了大量并发请求情形下Web服务器对系统资源造成的压力。但是优化Apache服务器在最初的构架设计上采用了多进程方式,因此这不能从根本上解决问题。

     

    多线程方式

      多线程方式和多进程方式相似,他是指,服务器每接收到一个客户端时,会有服务器主进程派生一个线程出来和该客户端进行交互。

      由于操作系统产生一个线程的开销远远小于产生一个进程的开销,所以多线程方式在很大程度上减轻了Web服务器对系统资源的要求。该方式使用线程进行任务调度,开发方面可以遵循一定的标准,这相对来说比较规范和有利于协作。但在线程管理方面,改方式有一定的不足。多个线程位于同一个进程内,可以访问同样的内存空间,彼此之间相互影响;同时,在开发过程中不过避免地要开发者自己对内存进行管理,其增加了出错的风险。服务器系统需要长时间连续不停地运转,错误的逐渐积累可能最终对整个服务器产生重大影响。

      IIS服务,器使用了多线程方式对外提供服务,它的稳定性相对来说还是不错的,但对于经验丰富的Web服务器管理人员而言,他们通常还是会定期检查和重启服务器,以预防不可预料的故障发生。

     

    异步方式

      异步方式是和多进程方式及多线程方式完全不同的一种处理客户端请求的方式。在介绍改方式之前 ,我们先复习下同步、异步以及阻塞、非阻塞的概念。

      网络通信中的同步机制和异步机制是描述通信模块的概念。同步机制,是指发送方发送请求后,需要等待接收到接收方发回的响应后,才接着发送下一个请求;异步机制,和同步机制正好相反,在异步机制中,发送方发出一个请求后,不等待接收方响应这个请求,就继续发送下个请求。在同步机制中,所有的请求在服务器端得到同步,发送方和接收方对请求的处理步调是一致的;在异步机制中,所有来着发送方的请求形成一个队列,接收方处理完成后通知发送方。

      阻塞和非阻塞用来描述进程处理调用的方式,在网络通信中,主要指网络套接字Socket的阻塞和非阻塞方式,而Socket的实质也是IO操作,Socket的阻塞调用方式为,调用结果返回之前,当前线程从运行状态被挂起,一直等到调用结果返回之后,才进行就绪状态,获取CPU后继续执行;Socket的非阻塞调用方式和阻塞方式调用方式正好相反,在非阻塞方式中,如果调用结果不能马上返回,当前线程也不会被挂起,而是立即返回执行下一个调用。

      在网络通信中,经常可以看到有人讲同步和阻塞等同、异步和非阻塞等同。事实上,这两对概念有一定的区别,不能混淆。两对概念的组合,就会产生四个新的概念,同步阻塞、异步阻塞、同步非阻塞、异步非阻塞。

    • 同步阻塞方式,发送方向接收方发送请求后,一直等待响应;接收方处理请求是进行的IO操作如果不能马上得到结果,就一直等到返回结果后,才响应发送方,期间不能进行其他工作。比如、在超时排队付账时,客户(发送方)想收款员(接收方)付款(发送请求)后需要等待收款员找零,期间不能做其他的事情;而收款员要等待收款机返回结果(IO操作)后才能把零钱取出来交给客户(响应请求),期间也只能等待,不能做其他的事情。这种方式实现简单,但是效率不高。
    • 同步非阻塞方式,发送方向接收方发送请求后,一直等待响应;接收方处理请求时进行的IO操作如果不能马上得到结果,就立即返回,去做其他事情,但由于没有得到请求处理结果,不响应发送方,发送方一直在等待,一直等IO操作完成后,接收方获得结果响应发送方后,接收方才进入下一次请求过程。在实际中不使用这种方式。
    • 异步阻塞方法,发送方向接收方发送请求后,不用等待响应,可以继续其他工作;接收方处理请求是进行的IO操作如果不能马上得到结果,就一直等到返回结果后,才响应发送方,期间不能进行其他工作。这种方式在实际中也不使用。
    • 异步非阻塞方式,发送方向接收方发送请求后,不用等待响应,可以继续其他工作;接收方处理请求时进行的IO操作富国不能马上得到结果,也不等待,而是马上返回去做其他事情。当IO操作完成以后,将完成状态和结果通知接收方,接收方再响应发送方。继续使用在超市付账排队的例子。客户(发送方)想收款员(接收方)付款(发送请求)后在等待收款员找零的过程中,还可以做其他事情,比如打电话、聊天等;而收款员在等待收款机处理交易(IO操作)的过程中可以帮助客户将商品打包,当收款机产生结果后,收款员给客户结账(响应请求)。在四种方式中,这种方式是发送方和接收方通信效率最高的一种。

     

    Nginx如何处理请求

      Nginx服务器的一个显著优势是能够同时处理大量并发请求。它结合多进程机制和异步机制对外提供服务,异步机制使用的是异步非阻塞方式

      Nginx服务器启动后,可以产生一个主进程(master process)和多个工作进程(worker processes),其中可以在配置文件中指定产生的工作进程数量。Nginx服务器的所有工作进程都用于接收和处理客户端的请求。这类似于Apache使用的改进的多进程机制,预先生成多个工作进程,等待处理客户端请求。

      每个工作进程使用了异步非阻塞方式,可以处理多个客户端请。当某个工作进程接收到客户端的请求以后,调用IO进行处理,如果不能立即得到结果,就去处理其他的请求;儿客户端在此期间业务需等待响应,可以去处理其他的事情;当IO调用返回结果时,就会通知此工作进程;该进程的到通知,暂时挂起当前处理的事务,去响应客户端请求。

      客户端请求数量增长、网络负载繁重时,Nginx服务器使用多进程机制能够保证不增长对系统资源的压力;同时使用异步非阻塞方式减少了工作进程在I/O调用上的阻塞延迟,保证了不降低对请求的处理能力。

     

     

     Nginx事件驱动处理模型

      在上面我们提到,Nginx服务器的工作进程调用IO后,就去进行其他工作了;当IO调用返回后,会通知工作进程。这里有一个问题,IO调用时如何把自己的状态通知给工作进程的呢?

      一般解决这个问题的方案有两种。一是,让工作进程在进行其他工作的过程中间隔一段时间就去检查一下IO 的运行状态,如果完成,就去响应客户端,如果未完成,就继续在进行的工作;二是,IO调用在完成后能主动通知工作进程。对于前者,虽然工作进程在IO调用过程中没有等待,但不断的检察仍然在时间和资源上导致了不小的开销,最理想的解决方案就是第二种。

      具体来说,select/pool/epool/kqueue等这样的系统调用就是用来支持第二种解决方案的。这些系统调用,也常被称为事件驱动模型,他们提供了一种机制,让进程可以同时处理多个并发请求,不用关心IO调用的具体状态,IO调用完全由事件驱动模型来管理,事件准备好之后就通知工作进程事件已经就绪。

      事件驱动处理库有被称为多路IO复用方法,最常见的包括以下三种:select模型、poll模型和epoll模型。

     

    select库

      select库,是各个版本的Linux和Windows平台都支持的基本事件驱动模型库,并且在接口的定义上也基本相同,只是部分参数的含义略有差异。使用select库的步骤一般是:

      首先,创建所关注事件的描述符集合。对于一个描述符,可以关注其上面的读(Read)事件、写(Write)事件以及异常发生(Exception)事件,所以要创建三类事件描述集合,分别用来收集读事件的描述符、写事件的描述符和异常事件的描述符。

      其次,调用底层提供的select()函数,等待事件发生。这里需要注意的一点是,select的阻塞与是否设置非阻塞I/O是没有关系的。

      然后,轮询所有事件描述符集合中的每个事件描述符,检查是否有相应的事件发生,如果有,就进行处理。

      Nginx服务器在编译过程中如果没有为其他指定其他高性能事件驱动模型库,它将自动编译该库。我们可以使用--with-select_module和--without-select_module两个参数强制Nginx是否编译该库。

     

    poll库

      poll库,作为Linux平台上的基本事件驱动模型,是在Linux2.1.23中引入。Windwos平台不支持poll库。

      poll与select的基本工作方式是相同的,都是先创建一个关注事件的描述符集合,再去等待这些事件的发生,然后再轮询描述符集合,检查有没有事件发生,如果有,就进行处理。

      poll库与select库的主要区别在于,select库需要为读事件、写事件和异常事件分别创建一个描述符集合,因此在最后轮询的时候,需要分别轮询这三个集合。而poll库只需要创建一个集合,在每个描述符对应的结构上分别设置读事件、写事件或者异常事件,最后轮询的时候,可以同时检查者三种事件是否发生。可以说,poll库是select库的优化实现。

      Nginx服务器在编译过程中如果没有为其制定其他高性能事件驱动模型库,它将自动编译该库。我们可以使用--with-poll_module和--without-poll_module两个参数强制Nginx是否编译该库

     

    epoll库
      epoll库是Nginx服务器支持的是高性能事件驱动库之一,它是公认的非常优秀的事件驱动模块,和poll库及select库有很大的不同。epoll属于poll库的一个变种,是在Linux2.5.44中引入的,在Linux2.6及以上的版本都可以使用它。poll库和select库在实际工作中,最大的区别在于效率。

      从前面的介绍我们知道,它们的处理方式都是创建一个待处理事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,以判断事件是否发生。这样在描述符比较多的应用中,效率就显得比较低下了。一种比较好的做法是,把描述符列表的管理交由内核负责,一旦有某种事件发生,内核把发生时间的描述符列表通知给进程,这样就避免了轮询整个描述符列表。epoll库就是这样一种模型。

      首先,epoll库通过相关调用通知内核创建一个有N个描述符的事件列表;然后,给这些描述符设置锁关注的事件,并把它添加到内核的事件列表中去,在具体的编码过程中也可通过相关调用对时间列表中描述符进行修改和删除。

      完成设置之后,epoll库就开始等待内核通知事件发生了。某一事件发生后,内核讲发生事件的描述符列表上报给epoll库。得到时间列表的epoll库,就可以进行事件处理了。

      epoll库在Linux平台上是搞笑的。它支持一个进程打开大数目的时间描述符,上限是系统可以打开文件的最大数目;同时,epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作

     

     

    上面的全部内容均出自于《Nginx高性能Web服务器详情》中第三章Nginx服务器架构初探(我纯手动打上去的/(ㄒoㄒ)/~~  ┭┮﹏┭┮),对于理解Python中三者的关系真的特别有帮助;

    Python开发【第九章】:线程、进程和协程---》》http://www.cnblogs.com/lianzhilei/p/5881434.html

    Python开发【第十章】:I/O多路复用、异步I/O(综合篇)---》》http://www.cnblogs.com/lianzhilei/p/5955526.html

     

     

     

    转载于:https://www.cnblogs.com/lianzhilei/p/6029100.html

    展开全文
  •  Nginx优势在于处理大量并发请求,结合多进程和异步机制  启动后会产生多个工作进程,进程可在配置中配置  每个工作进程使用异步非阻塞方式,处理多个客户端请求,如果不能立即得到结果则去做其他事情,当得到结果...

    下面了解一下Nginx的优势

      Nginx的优势在于处理大量并发请求,结合多进程和异步机制

      启动后会产生多个工作进程,进程可在配置中配置

      每个工作进程使用异步非阻塞方式,处理多个客户端请求,如果不能立即得到结果则去做其他事情,当得到结果后会通知工作进程

      得到通知后去响应客户端请求

    现在就有一个问题,IO调用是如何把自己的状态通知给工作进程呢

      解决该问题的方案有两种

        1.间隔一段时间去检查是否执行完毕,但会造成时间和资源的浪费[不推荐]

        2.IO调用完成后能主动通知工作进程,使用select/poll/epoll/kqueue 被称为事件驱动模型

    那么下面来讲解一下Nginx的事件驱动模型

      事件驱动模型是Nginx服务器保障完成功能和良好性能的重要机制

      大多数网络服务器使用

        事件发送器每传递一个请求,将目标对象放入待处理事件列表,使用非阻塞I/O方式调用事件处理器来处理改请求

        事件驱动处理库采用多路IO复用方法,常见的有select模型,poll模型和epoll模型

       select库

          linux和windows平台都支持,基本接口也相同,参数略有不同

          调用select()函数,等待事件发生,轮询所有事件,检测是否相应的事件发生

       poll库

          windows平台不支持,与select工作方式基本相同

          创建一个关注事件的描述符集合,等待事件发生,轮询描述集合,检查有没有事件发生

          与select的区别:

            select库需要为读事件,写事件,异常事件分别创建一个集合,分别轮询三个集合

            poll库只有一个集合,在每个描述符对应的结构分别设置读事件,写事件,或者异常事件,轮询的时候,同时检查三种事情是否发生

          poll是select的优化实现

          编译过程中没有指定其他高性能事件驱动模型库,默认自动编译该库

       epoll库

          epoll库最NB,和select,poll库有很大不同,epoll属于poll库的一个变种

          将描述符列表的管理交给内核负责,一旦事件发生,内核把发生的描述符通知给进程,避免了轮询整个描述符列表

       rtsig模型

          rtsig模型不常用,不介绍了

       其他事件驱动模型

          kqueue模型:一般在Mac OS X平台,和epoll没有本质区别

          /dev/poll模型:支持Unix衍生平台,在该平台建议使用

          eventport模型:用于支持Solaris10以上平台模型

     

          

    转载于:https://www.cnblogs.com/wangheng669/p/8192213.html

    展开全文
  • 1 多进程并发处理概述1.1概述第五章中我们讨论Apache主程序时候,当主程序调用了函数ap_mpm_run之后,整个主程序就算结束了。那么函数进入到ap_mpm_run之后它干什么去了呢?如果让我们来写服务器程序话,按照...
    1 多进程并发处理概述
    1.1概述
    第五章中我们讨论Apache主程序的时候,当主程序调用了函数ap_mpm_run之后,整个主程序就算结束了。那么函数进入到ap_mpm_run之后它干什么去了呢?
    如果让我们来写服务器程序的话,按照正常的思路,通常主程序在进行了必要的准备工作后会调用诸如fork之类的函数产生一个新的进程或者线程,然后由子进程进行并发处理。事实上,Apache尽管是一个先进的服务器,但是它也不能脱离窠臼。
    主进程一旦调用ap_mpm_run之后,它就进入多进程并发处理状态。为了并发处理客户端请求,Apache或者产生多个进程,或者产生多个线程,或者产生多个进程,每个进程又产生一定数目的线程等等。
    Apache HTTP服务器从一开始就被设计为一个强大、灵活的能够在多种平台上及不同的环境下工作的服务器。 不同的平台和不同的环境经常产生不同的需求,或是会为了达到同样的最佳效果而采用不同的方法。当然,Apache中提供了多种多进程并发模型,比如Prefork,Window NT,Event,perchild等等。并且为了方便移植和替换,Apache将这些多进程并发处理模型设计成模块。Apache凭借它的模块设计很好的适应了大量不同的环境。这一设计使得网站管理员能够在编译时和运行时凭借载入不同的模块来决定服务器的不同附加功能。
    Apache 2.0 将这种模块式设计延伸到web服务器的基础功能上。这个发布版本带有多道处理模块的选择以处理网络端口绑定、接受请求并指派子进程来处理这些请求。
    将模块设计延伸到这一层面主要有以下两大好处:
    Apache可以更简洁、更有效地支持各种操作系统。 尤其是在mpm_winnt使用本地网络特性以代替Apache 1.3中使用的POSIX层后, Windows版本的Apache现在有了更好的性能。 这个优势借助特定的MPM同样延伸到其他各种操作系统。
    从用户层面来讲,MPMs更像其他Apache模块。而主要的不同在于:不论何时,有且仅有一个MPM必须被载入到服务器中。现有的MPM列表可以在这里找到模块索引
    下表列出了不同操作系统下默认的MPMs。如果你在编译时没有进行选择,这将是默认选择的MPM。
    BeOS
    Netware
    OS/2
    Unix
    Windows
    在详细深入的描述各个MPM之前,我们有必要了解一下MPM中所使用到的公共数据结构,主要包括两种:记分板和父子进程的通信管道。记分板类似于共享内存,主要用于父子进程之间进行数据交换,类似于白板。任何一方都可以将对方需要的信息写入到记分板上,同时任何一方也可以到记分板上获取需要的数据。
    2 MPM公共数据结构
    2.1记分板
    2.1.1记分板概述
    Apache的MPM中通常总是包含一个主服务进程以及若干个子进程,因此不可避免的存在主进程和子进程通信的问题。Apache中采用了两种主要的通信方法:记分板和管道。
    记分板就是一块共享内存块,同时可以被父进程和子进程访问,通过共享实现了父子之间的通信。尽管如此,但是记分板则更主要用于父进程对子进程进行控制。在Apache中主进程的一个重要的职责就是控制空闲子进程的数目:如果空闲子进程过多,则父进程将终止一些子进程;如果空闲子进程太少,则父进程将创建一些新的空闲子进程以备使用。因此,父进程必须随时能够知道子进程的数目以便进行调整。子进程把自己的状态信息忙碌或者空闲写入到记分板中,这样通过读取记分板,父进程就可以知道子进程的数目了。
    记分板的数据结构可以描述如下:
    typedef struct {
        global_score *global;
        process_score *parent;
        worker_score **servers;
    } scoreboard;
    该结构定义在scoreboard.h中,由该数据结构可见,Apache中的记分板可以记录三种类型的信息:全局信息、进程间共享信息以及线程间共享信息。
    global_score是记分板中描述全局信息的结构,通常这些信息是针对整个Apache服务器的,而不是针对某个进程或者某个线程的,该结构定义如下:
    typedef struct {
        int server_limit;
        int thread_limit;
        ap_scoreboard_e sb_type;
        ap_generation_t running_generation;
        apr_time_t restart_time;
    } global_score;
    从该结构中我们可以看出,全局的共享信息包括下面的几个内容:server_limit描述系统中所存在的服务进程的极限值,thread_limit则是描述的线程的极限值。ap_scoreboard是枚举类型,只有两个值SB_NOT_SHARED和SB_SHARED,分别表示该记分板是否进程间共享还是不共享。
    ap_generation_t的定义实际上是整数值:typedef int ap_generation_t。该值主要用于“平稳启动(graceful restart)”。 Apache中允许在不终止Apache的情况下对Apache进行重新启动,这种启动称之为“平稳启动”。平稳启动的时候,主服务进程将退出,同时创建新的子进程。此时这些子进程由父进程创建,它们形成一个继承称此上的家族概念,只要是主进程产生的所有子进程都属于这个主进程家族,因此我们称它们称之为为新的“代(generation)”,在本书中我们统一用“家族”这个术语进行描述。只有子进程与父进程具有亲缘关系,它们才是一个家族。每一个进程在执行完任务之后都会检查它与当前的主进程是否属于同一个家族。如果属于,则继续等待处理下一个任务;否则其将退出。由于Apache在进行平稳启动的时候对于那些尚未结束的进程并不强行将其终止,而是让其继续执行,但是主进程必须退出重新启动。因此当新的主进程启动之后,这些残余的子进程显然已经跟它不是同一个家族,它们属于上一辈的。因此他们在执行完任务之后立即退出。
    某个主进程产生后它就产生一个唯一的家族号,用running_generation进行记录。该值永远不会重复。主进程的所有子进程将继承该家族号。running_generation是识别其家族的唯一标记。如果子进程的running_generation与父进程相同,则说明本家族的进程尚存在;反之,如果不相同,则它们执行完后必须结束。这正应了一句古语:”覆巢之下无完卵”或者为”树倒猢狲散”阿。
    restart_time则记录了主服务器重新启动的时间。
    进程间通信则可以使用process_score进行,其定义如下:
    typedef struct process_score process_score;
    struct process_score{
        pid_t pid;
        ap_generation_t generation;    /* generation of this child */
        ap_scoreboard_e sb_type;
        int quiescing;
    };
    通常情况下,父进程往该数据结构中写入数据,而子进程则从其中读取数据。其中pid是主进程的进程号;generation则是当前主进程以及其产生的所有子进程的家族号。sb_type的含义与global_score中的sb_type含义相同。
    Quiescing则
    与process_score用于主进程和子进程通信不同,worker_score则用于记录线程的运行信息,其定义如下:
    typedef struct worker_score worker_score;
    struct worker_score {
    /*第一部分*/
        int thread_num;
    #if APR_HAS_THREADS
        apr_os_thread_t tid;
    #endif
        unsigned char status;
    /*第二部分*/
        unsigned long access_count;
        apr_off_t     bytes_served;
        unsigned long my_access_count;
        apr_off_t     my_bytes_served;
        apr_off_t     conn_bytes;
        unsigned short conn_count;
    /*第三部分*/
        apr_time_t start_time;
        apr_time_t stop_time;
    #ifdef HAVE_TIMES
        struct tms times;
    #endif
        apr_time_t last_used;
    /*第四部分*/
        char client[32];                 /* Keep 'em small... */
        char request[64];        /* We just want an idea... */
        char vhost[32];               /* What virtual host is being accessed? */
    };
    整个worker_score结构可以被分成四部分理解:
    第一部分,主要描述线程的状态和识别信息
    thread_num是Apache识别该线程的唯一识别号,tid则是该线程的线程号。两者是不同的概念:后者是由操作系统或者线程库分配,应用程序无法参与,而前者是Apache设定,跟操作系统无关,具体的含义也只有Apache本身理解。不过thread_num通常遵循下面的设定原则:
    thread_num = 线程所在的进程的索引 * 每个进程允许产生的线程极限 + 线程在进程内的索引
    status则是当前线程的状态,它的状态种类与进程的状态种类相同,用SERVER_XXX常量进行识别。
    第二部分,主要描述线程的状态和识别信息
    第三部分,主要描述线程相关的时间信息
    start_time和stop_time分别是记录线程的启动和停止时间。last_used则用于记录线程最后一次使用的时间。
    第四部分,主要描述线程的状态和识别信息
    该部分主要描述当前线程处理的请求连接上的相关信息。client是请求客户端的主机名称或者是IP地址。request则是客户端发送的请求行信息,比如”GET /server-status?refresh=100 HTTP/1.1”,而vhost则是当前请求所请求的虚拟主机名称,比如”www.myserver.com”。
    从worker_score结构中可以看出,该结构中的记录的大部分信息并不是线程间通信而需要的。那么这些信息到底做什么用的呢?为什么worker_score结构中需要记录这些信息呢?为此我们必须了解Apache中的一个特殊的功能模块mod_status。尽管这个模块要到第三卷才能详细介绍,但是我们还是提前描述。
    为了时刻了解Apache的运行状态,一种方法就是直接在服务器上检测,另一种方法就是远程监控,通过http://www.xxxx.com/server-status URI在浏览器中显示服务器的信息:
    每一个进程或者线程都将自身信息写入到记分板上,这样,mod_status通过读取记分板,就可以知道各个线程的运行状态信息。当然,如果需要显示的信息越多,记分板上需要保存的信息也就越多,worker_score结构也就需要扩展。
    除此之外,Apache中还定义了几个与记分板相关的全局变量,它们是记分板的核心变量:
    (1)、AP_DECLARE_DATA extern scoreboard *ap_scoreboard_image;
    Apache使用使用该变量记录全局记分板,任何进程或者线程都可以通过ap_scoreboard_image直接访问记分板。
    (2)、AP_DECLARE_DATA extern const char *ap_scoreboard_fname;
    该全局变量描述了记分板的名称。
    (3)、AP_DECLARE_DATA extern int ap_extended_status;
    该全局变量描述了当前记分板的状态,
    (4)、AP_DECLARE_DATA extern ap_generation_t volatile ap_my_generation;
    该全局变量描述了当前Apache中的主进程的家族号,任何时候,主进程只要退出进行重新启动,ap_my_generation都会跟着发生变化。该变量与其余的变量相比特殊的地方在于它被声明为volatile类型。
    (5)、static apr_size_t scoreboard_size;
    该变量记录整个记分板所占用的内存的大小。
    在了解了记分板的数据结构之后,我们有必要了解一下记分板的内存组织结构,它的内存布局可以用下图进行描述:
     
    2.1.2记分板处理函数
    从前一节的图片中我们看一看出,每个记分板都包括多个插槽,每一个插槽分别用于记录一个进程的相关信息,不过这种记录的进程信息相对非常的简单,仅仅包括进程的当前状态以及进程号。从记分板的角度而言,每一个进程可以处于12中不同的状态:
    #define SERVER_DEAD 0                        /* 当前的进程执行完毕*/
    #define SERVER_STARTING 1                  /* 进程刚开始执行 */
    #define SERVER_READY 2                       /* 进程已经准备就绪,正在等待客户端连接 */
    #define SERVER_BUSY_READ 3               /* 进程正在读取客户端的请求*/
    #define SERVER_BUSY_WRITE 4             /* 进程正在处理客户端的请求*/
    #define SERVER_BUSY_KEEPALIVE 5      /* 进程在同一个活动连接上正在等待更多的请求*/
    #define SERVER_BUSY_LOG 6               /* 进程正在进行日志操作*/
    #define SERVER_BUSY_DNS 7               /* 进程正在查找主机名称 */
    #define SERVER_CLOSING 8                   /* 进程正在关闭连接 */
    #define SERVER_GRACEFUL 9                 /* 进程正在平稳的完成请求 */
    #define SERVER_IDLE_KILL 10            /* 进程正在清除空闲子进程. */
    #define SERVER_NUM_STATUS 11          /* 进程的多个状态都被设置 */
    进程总是从状态SERVER_STARTING开始,最后在SERVER_DEAD状态结束。当一个进程的状态处于SERVER_DEAD的时候,意味着记分板中的该插槽可以被重新利用。
     
    2.1.2.1创建记分板
    记分板的所有的操作都是从创建开始的,通常只有在刚启动Apache或者平稳启动之后才需要创建。Apache中通过ap_create_scoreboard函数实现记分板的创建,该函数在scoreboard.c中实现,函数原型如下:
    int ap_create_scoreboard(apr_pool_t *p, ap_scoreboard_e sb_type);
    参数p指定创建记分板中所需要的内存来自的内存池,而sb_type则是创建的记分板的类型,或者为SB_SHARED,或者为SB_NOT_SHARED。前者允许记分板在不同的进程之间共享,而后者则不允许。
        int running_gen = 0;
        int i;
        if (ap_scoreboard_image) {
            running_gen = ap_scoreboard_image->global->running_generation;
            ap_scoreboard_image->global->restart_time = apr_time_now();
            memset(ap_scoreboard_image->parent, 0, sizeof(process_score) * server_limit);
            for (i = 0; i < server_limit; i++) {
                memset(ap_scoreboard_image->servers[i], 0, sizeof(worker_score) * thread_limit);
            }
            if (lb_limit) {
                memset(ap_scoreboard_image->balancers, 0, sizeof(lb_score) * lb_limit);
            }       
            return OK;
        }
    公告板的创建与几个系统值密切相关的,比如server_limit和thread_limit。server_limit描述了允许同时存在的进程的最大极限,包括父进程和子进程,每一个进程通常都是用上面的process_score数据结构进行描述;而thread_limit则是每一个子进程又允许生成的子线程的数目,这些子线程用worker_score进行描述。因此创建记分板的一个重要的步骤就是分配足够的插槽。由于Apache需要记录每一个进程以及进程中的每一个线程的运行信息,因此,创建记分板之前必须能够分配足够多的空间以容纳process_score和worker_score结构。
    正如前面描述,系统中允许存在server_limit个进程,它们中的每一个都必须在记分板中拥有一个插槽,因此我们至少必须分配sizeof(process_score)*server_limit大小的内存空间,同时使用ap_scoreboard_image->parent指向该空间。
    同时对于server_limit个进程中的每一个进程,他们可能产生的线程数为thread_limit,这些线程也必须在记分板中拥有相应的插槽,为此共分配server_limit*sizeof(worker_score)*thread_limit的内存大小。
    Apache按照最大化的原则进行分配,一旦分配完毕肯定能够保证需要。不过这样的话可能存在很多的空闲插槽。因为即使只有一个进程和一个线程存在,Apache也是会分配所有的内存的。
    现在回到上面的代码中。如果是平稳启动,那么在创建新的记分板之前系统中应该已经存在一个旧的记分板(ap_scoreboard_image不为NULL)。在这种情况下,Apache首先得到当前记分板的家族号,同时重新设置启动时间。另外一个重要的任务就是清理初始化记分板上的数据,将其全部清零,彻底扫荡前一个家族的所有信息,并将其返回出去供使用。
        ap_calc_scoreboard_size();
    如果创建的时候发现公告板不存在,那么这意味着这是Apache启动以来的第一次记分板创建。因此创建之前必须计算记分板分配的空间大小。创建记分板的内存大小由函数ap_calc_scoreboard_size()函数完成:
    AP_DECLARE(int) ap_calc_scoreboard_size(void)
    {
        ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
        ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);
     
        if (!proxy_lb_workers)
            proxy_lb_workers = APR_RETRIEVE_OPTIONAL_FN(ap_proxy_lb_workers);
        if (proxy_lb_workers)
            lb_limit = proxy_lb_workers();
        else
            lb_limit = 0;
     
        scoreboard_size = sizeof(global_score); u
        scoreboard_size += sizeof(process_score) * server_limit; v
        scoreboard_size += sizeof(worker_score) * server_limit * thread_limit; w
     
        if (lb_limit)
            scoreboard_size += sizeof(lb_score) * lb_limit;x
     
        return scoreboard_size;
    }
    如前所述,记分板内存大小由thread_limit和server_limit决定,这两个值可以通过ap_mpm_query进行查询键值AP_MPMQ_HARD_LIMIT_DAEMONS和AP_MPMQ_HARD_LIMIT_THREADS获取。一旦确定这两个核心值,那么我们就可以计算记分板的分配空间,它包括四个部分:
    (1)、global_score的大小,因此在整个系统中global_score只有一个,因此它所占的大小为sizeof(global_score),如u说示。
    (2)、由于每一个进程都必须在记分板中拥有一个插槽来记录其相关信息,因此server_limit个进程所占的插槽的大小为server_limit*sizeof(process_score),如v所示。
    (3)、对于每一个进程而言,其允许产生的线程的数目为thread_limit个,因此server_limit个进程允许产生的总线程数目为thread_limit个,这些线程也必须在记分板中拥有各自的信息插槽它们所占的内存为server_limit*thread_limit*sizeof(worker_score),如w所示。
    (4)、,如x所示。
    很容易看出,Apache必须为记分板分配的空间大小为sizeof(global_score) + server_limit*sizeof(process_score) + server_limit*thread_limit*sizeof(worker_thread)+ sizeof(lb_score) * lb_limit。计算后的内存大小保存在scoreboard_size全局变量中。
    #if APR_HAS_SHARED_MEMORY
        if (sb_type == SB_SHARED) {
            void *sb_shared;
            rv = open_scoreboard(p);
            if (rv || !(sb_shared = apr_shm_baseaddr_get(ap_scoreboard_shm))) {
                return HTTP_INTERNAL_SERVER_ERROR;
            }
            memset(sb_shared, 0, scoreboard_size);
            ap_init_scoreboard(sb_shared);
        }
        else
    #endif
        {
            /* A simple malloc will suffice */
            void *sb_mem = calloc(1, scoreboard_size);
            if (sb_mem == NULL) {
                ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
                             "(%d)%s: cannot allocate scoreboard",
                             errno, strerror(errno));
                return HTTP_INTERNAL_SERVER_ERROR;
            }
            ap_init_scoreboard(sb_mem);
        }
    正常情况下,记分板应该作为共享内存存在从而被访问,但是并不是所有的操作系统都支持共享内存的操作。因此不同的操作系统,可能会采取不同的措施。
    对于那些不支持共享内存的操作系统,Apache只是简单的调用calloc函数分配scoreboard_size大小的内存块,同时调用ap_init_scoreboard对其进行初始化而已;如果操作系统支持共享内存,那么Apache将采用IPC技术创建一块共享内存,同时使用apr_shm_baseaddr_get得到该共享内存的首地址,并对其进行初始化。
        ap_scoreboard_image->global->sb_type = sb_type;
        ap_scoreboard_image->global->running_generation = running_gen;
        ap_scoreboard_image->global->restart_time = apr_time_now();
     
        apr_pool_cleanup_register(p, NULL, ap_cleanup_scoreboard, apr_pool_cleanup_null);
    在整个记分板创建完毕之后,对global中的全局属性进行设定,同时在内存池销毁链表中注册清除函数。当内存池被销毁的时候,其将调用ap_cleanup_scoreboard对记分板进行清除。
    现在我们再回头看看共享内存的初始化函数ap_init_scoreboard(),该函数主要用于对给定的共享内存块进行初始化,只有了解ap_init_scoreboard函数的初始化细节我们才能够明白记分板的内存布局状况。
    在初始化的过程中一直存在两个内存块:一个是记分板本身的的内存块,即scoreboard数据结构内存块;一个是分配的共享内存块,其中保存实际的进程以及线程信息。初始化的任务实际上就是将记分板结构中的各个指针指向共享内存块中的相应的位置。
    void ap_init_scoreboard(void *shared_score)
    {
        char *more_storage;
        int i;
       
        ap_calc_scoreboard_size();
        ap_scoreboard_image = calloc(1, sizeof(scoreboard) + server_limit * sizeof(worker_score *) +
                   server_limit * lb_limit * sizeof(lb_score *));
    在初始化之前,分配记分板所用内容。从上面的代码中,我们看到,分配的内存除了sizeof(scoreboard)大小是我们意料之内的,剩余的两部分server_limit*sizeof(worker_score)和server_limit*lb_limit*sizeof(lb_score*)则有点出乎意料之外。这两部分内存的用处我们稍后介绍。
        more_storage = shared_score;
        ap_scoreboard_image->global = (global_score *)more_storage;
        more_storage += sizeof(global_score);
        ap_scoreboard_image->parent = (process_score *)more_storage;
        more_storage += sizeof(process_score) * server_limit;
     
        ap_scoreboard_image->servers =
            (worker_score **)((char*)ap_scoreboard_image + sizeof(scoreboard));
        for (i = 0; i < server_limit; i++) {
            ap_scoreboard_image->servers[i] = (worker_score *)more_storage;
            more_storage += thread_limit * sizeof(worker_score);
        }
     
        if (lb_limit) {
            ap_scoreboard_image->balancers = (lb_score *)more_storage;
            more_storage += lb_limit * sizeof(lb_score);
        }
    对于传入的共享内存按照下面的顺序进行布局初始化:首先保存全局共享信息global_score,占用内存sizeof(global_score);然后保存server_limit个进程的信息,占用内存为server_limit *sizeof(process_score);第三部分的内存保存所有进程中产生的线程的信息,占用内存为server_limit*thread_limit*sizeof(worker_score);最后一部分的内存用于保存lb_limit个lb_score结构,四部分合计正好是分配的内存大小。
    上述的四部分分别通过ap_scoreboard_image中相关的成员指针指向:global指针指向第一部分,parent指向第二部分,balancers指向第四部分。稍微复杂的这是的三部分线程信息的指定。共享内存中从more_storage指针往后sizeof(process_score)*server_limit的内存区域用于保存进程信息,因此将这块区域赋值给parent指针。
    global_score和process_score它们在本质上都可以用一维线性结构进行保存,而worker_score则更类似于二维结构,一方便它要记录本身的信息,另一方面还必须知道它是哪一个进程产生的线程,因此它们的保存方法不太一样。在ap_scoreboard_image分配的内存中我们可以看到除了正常的sizeof(scoreboard)大小之外,还包括了server_limit * sizeof(worker_score *)大小的内存区域。该数组中的每一个元素都是一个指向worker_score结构的指针,指向thread_limit个元素的worker_score类型的数组。因此servers[i]对应的数组记录的则是进程i创建的所有thread_limit个线程的信息。按照这种规律,第i个进程内的第j个线程可以用server[i][j]进行描述。这个表达式在后面我们会多次使用到。
    经过分配,整个记分板的内存布局可以用下图描述。
    图4.1 记分板内存分配图
        ap_assert(more_storage == (char*)shared_score + scoreboard_size);
        ap_scoreboard_image->global->server_limit = server_limit;
        ap_scoreboard_image->global->thread_limit = thread_limit;
        ap_scoreboard_image->global->lb_limit     = lb_limit;
    在所有的处理结束后判断more_storage指针时候指向了需要初始化空间的末尾,即判断more_storage是否与(char*)shared_score + scoreboard_size相等,如果不相等,表明可能出错;
    至此,一个完整的记分板已经创建完毕。
    2.1.2.2记分板插槽管理
    对于记分板而言,其最频繁使用的一个功能就是在记分板中查找指定进程信息,函数find_child_by_pid(apr_proc_t *pid)用以完成该功能。
    AP_DECLARE(int) find_child_by_pid(apr_proc_t *pid)
    {
        int i;
        int max_daemons_limit;
        ap_mpm_query(AP_MPMQ_MAX_DAEMONS, &max_daemons_limit);
        for (i = 0; i < max_daemons_limit; ++i) {
            if (ap_scoreboard_image->parent[i].pid == pid->pid) {
                return i;
            }
        }
        return -1;
    }
    函数只需要一个参数,就是进程的描述数据结构。函数所做的事情无非就是对记分板中的parent数组逐一比较,判断其进程id是否是指定的id,如果是则表明找到,并返回其索引值;否则意味着没有找到该进程信息。
    记分板中的每一个插槽都记录了进程的相关信息,其中一个重要的属性就是当前进程的状态。当进程的状态发生变化的时候,记分板中的状态字段也应该随之变化。ap_update_child_status_from_indexes函数用以更新记分板中指定进程的状态。
    AP_DECLARE(int) ap_update_child_status_from_indexes(int child_num,
                                                        int thread_num,
                                                        int status,
                                                        request_rec *r)
    函数需要四个参数,child_num是需要更新状态的进程的索引;thread_num则是线程在该进程的线程组中的索引;status是设置的新的状态值;r则是与当前工作线程关联的请求结构。
        ws = &ap_scoreboard_image->servers[child_num][thread_num];
        old_status = ws->status;
        ws->status = status;
     
        ps = &ap_scoreboard_image->parent[child_num];
    更新之前首先的任务就是获取索引为child_num的进程以及该进程内索引为thread_num的线程的描述数据结构。根据记分板的内存布局很容易理解上面的语句。不过对于线程,在对其进行更改之前必须保存其以前的状态。
        if (status == SERVER_READY
            && old_status == SERVER_STARTING) {
            ws->thread_num = child_num * thread_limit + thread_num;
            ps->generation = ap_my_generation;
        }
    Apache中每个进程都会用一个唯一的整数进行标识。与此类似,每一个线程也会用一个唯一的整数进行标识。线程的识别号取决于它所在的进程索引以及它在进程内的索引:
    线程号 = 线程所在的进程的索引 * 每个进程允许产生的线程极限 + 线程在进程内的索引
    线程号实际上就是该线程描述结构在整个线性描述数组中的索引,同时线程的家族号就是父进程的家族号。不过并不是每一个线程都会有一个线程号。只有处于就绪状态或者正在工作的线程才会安排到对应的线程号。如果一个进程刚刚创建尚未准备好处理客户端请求,那么它暂时还不会分配线程号。
        if (ap_extended_status) {
            ws->last_used = apr_time_now();
            if (status == SERVER_READY || status == SERVER_DEAD) {
                if (status == SERVER_DEAD) {
                    ws->my_access_count = 0L;
                    ws->my_bytes_served = 0L;
                }
                ws->conn_count = 0;
                ws->conn_bytes = 0;
            }
            if (r) {
                conn_rec *c = r->connection;
                apr_cpystrn(ws->client, ap_get_remote_host(c, r->per_dir_config,
                           REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
                if (r->the_request == NULL) {
                    apr_cpystrn(ws->request, "NULL", sizeof(ws->request));
                } else if (r->parsed_uri.password == NULL) {
                    apr_cpystrn(ws->request, r->the_request, sizeof(ws->request));
                } else {
                    apr_cpystrn(ws->request, apr_pstrcat(r->pool, r->method, " ",
                                apr_uri_unparse(r->pool, &r->parsed_uri,
                                APR_URI_UNP_OMITPASSWORD),
                                r->assbackwards ? NULL : " ", r->protocol, NULL),
                                sizeof(ws->request));
                }
                apr_cpystrn(ws->vhost, r->server->server_hostname,
                            sizeof(ws->vhost));
            }
        }
    在前面部分,我们讨论worker_score结构的时候讨论了mod_status模块,它允许详细的显示进程和线程的状态信息。不过这也是有条件的。通常情况下很容易理解线程信息显示的越详细肯定会影响服务器的效率。因此通常情况下,监控信息显示的都不是很详细。除非你手工设置。Apache中提供了一个指令ExtendedStatus来控制是否需要在记分板中记录每个线程的详细信息。该指定反映到程序中则是通过全局变量ap_extended_status来控制。ap_extended_stauts为1的话则意味着必须将线程的详细信息写入到记分板中。
    上面的代码正是在记分板中记录线程的详细信息,包括请求客户端的IP地址,请求行以及请求虚拟主机名称。
    2.1.2.3记分板内存释放
    当记分板不再使用的时候,记分板占用的内存必须被使用。记分板的释放通常只在Apache完全重新启动的时候才会进行。对于平稳启动,记分板不会被释放,只是完成重新初始化。
    记分板通过函数ap_cleanup_scoreboard()完成内存释放。
    apr_status_t ap_cleanup_scoreboard(void *d)
    {
        if (ap_scoreboard_image == NULL) {
            return APR_SUCCESS;
        }
        if (ap_scoreboard_image->global->sb_type == SB_SHARED) {
            ap_cleanup_shared_mem(NULL);
        }
        else {
            free(ap_scoreboard_image->global);
            free(ap_scoreboard_image);
            ap_scoreboard_image = NULL;
        }
        return APR_SUCCESS;
    }
    如果记分板没有被共享,那么对它的释放就非常简单,直接调用free即可,如前所述,记分板中的两个内存块分别用ap_scoreboard_image和ap_scoreboard_image->global分别标识。
    如果记分板被用于进程间共享,则还必须调用共享内存的相关删除函数。
    展开全文
  • 我是这么理解路复用epoll技术就像信号处理机制一样,我们先安装(注册)需要关心文件描述符,然后对其进行监测,一旦有某个描述符被触发,epoll机制就会通知进程该描述符可以进行IO操作。因为socket服务器和...
  • Nginx工作机制剖析

    2020-10-13 21:19:15
    Nginx 以下简介摘自百度百科: Nginx是一款轻量级HTTP服务器/反向代理服务器及电子邮件...Nginx在启动后,在Unix系统中会以daemon(守护进程方式在后台运行,后台进程包含一个master进程个worker进程
  • 1 多进程机制 2 每个工作进程采用异步非阻塞方式 3 采用epoll事件驱动模型 同步异步:通信模式概念 同步:发送方发送请求后,需要等待接收方发回响应后才接着发送下一个请求。 异步:发送方发送请求后,不...
  • NGINX 内部信息图从进程框架顶层开始,向下逐步揭示NGINX如何处理单个进程的多个连接,并进一步探讨其工作机制。场景设置 — NGINX进程模型为了更好地理解这种设计模式,我们需要明白NGINX是如何...
  • Nginx服务器的一个显著优势是能够同时处理大量并发请求。它结合多进程机制和异步机制对外提供服务。异步机制使用的是异步非阻塞方式。在2.3.2节中,我们介绍过Nginx服务器启动后,可以产生一个主进程(master ...
  • 网络数据包、进程信息等各种数据,采用线程模式使五大探针并发工作,提高采集信 息效率与准确度。 2、提出基于心跳包自动连接方法。根据心跳包原理采用定时发送简单通讯包, 如果在指定时间段内未收到对方...
  • Nginx如何优雅地应对高并发

    千次阅读 2019-01-06 17:36:51
    前言 Nginx在网络应用中表现超群,...Nginx内部信息图从进程框架顶层开始,向下逐步揭示Nginx如何处理单个进程的多个连接,并进一步探讨其工作机制。 为了更好地理解这种设计模式,我们需要明白Nginx是如何...
  • NGINX 在网络应用中表现超群,...NGINX内部信息图从进程框架顶层开始,向下逐步揭示NGINX如何处理单个进程的多个连接,并进一步探讨其工作机制。场景设置 — NGINX进程模型为了更好地理解这种设计模式,我们需...
  • Nginx 工作原理 Nginx会按需同时运行进程:一个主进程(master)和几个工作进程(worker),配置了缓存时还会有缓存加载器进程(cache ...在高连接并发的情况下,Nginx是Apache服务器不错替代品 Nginx 安装非常
  • (2)建模简单:通过使用线程可以讲复杂并且异步的工作流进一步分解成一组简单并且同步的工作流,每个工作流在一个单独的线程中运行,并在特定的同步位置交互 (3)简化异步事件的处理:服务器应用程序在接受...
  • 并发、高实时的游戏服务器的开发是很复杂的工作。跟web应用一样, 一个好的开源容器或开发框架可以大大减少游戏开发的复杂性,让开发变得更加容易。遗憾的是目前在游戏服务器开发领域一直没有太好的开源解决方案。...
  • 8.18 并发服务器的算法 97 8.19 主线程和子线程 97 8.20 并发的、无连接的服务器的算法 98 8.21 并发的、面向连接的服务器算法 98 8.22 用分离的程序作为子进程 99 8.23 使用单个线程实现表面上的并发性 99 ...
  • 8.20 并发服务器的算法 80 8.21 主线程和从线程 80 8.22 并发的、无连接的服务器的算法 81 8.23 并发的、面向连接服务器的算法 81 8.24 服务器并发性的实现 82 8.25 把单独的程序作为从进程来使用 82 8.26 ...
  •  到此为止所描述的是把完成一个给定应用所涉及的工作散布到进程中,也许还散布到进程内的线程中。在含有个处理器(CPU)的系统上,进程可能(在不同的CPU上)同时运行,一个给定进程个线程也可能同时运行...
  • 线程

    2020-06-16 22:00:21
    在单个程序中同时运行个线程完成不同的工作。 在 Java 中,垃圾回收机制就是通过一个线程在后台实现的,这样做的好处在于:开发者通常不需要为内存管理投入太的精力。反映到我们现实生活中,在浏览网页时,...
  • 2、通过redis的复制功能可以很好的实现数据库的读写分离,提高服务器的负载能力。主数据库主要进行写操作,而从数据库负责读操作。 原理 1:当一个从数据库启动时,会向主数据库发送sync命令, 2:主数据库接收...
  • DM线程管理

    2020-10-16 14:22:35
    线程通过一定同步机制对数据结构进行并发访问和处理,以完成客户提交各种任务。DM 数据库服务器是共享的服务器,允许个用户连接到同一个服务器上,服务器进程称为共享服务器进程。 DM进程中主要包括监听线程、...
  • Nginx是一种高性能Web服务器,可以同时处理大量并发请求。它结合了多进程机制和异步机制。异步机制使用异步非阻塞模式。接下来,我将介绍Nginx多线程机制和异步非阻塞机制。   多进程  只要服务器接收到客户端...
  • NGINX在网络应用中表现超群,在于其...NGINX内部信息图从进程框架顶层开始,向下逐步揭示NGINX如何处理单个进程的多个连接,并进一步探讨其工作机制。 场景设置 — NGINX进程模型 为了更好地理解这...
  • NGINX 在网络应用中表现超群,在于其独特设计。...NGINX 内部信息图从进程框架顶层开始,向下逐步揭示NGINX如何处理单个进程的多个连接,并进一步探讨其工作机制。 场景设置 — NGINX进程模型
  • 它结合多进程机制和异步机制对外提供服务。异步机制使用是异步非阻塞方式。Nginx服务器启动后,可以产生一个主进程(master process)和多个工作进程(worker processes)。Master-Worker模型实际上被更广泛地称为...

空空如也

空空如也

1 2 3 4 5
收藏数 91
精华内容 36
关键字:

多进程并发服务器的工作机制