第8章 Nginx基础架构

在本书的第二部分,我们已经学习了如何开发HTTP模块,这使得我们可以实现高性能、定制化的Web服务器功能。不过,Nginx自身是高度模块化设计的,它给予了每一个基本的Nginx模块足够的灵活性,也就是说,我们不仅仅能开发HTTP模块,还可以方便地开发任何基于TCP的模块,甚至可以定义一类新的Nginx模块,就像HTTP模块、mail模块曾经做过的那样。任何我们能想到的功能,只要符合本章中描述的Nginx设计原则,都可以以模块的方式添加到Nginx服务中,从而提供强大的Web服务器。

另外,Nginx的BSD许可证足够开放和自由,因此,当Nginx的一些通用功能与要求不符合我们的想象时,还可以尝试着直接更改它的官方代码,从而更直接地达到业务要求。同时,Nginx也处于快速的发展中,代码中免不了会有一些Bug,如果我们对Nginx的架构有充分的了解,也可以积极地协助完善Nginx框架代码。

以上这些方向,都需要我们在整体上对Nginx的架构有清晰的认识。因此,本章的写作目的只有两个:

❑对Nginx的设计思路做一个概括性的说明,帮助读者了解Nginx的设计原则(见8.1节和8.2节)。

❑将从具体的框架代码入手,讨论Nginx如何启动、运行和退出,这里会涉及具体实现细节,如master进程如何管理worker进程、每个模块是如何加载到进程中的等(见8.3节~8.6节)。

通过阅读本章内容,我们将会对Nginx这个Web服务器有一个全面的认识,并对日益增长的各种Nginx模块与核心模块的关系有一个大概的了解。另外,本章内容将为下一章(事件模块)以及后续章节中HTTP模块的学习打下基础。

8.1 Web服务器设计中的关键约束

Nginx是一个功能堪比Apache的Web服务器。然而,在设计时,为了使其能够适应互联网用户的高速增长及其带来的多样化需求,在基本的功能需求之外,还有许多设计约束。Nginx作为Web服务器受制于Web传输协议自身的约束,另外,下面将说明的7个关注点也是Nginx架构设计中的关键约束,本章会分节简要介绍这些概念。在8.2节中,我们将带着这些问题再看一下Nginx是如何有效提升这些关注点属性的。

1.性能

性能是Nginx的根本,如果性能无法超越Apache,那么它也就没有存在的意义了。这里所说的性能主体是Web服务器,因此,性能这个概念主要是从网络角度出发的,它包含以下3个概念。

(1)网络性能

这里的网络性能不是针对一个用户而言的,而是针对Nginx服务而言的。网络性能是指在不同负载下,Web服务在网络通信上的吞吐量。而带宽这个概念,就是指在特定的网络连接上可以达到的最大吞吐量。因此,网络性能肯定会受制于带宽,当然更多的是受制于Web服务的软件架构。

在大多数场景下,随着服务器上并发连接数的增加,网络性能都会有所下降。目前,我们在谈网络性能时,更多的是对应于高并发场景。例如,在几万或者几十万并发连接下,要求我们的服务器仍然可以保持较高的网络吞吐量,而不是当并发连接数达到一定数量时,服务器的CPU等资源大都浪费在进程间切换、休眠、等待等其他活动上,导致吞吐量大幅下降。

(2)单次请求的延迟性

单次请求的延迟性与上面说的网络性能的差别很明显,这里只是针对一个用户而言的。对于Web服务器,延迟性就是指服务器初次接收到一个用户请求直至返回响应之间持续的时间。

服务器在低并发和高并发连接数量下,单个请求的平均延迟时间肯定是不同的。Nginx在设计时更应该考虑的是在高并发下如何保持平均时延性,使其不要上升得太快。

(3)网络效率

网络效率很好理解,就是使用网络的效率。例如,使用长连接(keepalive)代替短连接以减少建立、关闭连接带来的网络交互,使用压缩算法来增加相同吞吐量下的信息携带量,使用缓存来减少网络交互次数等,它们都可以提高网络效率。

2.可伸缩性

可伸缩性指架构可以通过添加组件来提升服务,或者允许组件之间具有交互功能。一般可以通过简化组件、降低组件间的耦合度、将服务分散到许多组件等方法来改善可伸缩性。可伸缩性受到组件间的交互频率,以及组件对一个请求是使用同步还是异步的方式来处理等条件制约。

3.简单性

简单性通常指组件的简单程度,每个组件越简单,就会越容易理解和实现,也就越容易被验证(被测试)。一般,我们通过分离关注点原则来设计组件,对于整体架构来说,通常使用通用性原则,统一组件的接口,这样就减少了架构中的变数。

4.可修改性

简单来讲,可修改性就是在当前架构下对于系统功能做出修改的难易程度,对于Web服务器来说,它还包括动态的可修改性,也就是部署好Web服务器后可以在不停止、不重启服务的前提下,提供给用户不同的、符合需求的功能。可修改性可以进一步分解为可进化性、可扩展性、可定制性、可配置性和可重用性,下面简单说明一下这些概念。

(1)可进化性

可进化性表示我们在修改一个组件时,对其他组件产生负面影响的程度。当然,每个组件的可进化性都是不同的,越是核心的组件其可进化性可能会越低,也就是说,对这个组件的功能做出修改时可能同时必须修改其他大量的相关组件。

对于Web服务器来说,“进化”这个概念按照服务是否在运行中又可以分为静态进化和动态进化。优秀的静态进化主要依赖于架构的设计是否足够抽象,而动态进化则不然,它与整个服务的设计都是相关的。

(2)可扩展性

可扩展性表示将一个新的功能添加到系统中的能力(不影响其他功能)。与可进化性一样,除了静态可扩展性外,还有动态可扩展性(如果已经部署的服务在不停止、不重启情况下添加新的功能,就称为动态可扩展性)。

(3)可定制性

可定制性是指可以临时性地重新规定一个组件或其他架构元素的特性,从而提供一种非常规服务的能力。如果某一个组件是可定制的,那么是指用户能够扩展该组件的服务,而不会对其他客户产生影响。支持可定制性的风格一般会提高简单性和可扩展性,因为通常情况下只会实现最常用的功能,不太常用的功能则交由用户重新定制使用,这样组件的复杂性就降低了,整个服务也会更容易扩展。

(4)可配置性

可配置性是指在Web服务部署后,通过对服务提供的配置文件进行修改,来提供不同的功能。它与可扩展性、可重用性相关。

(5)可重用性

可重用性指的是一个应用中的功能组件在不被修改的情况下,可以在其他应用中重用的程度。

5.可见性

在Web服务器这个应用场景中,可见性通常是指一些关键组件的运行情况可以被监控的程度。例如,服务中正在交互的网络连接数、缓存的使用情况等。通过这种监控,可以改善服务的性能,尤其是可靠性。

6.可移植性

可移植性是指服务可以跨平台运行,这也是当下Nginx被大规模使用的必要条件。

7.可靠性

可靠性可以看做是在服务出现部分故障时,一个架构容易受到系统层面故障影响的程度。可以通过以下方法提高可靠性:避免单点故障、增加冗余、允许监视,以及用可恢复的动作来缩小故障的范围。