Nginx服务器架构揭秘

Nginx服务器架构揭秘
Nginx (engine x)是一款免费的、开源的、高性能、轻量级的HTTP服务器、反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 Nginx以其高性能,稳定性,丰富的功能,简单的配置和低资源消耗而闻名。Nginx是一个Web服务器,也可以用作为反向代理,负载均衡器和HTTP缓存服务器。
很多高知名度的网站都使用Nginx,如:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx服务器架构揭秘

  1. Nginx的整体架构
    Nginx服务器架构揭秘
    1)Nginx启动时,会生成两种类型的进程,一个是主进程(Master),一个(windows版本的目前只有一个)或多个工作进程(Worker)。主进程并不处理网络请求,主要负责调度工作进程,也就是图示的三项:加载配置(Load configuration)、启动工作进程(Launch workers)及非停升级(Non-stop upgrade)。所以,Nginx启动以后,查看操作系统的进程列表,我们就能看到至少有两个Nginx进程。
    Nginx服务器架构揭秘
    2)服务器实际处理网络请求及响应的是工作进程(Worker),在类unix系统上,Nginx可以配置多个Worker,而每个Worker进程都可以同时处理数以千计的网络请求。
    3)模块化设计。Nginx的Worker,包括核心和功能性模块,核心模块负责维持一个运行循环(run-loop),执行网络请求处理的不同阶段的模块功能,如网络读写、存储读写、内容传输、外出过滤,以及将请求发往上游服务器等。而其代码的模块化设计,也使得我们可以根据需要对功能模块进行适当的选择和修改,编译成具有特定功能的服务器。
    4)基于异步及非阻塞的事件驱动模型,可以说是Nginx得以获得高并发、高性能的关键因素,同时也得益于对Linux、Solaris及类BSD等操作系统内核中事件通知及I/O性能增强功能的采用,如kqueue、epoll及event ports。
    5)代理(proxy)设计,可以说是Nginx深入骨髓的设计,无论是对于HTTP,还是对于FastCGI、memcache、Redis等的网络请求或响应,本质上都采用了代理机制。所以,Nginx天生就是高性能的代理服务器。
  2. Nginx的模块化设计
    高度模块化的设计是 Nginx 的架构基础。Nginx 服务器被分解为多个模块,每个模块就是一个功能模块,只负责自身的功能,模块之间严格遵循“高内聚,低耦合”的原则。
    Nginx服务器架构揭秘
    这5个模块从上到下重要性依次递减。
    (1)核心模块
    核心模块是Nginx服务器正常运行必不可少的模块,如同操作系统的内核。它提供了Nginx最基本的核心服务。像错误日志记录、配置文件解析、事件驱动机制、进程管理 等;
    (2)标准HTTP模块
    标准 HTTP 模块提供 HTTP 协议解析相关的功能,比如:端口配置、网页编码设置、HTTP 响应头设置等等;
    (3)可选HTTP模块
    可选 HTTP 模块主要用于 扩展 标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务,比如:Flash 多媒体传输、解析 GeoIP 请求、网络传输压缩、安全协议 SSL 支持等;
    (4)邮件服务模块
    邮件服务模块 主要用于支持 Nginx 的 邮件服务,包括对 POP3 协议、IMAP 协议和 SMTP 协议的支持;
    (5)第三方模块
    第三方模块 是为了扩展 Nginx 服务器应用,完成开发者自定义功能,比如:Json 支持、Lua 支持等。

