Nginx学习笔记(十一):Nginx的架构设计

前言


       开始学习书的第三部分,深入Nginx了,也越发在Nginx身上看到了之前实习公司所开发系统的影子,感谢过去的这段经历。另外,越来越发现CSDN的是个好地方,看别人博客的时候总能让自己也热血沸腾,在他们身上看到了自己缺少并且又难能可贵的品质,向他们学习。

Nginx架构设计

        这里先说下Nginx设计时重视的几个关键点:
  • 性能:包括网络性能,单词请求的延迟性,网络效率;(名词就不解释了~)
  • 可伸缩性:可通过添加组件来提升服务,或者允许组件之间具有交互功能;
  • 简单性:组件的简单程度,便于理解和实现;
  • 可修改性:包括可进化性,可扩展性,可定制性,可配置性,可重用性;
  • 可见性:可监控关键组件的运行情况;
  • 可移植性:跨平台运行;
  • 可靠性:在服务出现故障时,一个架构容易受到系统层面故障影响的程度;

模块化设计

      
       高度模块化的设计是Nginx的架构基础。在Nginx中,除了少量的核心代码,其他一切皆为模块。这一点在之前的模块开发的实践中已经感受到了。

       所有模块都遵循ngx_module_t的接口设计,并且所有模块间都是分层次、分类别的。

       官方Nginx共有五大类型模块:核心模块、配置模块、事件模块、HTTP模块、mail模块

       它们都各具备相同的ngx_module_t接口,但在请求处理流程中的层次不相同。

        Nginx常用模块间的关系如下图:

             

       配置模块与核心模块由Nginx的框架代码定义。其中,配置模块是所有模块的基础,它实现最基本的解析功能(即解析nginx.conf文件)。然后,Nginx框架会调用核心模块,其他三种模块则不会与框架产生直接关系。如上图示,事件模块、HTTP模块、mail模块在核心模块中分别都有一个自己的“代言人”,并在同类模块中有一个作为核心业务与管理功能的模块。比如,事件模块由其代言人ngx_events_module核心模块定义,但所有事件模块加载又由ngx_event_core_module负责。

       在上图中,配置模块与核心模块都是与Nginx框架密切相关的,是其他模块的基础。而事件模块又是HTTP模块和mail模块的接触。HTTP模块与mail模块更关注于应用层面,地位相似。

事件驱动架构

       事件驱动架构,简单来生活,就是由一些时间发生源产生事件,由一个或多个事件收集器来收集、分发时间,然后许多时间处理器会注册自己感兴趣的事件,同时会“消费”这些事件。

        Nginx采用完全的事件驱动架构来处理业务,与传统的Web服务器不同。区别用图示:
  • 传统Web服务器处理事件模型
             

  • Nginx处理事件模型
              

       两者最重要差别:传统Web服务器是每个事件消费者独占一个进程资源,Nginx的事件消费者只是被事件分发进程短期调用。

       这样的设计使得网络性能、用户感知的请求延时得到提升。但同时带来的一个弊端:每个时间消费者不能有阻塞行为,否则会由于长时间占用分发者进程而导致其他事件得不到响应。更进一步说,就是不能让分发者进程转为休眠或等待状态。

请求的多阶段异步处理

       请求的多阶段异步处理只能基于事件驱动架构,意思就是把一个请求的处理过程按照事件的触发方式划分为多个阶段,每个阶段都可以由事件收集、分发器来触发。

异步处理和多阶段:
       异步处理与多阶段的划分是相辅相成的。可以这样理解,当一个事件被分发到事件消费者中进行处理时,事件消费者处理完当前事件只是相当于处理完一个请求的某个阶段,然后再等待内核通知,进而再次调用消费者处理...

请求的多阶段异步处理优势:
       这种设计可以使得每个进程都能全力进行,不会或者尽量少地出现进程休眠状态。
  1. 一旦出现进程休眠,必然减少并发处理事件的数目,进而会降低网络性能同时增加请求处理时间的平均时延;
  2. 进程如果休眠导致网络性能无法满足业务需求,则系统只能通过增加进程来解决。这时,进程数目过多又将会导致操作系统的额外操作:进程间切换。频繁地进行进程切换会严重消耗CPU资源;
  3. 休眠进程会使得进程占用内存而不得释放,这将导致系统可用内存下降,从而影响系统能够处理的最大并发连接数;
