概叙
Nginx是什么
Nginx(engine X)是一个开源的轻量级的HTTP服务器,能够提供高性能的HTTP和反向代理服务。
与传统的Apache服务器相比,在性能上Nginx占用系统资源更小、支持高并发,访问效率更高;在功能上,Nginx不仅作为Web服务软件,还适用于反向代理、负载均衡等场景;在安装配置上,Nginx更为简单、灵活。
Nginx因为并发性能和资源占用上的优势,已经广泛用于大中型互联网企业。
Nginx特点
-
支持高并发:Nginx是专门为性能优化而开发的,采用内核Poll模型,单机能够支持几万以上的并发连接;nginx支持高并发连接,处理2-3万并发连接数,官方监测能支持5万并发。对HTTP并发连接的处理能力高,单台物理服务器可支持30000~50000个并发请求。(实际操作,很多公司为了服务器的稳定,都会设置在20000个左右)
-
低资源消耗:Nginx采取了分阶段资源分配技术,使得CPU与内存的占用率非常低。一般1万个非活跃的HTTP Keep-Alive连接在Nginx中仅消耗几MB内存;可以跨平台,配置简单,内存消耗少,10个nginx才占用150M内存。
-
低成本,高稳定:成本低,且开源,稳定性高,宕机概率非常小;
-
高拓展性:设计极具扩展性,由多个不同功能、不同层次、不同类型且耦合度极低的模块组成
-
高可用性:Nginx支持热部署,其中的master管理进程与worker工作进程的分离设计;启动速度特别迅速,因此可以在不间断服务的情况下,对软件版本或者配置进行升级,即使运行数月也无需重新启动,几乎可以做到7x24小时不间断地运行
-
丰富的使用场景:可以作为Web服务端、HTTP反向代理、负载均衡和前端缓存服务等场景使用
-
开源协议:使用BSD许可协议,免费使用,且可修改源码。Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。
-
异步非阻塞网络模型:使用的是epoll模型,这种模型是I/O多路复用技术(I/O多路复用是一种技术,它允许一个进程或线程监控多个网络连接,当其中某个或某几个连接有数据时,当前程序可以拿到网卡收到的数据进行下一步的处理;),异步非阻塞的模型(异步非阻塞模型可以提高程序的效率,在等待I/O操作完成的同时,可以继续执行其他代码。)
-
健康监测:内置的健康检查功能,可以允许在服务器宕机的时候,做健康检查,再发送的请求就不会发给宕机的服务器,会重新提交到其他节点上。
Nginx源码的目录结构:
.
├── auto 自动检测系统环境以及编译相关的脚本
│ ├── cc 关于编译器相关的编译选项的检测脚本
│ ├── lib nginx编译所需要的一些库的检测脚本
│ ├── os 与平台相关的一些系统参数与系统调用相关的检测
│ └── types 与数据类型相关的一些辅助脚本
├── conf 存放默认配置文件,在make install后,会拷贝到安装目录中去
├── contrib 存放一些实用工具,如geo配置生成工具(geo2nginx.pl)
├── html 存放默认的网页文件,在make install后,会拷贝到安装目录中去
├── man nginx的man手册
└── src 存放nginx的源代码
├── core nginx的核心源代码,包括常用数据结构的定义,以及nginx初始化运行的核心代码如main函数
├── event 对系统事件处理机制的封装,以及定时器的实现相关代码
│ └── modules 不同事件处理方式的模块化,如select、poll、epoll、kqueue等
├── http nginx作为http服务器相关的代码
│ └── modules 包含http的各种功能模块
├── mail nginx作为邮件代理服务器相关的代码
├── misc 一些辅助代码,测试c++头的兼容性,以及对google_perftools的支持
└── os 主要是对各种不同体系统结构所提供的系统函数的封装,对外提供统一的系统调用接口
1. Nginx架构简介
换张中文图,Nginx架构更容易理解
Master 进程
Master 进程:
- Nginx 的运行始于一个 master 进程,它负责管理所有的工作进程。
- master 进程负责读取和解析配置文件,并启动工作进程。
当 Nginx 启动时,它会生成两种类型的进程:主进程(master)和工作进程(worker)。
主进程并不处理网络请求,而是负责调度工作进程,包括加载配置、启动工作进程以及进行非停升级。
因此,当 Nginx 启动后,查看操作系统的进程列表,至少会有两个 Nginx 进程。
工作进程
工作进程:
- 一旦 master 进程启动,它会生成一组工作进程。
- 每个工作进程都是独立运行的,负责处理来自客户端的连接和请求。
- 工作进程之间相互独立,可以并行处理请求,提高了 Nginx 的性能和吞吐量。
服务器实际 处理网络请求 及 响应 的是 工作进程(worker
),在类 unix
系统上,Nginx
可以配置 多个 worker
,而每个 worker
进程 都可以同时处理 数以千计 的 网络请求。
每个工作进程在启动时都会复制主进程的配置信息和相关资源,但它们彼此之间是相互独立的,这意味着它们可以并行地处理请求,互不影响。
此外,每个工作进程还会维护一个事件驱动的事件循环,通过事件驱动机制处理来自客户端的连接请求、数据读取和响应发送,这种异步非阻塞的 I/O 模型确保了 Nginx 的高性能和低资源消耗。
模块化设计
Nginx 核心模块:
- Nginx 的核心模块包括 HTTP 模块、事件模块、解析器模块等。
- HTTP 模块处理 HTTP 请求和响应,包括 HTTP 头部解析、HTTP 请求方法解析、URI 解析等。
- 事件模块负责处理底层的事件通知机制,如 Epoll、Kqueue 等。
- 解析器模块负责解析 Nginx 配置文件。
Nginx 的 worker 进程分为核心模块和功能性模块。
核心模块主要负责维持一个运行循环(run-loop),在其中执行网络请求处理的不同阶段的模块功能,如网络读写、存储读写、内容传输、外出过滤,以及将请求发往上游服务器等。
Nginx 的代码采用了模块化设计,这使得我们可以根据需要选择和修改功能模块,然后编译成具有特定功能的服务器。
事件驱动模型
事件驱动模型:
- Nginx 采用了事件驱动的模型,主要利用了操作系统提供的异步 I/O 机制。
- 当有新的连接建立或者数据可读写时,Nginx 不会阻塞等待,而是通过事件通知机制处理这些事件,从而提高了处理效率。
Nginx 实现了高并发、高性能的关键在于其基于异步及非阻塞的事件驱动模型。
这种模型使得 Nginx 能够高效地处理大量并发请求,而不会因为阻塞等待而降低性能。
此外,Nginx 还充分利用了 Linux、Solaris 以及类 BSD 等操作系统内核中提供的事件通知和 I/O 性能增强功能,如 kqueue、epoll 以及 event ports,进一步提升了其性能表现。
代理设计
Nginx 作为高性能的代理服务器,其代理原理是其设计的核心之一。
无论是针对 HTTP 还是其他协议(如 FastCGI、Memcache、Redis等)的网络请求或响应,Nginx 都采用了代理机制来实现数据的转发和处理。
Nginx 的代理原理主要基于以下几个关键点:
- 接收请求:当 Nginx 接收到客户端的请求时,根据配置文件中的代理设置,确定是否需要进行代理转发。如果需要代理转发,则根据配置选择合适的代理方式。
- 建立连接:Nginx 会与目标服务器建立连接,可以是与远程服务器建立 TCP 连接,也可以是与本地应用程序之间建立的 Unix Socket 连接,取决于代理目标的具体情况。
- 数据传输:一旦连接建立成功,Nginx 会将客户端的请求数据转发给目标服务器,并且在接收到目标服务器的响应后,再将响应数据返回给客户端。这个过程可以是全双工的,意味着 Nginx 可以同时接收客户端请求和目标服务器响应,然后进行相应的转发和处理。
- 代理缓存:为了进一步提高性能,Nginx 还支持代理缓存功能。它可以将经常请求的数据缓存在本地,避免每次请求都要向后端服务器发起请求,从而减少响应时间和网络负载。
- 负载均衡:对于需要代理转发的请求,Nginx 还支持负载均衡功能,可以根据一定的策略将请求分发到多个后端服务器上,以实现负载均衡和高可用性。
2. Nginx架构详解
工作流程:
- 当有新的 HTTP 请求到达时,master 进程会将其分发给一个工作进程。
- 工作进程处理请求,根据配置文件进行请求的处理,包括反向代理、负载均衡、静态文件服务等。
- 处理完成后,工作进程将响应返回给客户端。
2.1 Nginx进程模型
Nginx默认采用多进程工作方式,在Nginx启动后,会运行一个master进程和多个worker进程。
- master主要用来管理worker进程,充当整个进程组与用户的交互接口,同时对进程进行监护,实现worker进程的重启服务、平滑升级、更换日志文件、配置文件实时生效等功能;
- worker进程用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。一个请求只能在一个worker进程中处理,一个worker进程不可能处理其它worker进程中的请求。
另外在Nginx架构中还有Cache Loader和Cache Manager进程,Cache Loader进程加载缓存索引文件信息;Cache Manager进程管理磁盘的缓存大小,超过预定值大小后最小使用的数据将被删除。
2.1.1 Master管理进程
Master进程主要用来管理worker进程,具体包括如下4个主要功能:
- 接收来自外界的信号;
- 向各worker进程发送信号;
- 监控woker进程的运行状态;
- 当woker进程退出后(异常情况下),会自动重新启动新的woker进程。
Master进程接受到命令重启Nginx进程(./nginx -s reload),会按照以下流程:
1) 首先master进程在收到重启命令后,会先重新加载配置文件,然后再启动新的worker进程,并向所有老的worker进程发送信号,告诉他们可以光荣退休了。
2) 新的worker进程在启动后,就开始接收新的请求,而老的worker在收到来自master的信号后,就不再接收新的请求,并且处理完当前进程中的所有未处理完的请求后,再退出。
2.1.2 Worker工作进程
Worker工作进程之间是对等的,每个进程处理请求的机会也是一样的。Nginx采用异步非阻塞的方式来处理网络事件,具体流程如下:
1) 接收请求:首先,每个worker进程都是从master进程fork过来,在master进程建立好需要listen的socket(listenfd)之后,然后再fork出多个worker进程。
a) 所有worker进程的listenfd会在新连接到来时变得可读,每个work进程都可以去accept这个socket(listenfd)。
b) 当一个client连接到来时,所有accept的work进程都会受到通知,但只有一个进程可以accept成功,其它的则会accept失败。
c) 为保证只有一个进程处理该连接,Nginx提供了一把共享锁accept_mutex来保证同一时刻只有一个work进程在accept连接。
d) 所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。
2) 处理请求:当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接。
由上可以看出,一个请求完全由worker进程处理,并且只在一个worker进程中处理。
2.2 Nginx请求处理流程
Nginx工作进程会监听套接字上的事件(accept_mutex和kernel socketsharding),来决定什么时候开始工作。
事件是由新的连接初始化的,这些连接会被分配给状态机,Nginx中有三大类状态机:处理应用层的HTTP状态机、处理TCP/UDP的4层的传输层状态机和处理邮件的MAIL状态机,其中HTTP状态机最为常见。
在多种流量进入Nginx后,Nginx的三种状态机在Nginx解析出请求后,会动用线程池处理调用,将静态资源、反向代理、错误日志等信息分别导向不同的出口,比如fastcgi会导向PHP处理、html会导向nginx处理,并将处理请求日志记录到本地或远程服务器中。
可参考:nginx架构&&基本数据结构&&配置&&模块&&请求详解-CSDN博客
处理流程图:
在nginx中我们指的是http请求,具体到nginx中的数据结构是ngx_http_request_t。ngx_http_request_t是对一个http请求的封装。 我们知道,一个http请求,包含请求行、请求头、请求体、响应行、响应头、响应体。
http请求是典型的请求-响应类型的的网络协议,而http是文件协议,所以我们在分析请求行与请求头,以及输出响应行与响应头,往往是一行一行的进行处理。
- 如果我们自己来写一个http服务器,通常在一个连接建立好后,客户端会发送请求过来。
- 然后我们读取一行数据,分析出请求行中包含的method、uri、http_version信息。
- 然后再一行一行处理请求头,并根据请求method与请求头的信息来决定是否有请求体以及请求体的长度,然后再去读取请求体。
- 得到请求后,我们处理请求产生需要输出的数据,然后再生成响应行,响应头以及响应体。
- 在将响应发送给客户端之后,一个完整的请求就处理完了。
- 当然这是最简单的webserver的处理方式,其实nginx也是这样做的,只是有一些小小的区别,比如,当请求头读取完成后,就开始进行请求的处理了。
- nginx通过ngx_http_request_t来保存解析请求与输出响应相关的数据。
2.3 Nginx多进程IO模型
2.3.1 Nginx多进程模型
Nginx默认使用多进程的工作方式,相比较多线程的方式,有以下好处:
1) 首先,对于每个worker进程来说,独立的进程不需要加锁,所以省掉了锁带来的开销,同时在编程以及问题查找时,也会方便很多;
2) 其次,采用独立的进程,可以让进程之间相互不会影响,一个进程退出后,其它进程还在工作,服务也不会中断,master进程则很快启动新的worker进程;
3) 再次,为Nginx热部署提供了支持。在修改配置文件nginx.conf后,重新生成新的worker进程,新的worker进程会以新的配置处理请求,而老的worker进程,等把以前的请求处理完成以后,kill掉就可以。
2.3.2 Nginx异步非阻塞事件模型
异步非阻塞事件是怎么回事?
先看一个请求的完整过程,首先请求过来建立连接,然后再接收数据再发送数据,具体到系统层就是IO读写事件。当读写事件没有准备好,如果不采用非阻塞的方式,就得阻塞调用,阻塞调用会进入内核等待,导致CPU资源被其它进程占用。当并发请求越大时,等待的事件越多,CPU利用不上去,并发也上不去。因此Nginx使用非阻塞的事件模型,系统中事件模型有很多中,比如select/poll/kqueue/epoll等,Nginx采用epoll模型。
Epoll模型基于事件驱动机制,可以监控多个事件是否准备完毕,如果可以,就放入epoll队列,这个过程是异步的,worker进程只需要从epoll队列循环处理即可。
Epoll调用过程如下图所示:
事件驱动&异步非阻塞:
本质来说,事件驱动是一种思想(事实上它不仅仅局限于编程) ,事件驱动思想是实现 异步非阻塞特性 的一个重要手段。
对于web服务器来说,造成性能拉胯不支持高并发的常见原因就是由于使用了传统的I/O模型造成在内核没有可读/可写事件(或者说没有数据可供用户进程读写)时,用户线程 一直在等待(其他事情啥也干不了就是干等等待内核上的数据可读/可写),这样的话其实是一个线程(ps:线程在Linux系统也是进程)对应一个请求,请求是无限的,而线程是有限的从而也就形成了并发瓶颈。
而大佬们为了解决此类问题,运用了事件驱动思想来对传统I/O模型做个改造,即在客户端发起请求后,用户线程不再阻塞等待内核数据就绪,而是立即返回(可以去执行其他业务逻辑或者继续处理其他请求)。
当内核的I/O操作完成后,内核系统会向用户线程发送一个事件通知,用户线程才来处理这个读/写操作,之后拿到数据再做些其他业务后响应给客户端,从而完成一次客户端请求的处理。
事件驱动的I/O模型中,程序不必阻塞等待I/O操作的完成,也无需为每个请求创建一个线程,从而提高了系统的并发处理能力和响应速度。
事件驱动型的I/O模型通常也被被称为I/O多路复用,即这种模型可以在一个线程中,处理多个连接(复用就是指多个连接复用一个线程,多路也即所谓的 多个连接),通过这种方式避免了线程间切换的开销,同时也使得用户线程不再被阻塞,提高了系统的性能和可靠性。
nginx支持事件驱动是因为他利用了操作系统提供的I/O多路复用接口,如Linux系统中,常用的I/O多路复用接口有select/poll,epoll。
这些接口可以监视多个文件描述符的状态变化,当文件描述符可读或可写时,就会向用户线程发送一个事件通知。
用户线程通过事件处理机制(读取/写入数据)来处理这个事件,之后进行对应的业务逻辑完了进行响应。
简单一句话概括: 事件驱动机制就是指当有读/写/连接事件就绪时 再去做读/写/接受连接这些事情,而不是一直在那里傻傻的等,也正应了他的名词: 【事件驱动!】,基于事件驱动思想设计的多路复用I/O(如select/poll,epoll),相对于传统I/O模型,达到了异步非阻塞的效果!
既然提到了select/poll,epoll 那么我们就简单说一下(注意我这里是简单描述,后续有时间会对相关知识点从源码层面做个系统的整理和图解):
select: 将已连接的 Socket 都放到一个文件描述符集合,然后用户态调用 select 函数将文件描述符集合拷贝到内核里,让内核来检查是否有网络事件产生,检查的方式很粗暴,就是通过遍历文件描述符集合的方式,当检查到有事件产生后,将此 Socket 标记为可读或可写, 接着再把整个文件描述符集合拷贝回用户态里,然后用户态还需要再通过遍历的方法找到可读或可写的 Socket,然后再对其处理。
poll: poll函数的话其实和select大差不差,唯一区别可能就是socket列表的结构有所不同,不再受FD_SETSIZE的限制。这里就不多说了。
epoll: epoll在前边两者的基础上做了很大的优化,select/poll都需要遍历整个socket列表,当检测到传入的socket可读/可写时,则copy socket列表给用户空间,用户态仍然需要遍历(因为内核copy给用户态的是整个socket列表) ,而epoll则是通过红黑树结构将需要监控的socket插入到进去,然后当有socket可读时会通过回调机制来将其添加到可读列表中,然后内核将可读列表copy给用户态即可(据说此处使用了mmap这里我们不去验证探究,后续写相关文章时在深究吧),整个过程少了无效的遍历以及不用copy整个socket集合。
2.3.3 Nginx请求响应过程种的 I/O过程
- 用户态:应用程序,我们可以控制
- 内核态:操作系统层面,我们不容易去控制的 操作系统
- ① 当用户发起 http 请求需要请求一个index.html 网页文件
- ② 客户端请求与服务器端建立连接,建立连接后,会发送请求报文
- ③ 服务端的网卡收到请求报文,会将该报文复制到内核空间,内核空间分析报文后交给对应的程序。nginx 分析该报文,将报文和自己的配置文件,一一比对,按照配置文件完成请求,分析后发现客户需要 index.html 文件
- ④ 由于程序的权限问题,没有资格直接调用磁盘上的文件,程序会再将这个请求再次转发给内核,内核得到后请求去磁盘上找文件,找到文件后复制给程序 nginx
- ⑤ 程序会构建响应报文,构建好后再交给内核空间
- ⑥ 内核空间得到响应报文后,再交给网卡发给客户
2.3.4 零拷贝技术
在传统的数据传输过程中,数据通常需要经过多次复制。
比如,当数据从磁盘读取到内存时,首先将数据读入内核缓冲区,然后再从内核缓冲区复制到用户空间的应用程序缓冲区。
零拷贝技术通过避免或减少数据在内存和设备之间的多次复制来提高效率。
具体做法包括直接内存访问(DMA)、文件映射(mmap)和发送文件(sendfile)等。
MMAP ( Memory Mapping )
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问。
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。
直接内存访问(DMA)
文件映射(mmap)
发送文件(sendfile)
Nginx 的零拷贝技术主要是指在网络 I/O 操作中,使用 sendfile 方法来提高性能,这样可以避免用户空间和内核空间之间的数据拷贝,从而提高系统的吞吐量。
零拷贝技术的核心函数是 sendfile()
,它在 Linux 2.0 版本以上的操作系统中得到支持。当 Nginx 配置文件中启用了 sendfile
指令时,Nginx 会使用这个系统调用来输出静态文件。
以下是一个简单的 Nginx 配置示例,其中启用了 sendfile:
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
sendfile on; # 启用 sendfile 方法
}
}
在这个配置中,当客户端请求静态文件时,Nginx 会直接将文件内容通过 sendfile()
系统调用发送给客户端,而不是将文件内容读取到用户空间的缓冲区中,再从用户空间的缓冲区拷贝到内核空间去发送。这样就减少了数据拷贝的次数和 CPU 的参与,从而提高了性能。
2.3.5 Nginx为什么不使用多线程?
Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存(线程要比进程小的多,所以worker支持比perfork高的并发),并发过大会耗光服务器资源
Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换。所以才使得Nginx支持更高的并发。
2.3.6 Nginx 是如何实现高并发的?
1.异步,非阻塞,使用了epoll 和大量的底层代码优化
如果一个server采用一个进程负责一个request的方式,那么进程数就是并发数。正常情况下,会有很多进程一直在等待中
而nginx采用一个master进程,多个woker进程的模式
master进程主要负责收集、分发请求。每当一个请求过来时,master就拉起一个worker进程负责处理这个请求。同时master进程也负责监控woker的状态,保证高可靠性
woker进程一般设置为跟cpu核心数一致。nginx的woker进程在同一时间可以处理的请求数只受内存限制,可以处理多个请求
Nginx 的异步非阻塞工作方式正把当中的等待时间利用起来了。在需要等待的时候,这些进程就空闲出来待命了,因此表现为少数几个进程就解决了大量的并发问题
2.同步和异步
同步:一个服务的完成需要依赖其他服务时,只有等待被依赖的服务完成后,才算完成,这是一种可靠的服务序列。要么成功都成功,失败都失败,服务的状态可以保持一致
异步:一个服务的完成需要依赖其他服务时,只通知其他依赖服务开始执行,而不需要等待被依赖的服务完成,此时该服务就算完成了。被依赖的服务是否最终完成无法确定,因此它是一个不可靠的服务序列
3.阻塞与非阻塞
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起,一直处于等待消息通知,不能够执行其他业务,函数只有在得到结果之后才会返回。
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回
3.Nginx功能模块和应用场景
3.1 Nginx功能模块说明
Nginx由内核和模块组成,其中内核在设计上非常简洁,完成的工作非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block,而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作。
nginx 六大模块
- 核心模块:是 Nginx 服务器正常运行必不可少的模块,提供错误日志记录 、配置文件解析 、事件驱动机制 、进程管理等核心功能
- 标准HTTP模块:提供 HTTP 协议解析相关的功能,比如: 端口配置 、 网页编码设置 、 HTTP响应头设置 等等
- 可选HTTP模块:主要用于扩展标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务,比如:Flash 多媒体传输 、解析 GeoIP 请求、 网络传输压缩 、 安全协议 SSL 支持等
- 邮件服务模块:主要用于支持 Nginx 的 邮件服务 ,包括对 POP3 协议、 IMAP 协议和 SMTP协议的支持
- Stream服务模块: 实现反向代理功能,包括TCP协议代理 反向
- 第三方模块:是为了扩展 Nginx 服务器应用,完成开发者自定义功能,比如: Json 支持、 Lua 支持等
nginx高度模块化,但其模块早期不支持DSO机制;1.9.11 版本支持动态装载和卸载
3.1.1 Nginx模块分类
Nginx的模块从结构上分为核心模块、基础模块和第三方模块,其中用户根据自己的需要开发的模块都属于第三方模块:
-
核心模块:HTTP模块、EVENT模块和MAIL模块
-
基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块;
-
第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。
3.1.2 Nginx模块功能
Nginx模块常规的HTTP请求和响应的过程如上图所示,Nginx模块从功能上分为以下三类:
-
Handlers处理器模块:此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。
-
Filters过滤器模块:此类模块主要对其他处理器模块输出的内容进行修改操作,最后由Nginx输出。
-
Proxies代理类模块:此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。
Nginx本身处理的工作很少,当它接到一个HTTP请求时,通过查找配置文件将此次请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作。
3.2 Nginx使用场景
首先,我们一般会将请求打到Nginx, 再把请求转发到我们的应用服务。
比如我们常用的php-fpm/golang程序或者tomcat,再由应用服务访问缓存,数据库等存储以提供基本的数据服务能力。
由于我们开发过程中要求应用程序开发效率较高,但其运行效率是较低的。
单个应用程序的qps,tps都是受限的,不足以支撑用户的请求量,那么为了提高整个服务的吞吐能力,就需要将多个应用程序组成一个集群来整体向外提供高可用服务。这样就会延伸出来2个需求,1.负载均衡,2.当有个别应用程序出问题的时候,需要做容灾。那么我们的反向代理就需要具备负载均衡的能力。
其次,Nginx一般处于边缘节点,离用户最近,我们可以将一些热点数据缓存在Nginx中,直接向用户提供访问,从而达到减少用户时延的效果。这也就衍生出了缓存功能。
第三,当应用程序的性能不及缓存,数据库的性能时,有一些接口我们可以由Nginx直接访问数据库,redis,第三方应用服务。如:使用Openresty,lua等。
第四,我们还可以将css, js, 小图片等静态资源直接放到Nginx服务中,没有必要再次请求应用服务。
3.2.1 静态文件服务
静态文件服务:Nginx在提供静态资源服务方面效率很高,可以快速的响应大量的静态请求,减轻其他动态服务器的负担,如CSS、JavaScript、Image、Audio和Video文件等。
以下是一个简单的配置示例,用于设置Nginx以服务静态文件:
server {
listen 80;
server_name example.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ =404;
}
}
解释:
-
listen 80;
表示Nginx监听80端口。 -
server_name example.com;
表示对应的域名。 -
location / { ... }
定义了一个处理所有请求的区块。 -
root /usr/share/nginx/html;
指定了静态文件存放的根目录。 -
index index.html index.htm;
指定了默认页面。 -
try_files $uri $uri/ =404;
尝试按顺序提供请求的文件,如果找不到文件则返回404错误。
确保/usr/share/nginx/html
目录包含您的静态文件,并根据需要调整server_name
和root
指令。
3.2.2 缓存服务器
缓存服务器:Nginx可以缓存一些响应结果,降低后端服务器的负载,提高数据的访问速度,平衡访问压力等。
Nginx 可以用作缓存服务器,通过配置 ngx_http_proxy_module
和 ngx_http_cache_module
实现。以下是一个简单的配置示例,它将设置 Nginx 作为反向代理的同时开启缓存功能:
http {
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
listen 80;
location / {
proxy_pass http://upstream_server;
proxy_cache my_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
}
}
}
解释:
-
proxy_cache_path
定义了缓存的存储路径、缓存目录的层次结构、缓存键的区域、最大缓存大小以及缓存内容在多长时间没有被访问后应被视为不活动并可能被删除。 -
proxy_cache
指定使用名为my_cache
的缓存。 -
proxy_cache_valid
设置了不同HTTP响应状态码的缓存有效期。 -
proxy_cache_use_stale
定义了在指定的错误情况下或缓存内容超时时是否可以使用过期的缓存数据。
确保替换 http://upstream_server
为你的后端服务器地址。这个配置假设 Nginx 已经安装了 ngx_http_proxy_module
和 ngx_http_cache_module
。
3.2.3 SSL加速
SSL加速:Nginx 可以通过 HTTPS 访问加速,提高 HTTPS 访问的性能,减少SSL负载压力,保证数据的安全性。
Nginx SSL加速通常指的是通过优化SSL/TLS配置来提高性能,这可以包括禁用不必要的SSL/TLS协议版本、使用更快的加密算法、启用会话复用等。以下是一个基本的Nginx配置示例,展示了一些可以用来加速SSL的配置:
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /path/to/your/certificate.pem;
ssl_certificate_key /path/to/your/private.key;
# 仅启用最安全的TLS协议版本
ssl_protocols TLSv1.2 TLSv1.3;
# 仅使用安全的加密算法
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers on;
# 启用会话复用
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 其他SSL配置和安全头配置
...
# 服务器的其他配置
location / {
...
}
}
这个配置示例中,我们启用了TLSv1.2和TLSv1.3协议,并且指定了支持的加密算法。我们也启用了SSL会话复用,这可以减少SSL握手时间,从而加速连接建立的过程。
请根据你的实际需求和服务器的安全策略来调整这些配置。如果你需要进一步优化SSL性能,可以考虑使用HTTP/2,或者进行SSL/TLS性能分析来找到瓶颈。
3.2.4 WebSocket服务
WebSocket:Nginx也支持WebSocket协议,可用于实时通信应用程序。
在Nginx中使用WebSocket,你需要确保Nginx版本支持WebSocket,一般来说,较新版本的Nginx已经支持WebSocket。
以下是一个配置示例,它将Nginx配置为代理WebSocket请求:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://websocket_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
upstream websocket_backend {
server websocket_server_address:port;
}
}
在这个配置中,我们定义了一个map
指令,它根据$http_upgrade
变量的值设置$connection_upgrade
变量。这样做是为了确保当Nginx收到一个包含Upgrade
头部的HTTP请求时,它会将连接升级到Upgrade
类型。
然后,在server
块中,我们定义了监听端口和服务器名称。在location /
块中,我们配置了Nginx来代理WebSocket请求。我们设置了代理的目标(upstream
块中定义的服务器),并设置了必要的头部,以确保WebSocket连接可以正确建立。
确保替换example.com
、websocket_server_address
和port
为你的实际域名和WebSocket服务器的IP地址和端口。
3.2.5 访问控制和安全
访问控制和安全:Nginx可以使用访问控制、基于IP地址的访问限制等来提高服务器的安全性,有效保护Web应用程序和服务器。
在Nginx中实现访问控制和安全性,可以通过配置文件来设置。
以下是一些常见的安全性配置示例:
-
禁止特定IP访问:
location / {
deny 192.168.1.1;
allow all;
}
-
使用Auth Basic进行访问认证:
location / {
auth_basic "Restricted Content";
auth_basic_user_file /etc/nginx/.htpasswd;
}
-
设置SSL要求,强制使用HTTPS:
server {
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/nginx.crt;
ssl_certificate_key /etc/nginx/ssl/nginx.key;
# 其他配置...
}
-
限制请求速率:
http {
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
server {
location / {
limit_req zone=mylimit burst=5;
}
}
}
-
设置X-Frame-Options防止点击劫持:
add_header X-Frame-Options "SAMEORIGIN";
-
设置Content Security Policy(CSP):
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' https: 'unsafe-eval';";
-
禁止浏览器缓存:
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
这些配置可以根据具体需求进行组合和调整,以增强Nginx服务器的安全性和访问控制。
3.2.6 Api网关服务(综合)
Nginx可以作为API网关,通过配置来处理API路由、负载均衡、请求限流、缓存、SSL/TLS终结等。以下是一个简单的Nginx配置示例,用于将API请求代理到后端服务:
http {
upstream userapi {
server user1.example.com;
server user2.example.com;
}
upstream orderapi {
server order1.example.com;
server order2.example.com;
}
server {
listen 80;
location /api/user/ {
proxy_pass http://userapi;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /api/order/ {
proxy_pass http://orderapi;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location / {
return 404;
}
}
}
在这个配置中:
-
upstream
块定义了一个后端服务器群组,可以通过server
指令添加更多后端服务器。 -
server
块中的listen
指令设置Nginx监听80端口。 -
location /api/
块捕获所有到达/api/
路径的请求,并使用proxy_pass
指令将请求转发到backend
群组。 -
proxy_set_header
指令用于设置转发给后端服务器的HTTP头部,以确保后端服务可以获取到正确的原始请求信息。 -
第二个
location /
块捕获所有其他请求,并返回404状态码。
这个配置可以根据具体需求进行扩展,比如添加缓存、请求限流、服务发现、TLS/SSL配置等功能。
3.2.7 代理:正向代理和反向代理
代理服务器一般指代局域网内部的机器通过代理服务发送请求到互联网上的服务器,代理服务器一般作用于客户端。代理服务器是介于客户端和Web服务器之间的服务器,客户端首先与代理服务器创建连接,然后根据代理服务器所使用的代理协议,请求对目标服务器创建连接、或则获得目标服务器的指定资源。
正向代理:为了从原始服务器取的内容,客户端向代理发送一个请求并指定目标(Web服务器),然后代理向Web服务器转交请求并将获得的内容返回给客户端,客户端必须要进行一些特别的设置才能使用正向代理。
-
像VPN就是正向代理,一般在浏览器中配置代理服务器的相关信息。
-
正向代理中代理的对象是客户端,代理服务器和客户端属于同一个LAN,对服务器端来说是透明的。
反向代理:客户端发送请求到代理服务器,由代理服务器转发给相应的Web服务器进行处理,最终返回结果给客户端。
-
像Nginx就是反向代理服务器软件,对客户端暴露的其实是一个VIP,不是真实的Web服务器的IP。
-
反向代理的是对象是Web服务器端,代理服务器和Web服务端属于同一个LAN,对客户端来说是透明的。
使用反向代理的好处是客户端不需要任何配置就可以访问,对外暴露的是代理服务器的地址隐藏了真实服务器的地址,客户端只需要把请求发送给代理服务器,由代理服务器去选择后端的Web服务器,获取到数据后再返回给客户端。
Nginx配置正向代理的示例(典型的用香港机器访问脸书和谷歌):
server {
listen 3128;
location / {
proxy_pass http://$http_host$request_uri;
proxy_set_header Host $http_host;
proxy_buffers 256 4k;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
send_timeout 300;
}
}
Nginx配置反向代理的示例:
http {
upstream backend {
server backend1.example.com;
server backend2.example.com;
}
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
3.2.8 负载均衡
负载均衡建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展网络设备和服务器的带宽、增加吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。负载均衡(Load Balance)其意思就是分摊到多个操作单元上进行执行,例如Web服务器、FTP服务器、企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。
简而言之,单个Web应用服务器不能承受日益增长的并发量请求,因此需要不断扩展web服务器来支撑高并发请求,根据不同的负载均衡策略将请求分配到各个服务器上。
Nginx支持6种不同的负载均衡策略:
-
round_robin轮询:轮询(默认策略),每个请求按时间顺序依次分配至不同的后端服务器。每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉能够被自动剔除。轮询算法适合服务器配置相当,无状态且短平快的服务使用。
-
weight权重(基于轮询):指定轮询的几率,weight和后端的访问比例成比例,weight权重越高比例越大。通常用于后端服务器配置不均的情况。
-
ip_hash:上面两种算法存在一个问题是就是无法做到会话保持,当用户登录到服务器上后,第二次请求的时候会被定位到服务器集群中的某一个,那么已经登录到某个服务器上的用户会重新定位到另一台,之前的登录信息会丢失。ip_hash算法可以解决这个问题,当用户再次访问请求时,会通过hash算法自动定位到已经登录的服务器上,这样每个客户端可以固定在某个web服务器上,解决客户端session的问题。
-
hash $request_uri
:uri哈希,根据请求的URI进行哈希计算,相同的URI将会被分配到相同的服务器。 -
hash $remote_addr
:远程地址哈希,根据客户端IP地址进行哈希计算,相同的IP地址将会被分配到相同的服务器。 -
random
:随机,每个请求随机分配至不同的后端服务器。
3.2.9 动静分离
动静分离技术是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,将静态文件放在一个单独的web服务器上,加快解析速度,降低原来单个服务器的压力。在Nginx的配置中,在server{}段中加入带正则匹配的location来指定匹配项针对PHP的动静分离:静态页面交给Nginx处理,动态页面交给PHP-FPM模块或Apache处理。
Nginx 动静分离是一种常见的web服务器优化方法,通过将网站的静态内容(例如图片、CSS、JS等)与动态内容(例如PHP、Python脚本处理的内容)分开存储,可以有效提升网站的性能。
以下是一个简单的Nginx配置示例,实现动静分离:
server {
listen 80;
server_name example.com;
# 静态文件目录
location /static/ {
alias /path/to/your/static/files/;
expires 30d;
add_header Cache-Control "public";
try_files $uri $uri/ =404;
}
# 动态内容处理
location / {
include uwsgi_params;
uwsgi_pass unix:/path/to/your/uwsgi/socket.sock;
uwsgi_param UWSGI_SCRIPT your_app.wsgi;
uwsgi_param UWSGI_CHDIR /path/to/your/project;
client_max_body_size 32M;
}
}
在这个配置中:
-
静态文件位于
/path/to/your/static/files/
目录下,并且通过location /static/
指定。 -
对于静态文件,设置了缓存时间为30天,并添加了Cache-Control头,以指示客户端和代理服务器缓存文件。
-
所有其他请求都被转发到uWSGI服务器(通过UNIX socket),以处理Python/Django等应用的动态内容。
确保替换 /path/to/your/
、example.com
、your_app.wsgi
和项目路径为你的实际路径和应用信息。