3.Nginx的请求处理机制
Web服务器和客户端是一对多的关系,Web服务器必须有能力同时为多个客户端提供服务。一般来说完成并行处理请求工作有三种方式:
1.多进程方式;2.多线程方式;3.异步方式;
(1)多进程方式
多进程方式指,服务器每当收到一个客户端时。就有服务器主进程生成一个子进程出来和客户端建立连接进行交互。直到连接断开,该子进程就结束了。
多进程方式的优点是设计简单,各个子进程相对独立,处理客户端请求时彼此不受干扰;缺点是操作系统生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的开销;当有大量请求时,会导致系统性能下降;
(2)多线程方式
多线程方式指每当服务器接收到一个请求后,会由服务器主进程派生出一个线程出来和客户端进行交互。由于操作系统产生出一个线程的开销远远小于一个进程的开销。故多线程方式在很大程度上减轻了Web服务器对系统资源的要求。但同时由于多个线程位于一个进程内,可以访问同样的内存空间。所以需要开发者自己对内存进程管理,增大了开发难度。
(3)异步方式
异步方式适合多进程和多线程完全不同的一种处理客户端请求的方式。这里有几个概念我们需要熟悉一下:同步,异步,阻塞,非阻塞;
在网络通信中同步和异步是描述通信模式的概念。
同步:发送方发送完请求后,需要等待接收到接收方发回的响应,才能发送下一个请求;所有请求在服务端得到同步,发送方和接收方的步调是一致的;
异步:和同步机制相反,在异步机制中,发送方发出一个请求后,不等接收方响应这个请求,就继续发送下一个请求;所有来自发送方的请求形成一个队列,接收方处理完成后通知发送方;
在进程处理调度方式上用阻塞与非阻塞。在网络通信中主要指套接字Socket的阻塞和非阻塞,而Socket的实质就是IO操作。
阻塞:调用结果返回之前,当前线程从运行状态被挂起,一直等到调用结果返回之后,才进入就绪状态,获取CPU后继续执行。
非阻塞:和阻塞方式正好相反,如果调用结果不能马上返回,当前线程也不会马上返回,而是立即返回执行下一个调用。
因此就衍生出4中方式:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞。
这里简单解释一下异步非阻塞:发送方向接收方发送请求后,不用等待响应,可以继续其他工作;接收方处理请求时进行的IO操作如果不能马上得到结果,也不必等待,而是马上返回去去做其他事情。当IO操作完成以后,将完成状态和结果通知接收方,接收方再响应发送方。
Nginx服务器的一个显著的优势就是能够同时处理大量的并发请求。它结合多进程机制和异步机制。异步机制使用的是异步非阻塞方式。
多进程
服务器每当收到一个客户端时。就有服务器主进程(Master process)生成一个子进程(Worker process)出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。使用进程的好处是各个进程之间相互独立,不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。其次,采用独立的进程,可以让进程互相之间不会影响,如果一个进程发生异常退出时,其它进程正常工作,master 进程则很快启动新的 Worker 进程,确保服务不会中断,将风险降到最低。缺点是操作系统生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的开销;当有大量请求时,会导致系统性能下降。
异步非阻塞
每个工作进程使用异步非阻塞方式,可以处理多个客户端请求。当某个工作进程接收到客户端的请求以后,调用 IO 进行处理,如果不能立即得到结果,就去处理其他的请求(即为非阻塞);而客户端在此期间也无需等待响应,可以去处理其他事情(即为异步);当 IO 返回时,就会通知此工作进程;该进程得到通知,暂时挂起当前处理的事务去响应客户端请求。
4. Nginx事件驱动模型
在Nginx的异步非阻塞机制中,工作进程在调用 IO 后,就去处理其他的请求,当 IO 调用返回后,会通知该工作进程。对于这样的系统调用,主要使用Nginx服务器的事件驱动模型来实现。
Nginx服务器架构揭秘
如上图所示,Nginx的事件驱动模型由事件收集器、事件发送器和事件处理器三部分基本单元组成。其中,事件收集器负责收集Worker进程的各种IO请求,事件发送器负责将 IO 事件发送到事件处理器,而事件处理器负责各种事件的响应工作。
事件发送器将每个请求放入一个待处理事件的列表,使用非阻塞I/O方式调用“事件处理器”来处理该请求。其处理方式称为“多路IO复用方法”,常见的包括以下三种:select模型、poll模型、epoll模型。
select,poll,epoll都是IO多路复用的机制。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的。
5. Nginx进程处理模型
Nginx 服务器使用Master/Worker多进程模式。多线程启动和执行的流程如下:
主程序 Master process 启动后,通过一个for循环来接收和处理外部信号;
主进程通过 fork() 函数产生Worker子进程,每个子进程执行一个 for 循环来实现Nginx服务器对事件的接收和处理。
一般推荐 worker 进程数与CPU内核数一致,这样一来不存在大量的子进程生成和管理任务,避免了进程之间竞争CPU资源和进程切换的开销。而且 Nginx 为了更好的利用多核特性,提供了 CPU亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效。
对于每个请求,有且只有一个工作进程对其处理。首先,每个Worker进程都是从Master进程fork过来,在Master进程里面,先建立好需要listen的Socket(listenfd)之后,然后再fork出多个Worker进程。所有Worker进程的 listenfd 会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有Worker进程在注册 listenfd 读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。当一个Worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由Worker进程来处理,而且只在一个Worker进程中处理。
Nginx服务器架构揭秘
在 Nginx 服务器的运行过程中,主进程和工作进程需要进程交互。交互依赖于Socket实现的管道来实现。
Master-Worker交互
这条管道与普通的管道不同,它是由主进程指向工作进程的单向管道,包含主进程向工作进程发出的指令,工作进程 ID等;同时主进程与外界通过信号通信;每个子进程具备接收信号,并处理相应的事件的能力。
Worker-Worker交互
这种交互是和 Master-Worker交互是基本一致的,但是会通过主进程。工作进程之间是相互隔离的,所以当工作进程W1需要向工作进程W2发指令时,首先找到W2的进程ID,然后将正确的指令写入指向W2的通道。W2收到信号采取相应的措施。
小结
通过这篇文章,我们对Nginx服务器的整体架构有了一个整体的认识。包括其模块化的设计、多进程和异步非阻塞的请求处理方式、事件驱动模型等。通过这些理论知识,对于我们以后学习Nginx的源码有很大的帮助;也推荐大家多看看Nginx的源码,才能更好地领悟 Nginx的设计思想。
随书分享Nginx学习相关书籍(请关注下方微信公众号,输入分享码获取书籍提取码)
深入理解Nginx模块开发与架构解析 分享码:701210
精通Nginx(第2版) 分享码:701223
决战Nginx: 系统卷 - 高性能Web服务器详解与运维 分享码:701335
Nginx服务器架构揭秘