如何划分请求的阶段?
  • 将阻塞进程的方法按照相关的触发事件分为两个阶段
       一个本身能够导致进程休眠的方法或系统调用,一般都能够分解为多个更小的方法或者系统调用。大部分情况下,可以这样划分:
        1,阻塞方法改为非阻塞方法,此为第一阶段;
        2,处理非阻塞方法返回后的结果,此为第二阶段;
        举例:
              Nginx学习笔记(十一):Nginx的架构设计_第1张图片                         Nginx学习笔记(十一):Nginx的架构设计_第2张图片
  • 将阻塞方法调用按照时间分解为多个阶段的方法调用
             如果按照上一种方式试图划分时,发现找出的触发事件不能被事件收集、分发器处理,这时,只能按照执行事件拆分。
        举例:
        读取10M文件,但文件未必在磁盘中连续,此时就需要多次驱动硬盘寻址。而在寻址过程中,就可能会导致进程休眠或等待。此时,我们希望能想上述方法划分阶段。但是,比如在Linux上,读取磁盘文件必须要通过内核异步I/O,但Linux上的Nginx的事件模块在没打开异步I/O时不支持这种做法,所以,这样我们就只能分写读取文件调用:把10M均分成100份,每次读取10K大小。于是,每阶段读10K,这样就将事件拆分。

  • 在“无所事事”且必须等待系统的响应,从而导致进程空转时,使用定时器划分阶段
       代码段本身并没有阻塞方法,但实际上是阻塞进程。比如:进行某个无阻塞的系统调用后,必须通过持续的检查标志位来确定是否继续向下执行,当标志位没有获得满足时就无限循环检查。这种情况下,需要用到定时器控制,如果超时,标志位仍不满足,则开始下一阶段事件。

  • 如果阻塞方法完全无法继续划分,则必须使用独立的进程执行这个阻塞方法
       当某个方法调用时可能导致进程休眠,或者占用进程时间过长,可是又无法将该方法分解为不阻塞的方法,这种情况通常与事件驱动架构本身相违背。这时,必须通过产生新的进程或者指定某个非事件分发者进程来执行阻塞方法,并在阻塞方法执行完毕时想事件收集、分发者进程发送事件通知继续执行。这里至少拆分两阶段:阻塞方法执行前阶段,阻塞方法执行后阶段,而阻塞方法的执行要使用单独的进程去调度,并在方法返回后发送事件通知。
       一般出现这种设计,需要审视事件消费者是否合理。。(这一段比较抽象,书中也没有举例,所以这句话不太能理解)

管理进程、多工作进程设计

        Nginx采用一个master管理进程、多个worker工作进程的设计方式。如下图:

                Nginx学习笔记(十一):Nginx的架构设计_第3张图片

        这种设计带来的优势:
  • 1)利用多核系统的并发处理能力。多个Work进程可以占用不同的CPU核心来工作,这样提供网络性能,降低了请求时延。
  • 2)负载均衡。一个请求到来时被分配到负载较轻的Worker进程中去处理。
  • 3)管理进程负责监控工作进程状态,并负责管理其行为。Master进程基本不占用多少系统资源,它只负责启动、停止、监控或使用其他行为来控制Worker进程。

平台无关代码实现

       Nginx重新封装了日志、各种基本数据结构(之前的笔记都有记录)、常用算法等。

       在核心代码都使用了与操作系统无关的代码实现,在与操作系统相关的系统调用上则分别针对各个操作系统都有各自独立的实现,这造就了Nginx的可移植性。

内存池设计

       以前第一次听说的时候总觉得这是个很高端的东西,我现在突然明白了为什么要有内存池设计了。很简单,就是把多次向系统申请内存的操作整合成一次,从而减少向操作系统申请内存的次数,以及避免出现内存碎片。

       内存池不负责回收内存池中已经分配出的内存。

       然后在代码结束的最后,再统一释放,即统一申请,统一释放。这样子,方便太多。。

使用统一管道过滤器模式的HTTP过滤模块

       其实就是过滤模块,这个完全可以看之前笔记《HTTP过滤模块》。每一个过滤模块的输入输出都是统一的接口,通过一种链表式的结构串联在一起,但每个模块又各自独立。

       整个过程就是一个过滤模块处理完输入的数据,然后通过统一接口传给下一个模块。

       这种统一管理过滤器的设计方式优点:
  1. 整个HTTP过滤系统的输入/输出简化为一个个过滤模块的简单组合;
  2. 提供很好的可重用性,可将任意两个HTTP过滤模块连接在一起;
  3. 容易维护与增强,在可验证性与可测试性上更加友好,可以灵活变动过滤模块流水线来验证功能;
  4. 完全支持并发执行;

其他一些用户模块

       Nginx还有许多特定的用户模块。这些模块用来改善或者提高Nginx设计上的关键点。
       比如, ngx_http_stub_status_module模块提供对所有HTTP连接状态的监控,提高Nginx的系统可见性。

总结


       Nginx的架构设计这一节的笔记,零散的用了两三天时间。先是自己过了一遍,笔记的过程中又详读。有些举例又通过画图重复理解一遍。所以,对Nginx的架构设计这一节基本掌握。总的来说两个重点吧:多阶段和异步处理、管理进程和多工作进程。

主要参考

《深入理解Nginx》

你可能感兴趣的:(深入理解Nginx,Nginx学习笔记系列)