一、Web 服务基础介绍
正常情况下的单次web服务访问流程:
1.1.1 Apache 经典的 Web 服务端
Apache起初由美国的伊利诺伊大学香槟分校的国家超级计算机应用中心开发
目前经历了两大版本分别是1.X和2.X
其可以通过编译安装实现特定的功能
1.1.1.1 Apache prefork 模型
- 预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024
- 每个子进程有一个独立的线程响应用户请求
- 相对比较占用内存,但是比较稳定,可以设置最大和最小进程数
- 是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景
优点:稳定
缺点:每个用户请求需要对应开启一个进程,占用资源较多,并发性差,不适用于高并发场景
1.1.1.2 Apache worker 模型
- 一种多进程和多线程混合的模型
- 有一个控制进程,启动多个子进程
- 每个子进程里面包含固定的线程
- 使用线程程来处理请求
- 当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求
- 由于其使用了线程处理请求,因此可以承受更高的并发
优点:相比prefork 占用的内存较少,可以同时处理更多的请求
缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超 时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用(该问题在 prefork模式下,同样会发生)
1.1.1.3 Apache event模型
Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll)
每个进程响应多个请求,在现在版本里的已经是稳定可用的模式
它和worker模式很像,最大的区别在于,它解决了keepalive场景下长期被占用的线程的资源浪费问题 (某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)
event MPM中,会有一个专门的线程来管理这些keepalive类型的线程
当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场 景下的请求处理能力
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keep alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
1.1.2 Nginx-高性能的 Web 服务端
基于Nginx的工作场景:
1.1.3 用户访问体验和性能
1.1.3.1 用户访问体验统计
互联网存在用户速度体验的1-3-10原则,即1秒最优,1-3秒较优,3~10秒比较慢,10秒以上用户无法接 受。用户放弃一个产品的代价很低,只是换一个URL而已。
1.1.3.2 影响用户体验的因素
1.客户端
- 客户端硬件配置
- 客户端网络速率
- 客户端与服务端距离
2.服务器
- 服务端网络速率
- 服务端硬件配置
- 服务端架构设计
- 服务端应用程序工作模式
- 服务端并发数量服务端响应文件大小及数量 buffer cache
- 服务端I/O压力1.2.4 服务端 I/O 流程
1.1.4 服务端 I/O 流程
I/O在计算机中指Input/Output, IOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数), 是衡量磁盘性能的主要指标之一。IOPS是指单位时间内系统能处理的I/O请求数量,一般以每秒处理的 I/O请求数量为单位,I/O请求通常为读或写数据操作请求。
一次完整的I/O是用户空间的进程数据与内核空间的内核数据的报文的完整交换,但是由于内核空间与用 户空间是严格隔离的,所以其数据交换过程中不能由用户空间的进程直接调用内核空间的内存数据,而 是需要经历一次从内核空间中的内存数据copy到用户空间的进程内存当中,所以简单说I/O就是把数据从 内核空间中的内存数据复制到用户空间中进程的内存当中。
服务器的I/O
- 磁盘I/O
- 网络I/O : 一切皆文件,本质为对socket文件的读写
1.1.4.1 磁盘 I/O
磁盘I/O是进程向内核发起系统调用,请求磁盘上的某个资源比如是html 文件或者图片,然后内核通过相 应的驱动程序将目标文件加载到内核的内存空间,加载完成之后把数据从内核内存再复制给进程内存, 如果是比较大的数据也需要等待时间。
1.1.4.2 网络 I/O
网络通信就是网络协议栈到用户空间进程的IO就是网络IO
网络I/O 处理过程
- 获取请求数据,客户端与服务器建立连接发出请求,服务器接受请求(1-3)
- 构建响应,当服务器接收完请求,并在用户空间处理客户端的请求,直到构建响应完成(4)
- 返回数据,服务器将已构建好的响应再通过内核空间的网络 I/O 发还给客户端(5-7)
不论磁盘和网络I/O
每次I/O,都要经由两个阶段:
- 第一步:将数据从文件先加载至内核内存空间(缓冲区),等待数据准备完成,时间较长
- 第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
1.2 I/O 模型
1.2.1 I/O 模型相关概念
简单来说,我这里举一个通俗的例子来让我们更快理解同步/异步和阻塞/非阻塞,我现在是一个厨师,顾客现在需要一个红烧狮子头。
同步/异步:我做好了直接放在出餐口等顾客主动发现后自己来拿叫做同步,我做好了之后通知顾客来取拿叫做异步,
阻塞/非阻塞:我做红烧狮子头过程中顾客一直在旁边等着我做好叫做阻塞,在我做红烧狮子头的过程中顾客没有在旁边一直等着我做好叫做非阻塞
同步/异步:关注的是消息通信机制,即调用者在等待一件事情的处理结果时,被调用者是否提供完成状 态的通知。
- 同步:synchronous,被调用者并不提供事件的处理结果相关的通知消息,需要调用者主动询问事 情是否处理完成
- 异步:asynchronous,被调用者通过状态、通知或回调机制主动通知调用者被调用者的运行状态
阻塞/非阻塞:关注调用者在等待结果返回之前所处的状态
- 阻塞:blocking,指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂 起,干不了别的事情。
- 非阻塞:nonblocking,指IO操作被调用后立即返回给用户一个状态值,而无需等到IO操作彻底完 成,在最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情
1.2.2 网络 I/O 模型
阻塞型、非阻塞型、复用型、信号驱动型、异步
1.2.2.1 阻塞型 I/O 模型(blocking IO)
- 阻塞IO模型是最简单的I/O模型,用户线程在内核进行IO操作时被阻塞
- 用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间。内核等到数据包到达后,然 后将接收的数据拷贝到用户空间,完成read操作
- 用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个I/O请求的过程中,用户线 程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够
优缺点
- 优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源
- 缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销较apache 的preforck使用的是这种模式。
阻塞型 I/O 模型适用于一些 I/O 操作相对较少、对性能要求不高的场景。在高并发、高性能的系统中,通常会采用非阻塞 I/O 或异步 I/O 等模型来提高系统的性能和响应能力。
同步阻塞:程序向内核发送I/O请求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回,则进 程将一直等待并不再接受新的请求,并由进程轮询查看I/O是否完成,完成后进程将I/O结果返回给 Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看I/O是否完成,这种 方式简单,但是比较慢,用的比较少。
1.2.2.2 非阻塞型 I/O 模型 (nonblocking IO)
当进程或线程发起一个非阻塞的 I/O 操作时,如果此时数据尚未准备好,系统不会阻塞该进程或线程,而是立即返回一个状态,表明数据不可用。这样,进程或线程可以继续执行其他任务,然后周期性地检查 I/O 操作是否完成或数据是否准备好。
用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据 到达后,才真正读取到数据,继续执行。即 “轮询”机制存在两个问题:如果有大量文件描述符都要等, 那么就得一个一个的read。这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户 态和核心态切换一次)。轮询的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长, 程序响应延迟就过大;设的太短,就会造成过于频繁的重试,干耗CPU而已,是比较浪费CPU的方式,一 般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
非阻塞:程序向内核发送请I/O求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回IO结 果,进程将不再等待,而且继续处理其他请求,但是仍然需要进程隔一段时间就要查看内核I/O是否完 成。
在设置连接为非阻塞时,当应用进程系统调用 recvfrom 没有数据返回时,内核会立即返 回一个 EWOULDBLOCK 错误,而不会一直阻塞到数据准备好。如上图在第四次调用时有一个数据报准 备好了,所以这时数据会被复制到 应用进程缓冲区 ,于是 recvfrom 成功返回数据
当一个应用进程这样循环调用 recvfrom 时,称之为轮询 polling 。这么做往往会耗费大量CPU时间,在实际应用中,非阻塞型 I/O 通常与其他技术(如 I/O 多路复用)结合使用,以实现高效的 I/O 处理。实际使用很少!!!
优点:
- 提高了 CPU 的利用率:由于进程或线程不会因为等待 I/O 而被阻塞,CPU 可以用于执行其他有意义的计算任务。
- 更好的响应性:可以更快地对其他事件做出响应,适用于需要快速处理多个并发 I/O 操作的场景。
缺点:
- 编程复杂度增加:需要通过不断轮询来检查 I/O 状态,这可能导致复杂的控制逻辑。
- 可能产生大量的无效系统调用:因为需要频繁检查 I/O 状态,可能会产生许多不必要的系统调用,从而增加系统开销。
1.2.2.3 多路复用 I/O 型(I/O multiplexing)
上面的模型中,每一个文件描述符对应的IO是由一个线程监控和处理
多路复用IO指一个线程可以同时(实际是交替实现,即并发完成)监控和处理多个文件描述符对应各自的IO,即复用同一个线程
一个线程之所以能实现同时处理多个IO,是因为这个线程调用了内核中的SELECT,POLL或EPOLL等系统调 用,从而实现多路复用IO
I/O multiplexing 主要包括:select,poll,epoll三种系统调用,select/poll/epoll的好处就在于单个 process就可以同时处理多个网络连接的IO。
它的基本原理就是select/poll/epoll这个function会不断的轮询所负责的所有socket,当某个socket有数 据到达了,就通知用户进程。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket, 当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从 kernel拷贝到用户进程。
Apache prefork是此模式的select,worker是poll模式。
IO多路复用(IO Multiplexing) :是一种机制,程序注册一组socket文件描述符给操作系统,表示“我要 监视这些fd是否有IO事件发生,有了就告诉程序处理”IO多路复用一般和NIO一起使用的。NIO和IO多路 复用是相对独立的。NIO仅仅是指IO API总是能立刻返回,不会被Blocking;而IO多路复用仅仅是操作系统 提供的一种便利的通知机制。操作系统并不会强制这俩必须得一起用,可以只用IO多路复用 + BIO,这时 还是当前线程被卡住。IO多路复用和NIO是要配合一起使用才有
实际意义
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,就通知该进程多个连接共用一 个等待机制,本模型会阻塞进程,但是进程是阻塞在select或者poll这两个系统调用上,而不是阻塞在真 正的IO操作上用户首先将需要进行IO操作添加到select中,同时等待select系统调用返回。当数据到达 时,IO被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行从流程上来看,使用 select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视IO,以及调用select函数的 额外操作,效率更差。并且阻塞了两次,但是第一次阻塞在select上时,select可以监控多个IO上是否已 有IO操作准备就绪,即可达到在同一个线程内同时处理多个IO请求的目的。而不像阻塞IO那种,一次只 能监控一个IO虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select 函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只是注册自己需要的IO请求,然 后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率IO多路复用是最常使用的IO 模型,但是其异步程度还不够“彻底”,因它使用了会阻塞线程的select系统调用。因此IO多路复用只能称 为异步阻塞IO模型,而非真正的异步IO
优点:
- 可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程(每个文件描述 符一个线程),这样可以大大节省系统资源
- 高效利用系统资源:相比为每个 I/O 操作创建单独的线程或进程,显著减少了内存和 CPU 资源的消耗。
- 高并发处理能力:可以在单个线程或进程中同时处理多个 I/O 操作,能够应对大量并发连接。
- 灵活性:可以根据需要动态地添加或删除要监控的 I/O 源。
- 简化编程模型:减少了复杂的线程或进程同步和管理工作。
缺点:
- 当连接数较少时效率相比多线程+阻塞 I/O 模型效率较低,可能延迟更大,因为单个连接处理 需要 2 次系统调用,占用时间会有增加
- 编程复杂度较高:相比于简单的阻塞 I/O 模型,多路复用的实现和使用相对复杂,需要更细致的编程和错误处理。
- 可能存在就绪通知的延迟:某些情况下,I/O 就绪事件可能不能及时被检测到,导致一定的延迟。
- 对于小量的 I/O 操作可能效率不高:当需要处理的 I/O 源数量较少时,其额外的设置和管理开销可能会使性能不如简单的阻塞式 I/O。
IO多路复用适用如下场合:
- 当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
- 当一个客户端同时处理多个套接字时,此情况可能的但很少出现
- 当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用
- 当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
- 当一个服务器要处理多个服务或多个协议,一般要使用I/O复用
1.2.2.4 信号驱动式 I/O 模型 (signal-driven IO)
信号驱动I/O的意思就是进程现在不用傻等着,也不用去轮询。而是让内核在数据就绪时,发送信号通知进程。
调用的步骤是,通过系统调用 sigaction ,并注册一个信号处理的回调函数,该调用会立即返回,然后主 程序可以继续向下执行,当有I/O操作准备就绪,即内核数据就绪时,内核会为该进程产生一个 SIGIO信 号,并回调注册的信号回调函数,这样就可以在信号回调函数中系统调用 recvfrom 获取数据,将用户进程所需要的数据从内核空间拷贝到用户空间
此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处 理函数的通知。
在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续运行并不阻塞
在信号驱动式 I/O 模型中,应用程序使用套接口进行信号驱动 I/O,并安装一个信号处理函数,进程继续 运行并不阻塞 当数据准备好时,进程会收到一个 SIGIO 信号,可以在信号处理函数中调用 I/O 操作函数处理数据。
优点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此 可以提高资源的利用率
缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知
异步阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核收到进程请求后
进行的IO如果不能立即返回,就由内核等待结果,直到IO完成后内核再通知进程。
1.2.2.5 异步 I/O 模型 (asynchronous IO)
异步I/O 与 信号驱动I/O最大区别在于,信号驱动是内核通知用户进程何时开始一个I/O操作,而异步I/O 是由内核通知用户进程I/O操作何时完成,两者有本质区别,相当于不用去饭店场吃饭,直接点个外卖,把等待上菜的时间也给省了
相对于同步I/O,异步I/O不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备 好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直 接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。
信号驱动IO当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到 用户空间缓冲区这个阶段,而异步IO直接是在第二个阶段完成后,内核直接通知用户线程可以进行后续 操作了
优点:异步 I/O 能够充分利用 DMA 特性,让 I/O 操作与计算重叠
缺点:要实现真正的异步 I/O,操作系统需要做大量的工作。目前 Windows 下通过 IOCP 实现了真正的 异步 I/O,在 Linux 系统下,Linux 2.6才引入,目前 AIO 并不完善,因此在 Linux 下实现高并发网络编 程时以 IO 复用模型模式+多线程任务的架构基本可以满足需求
Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、 libuv。
异步非阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核调用的 IO如果不能立即返回,内核会继续处理其他事物,直到IO完成后将结果通知给内核,内核在将IO完成的 结果返回给进程,期间进程可以接受新的请求,内核也可以处理新的事物,因此相互不影响,可以实现 较大的同时并实现较高的IO复用,因此异步非阻塞使用最多的一种通信方式。
apache用的是同步阻塞,nginx用的是异步非阻塞,apache为什么还存在,是因为有些系统不支持epoll模式
1.3 零拷贝
1.3.1 零拷贝介绍
1.3.1.1 传统 Linux中 I/O 的问题
传统的 Linux 系统的标准 I/O 接口(read、write)是基于数据拷贝的,也就是数据都是 copy_to_user 或者 copy_from_user,这样做的好处是,通过中间缓存的机制,减少磁盘 I/O 的操作,但是坏处也很明 显,大量数据的拷贝,用户态和内核态的频繁切换,会消耗大量的 CPU 资源,严重影响数据传输的性 能,统计表明,在Linux协议栈中,数据包在内核态和用户态之间的拷贝所用的时间甚至占到了数据包整 个处理流程时间的57.1%
1.3.1.2 什么是零拷贝
零拷贝就是上述问题的一个解决方案,通过尽量避免拷贝操作来缓解 CPU 的压力。零拷贝并没有真正做 到“0”拷贝,它更多是一种思想,很多的零拷贝技术都是基于这个思想去做的优化
1.3.2 零拷页相关技术
1.3.2.1 MMAP ( Memory Mapping )
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间 后,进程可以向访问普通内存一样对文件进行访问。
mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘 地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。
实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到 对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对 这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
内存映射减少数据在用户空间和内核空间之间的拷贝操作,适合大量数据传输
1.3.2.2 SENDFILE
1.3.2.3 DMA 辅助的 SENDFILE
二、Nginx 架构和安装
2.1 Nginx 概述
2.1.1 Nginx 介绍
Nginx(engine x)是一个高性能的开源 Web 服务器、反向代理服务器和负载均衡器,也可以作为邮件代理服务器和 HTTP 缓存。
2.1.2 Nginx 功能介绍
- 静态的web资源服务器html,图片,js,css,txt等静态资源
- http/https协议的反向代理
- 结合FastCGI/uWSGI/SCGI等协议反向代理动态资源请求
- tcp/udp协议的请求转发(反向代理)
- imap4/pop3协议的反向代理
2.2 Nginx 架构和进程
2.2.1 Nginx 进程结构
web请求处理机制
- 多进程方式:服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直 到用户关闭连接,这样的优势是处理速度快,子进程之间相互独立,但是如果访问过大会导致服务 器资源耗尽而无法提供请求
- 多线程方式:与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程和此客 户端进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了web服务器 对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可 以相互访问同样的内存地址空间,所以他们相互影响,一旦主进程挂掉则所有子线程都不能工作 了,IIS服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。
Nginx是多进程组织模型,而且是一个由Master主进程和Worker工作进程组成。
主进程(master process)的功能:
- 对外接口:接收外部的操作(信号)
- 对内转发:根据外部的操作的不同,通过信号管理 Worker
- 监控:监控 worker 进程的运行状态,worker 进程异常终止后,自动重启 worker 进程
- 读取Nginx 配置文件并验证其有效性和正确性
- 建立、绑定和关闭socket连接 按照配置生成、管理和结束工作进程
- 接受外界指令,比如重启、升级及退出服务器等指令
- 不中断服务,实现平滑升级,重启服务并应用新的配置
- 开启日志文件,获取文件描述符
- 不中断服务,实现平滑升级,升级失败进行回滚处理
- 编译和处理perl脚本
工作进程(worker process)的功能:
- 所有 Worker 进程都是平等的
- 实际处理:网络请求,由 Worker 进程处理
- Worker进程数量:一般设置为核心数,充分利用CPU资源,同时避免进程数量过多,导致进程竞争 CPU资源
- 增加上下文切换的损耗
- 接受处理客户的请求
- 将请求依次送入各个功能模块进行处理 I/O调用,获取响应数据
- 与后端服务器通信,接收后端服务器的处理结果
- 缓存数据,访问缓存索引,查询和调用缓存数据
- 发送请求结果,响应客户的请求
- 接收主程序指令,比如重启、升级和退出等
2.3 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 版本支持动态装载和卸载
2.4 Nginx 安装
2.4.1 Nginx版本和安装方式
Nginx版本
- Mainline version 主要开发版本,一般为奇数版本号,比如1.19
- Stable version 当前最新稳定版,一般为偶数版本,如:1.20
- Legacy versions 旧的稳定版,一般为偶数版本,如:1.18
Nginx安装可以使用yum或源码安装,但是推荐使用源码编译安装
- yum的版本比较旧
- 编译安装可以更方便自定义相关路径
- 使用源码编译可以自定义相关功能,更方便业务的上的使用
2.4.2.Nginx 编译安装
编译器介绍
源码安装需要提前准备标准的编译器,GCC的全称是(GNU Compiler collection),其有GNU开发,并以 GPL即LGPL许可,是自由的类UNIX即苹果电脑Mac OS X操作系统的标准编译器,因为GCC原本只能处理C语 言,所以原名为GNU C语言编译器,后来得到快速发展,可以处理C++,Fortran,pascal,objective C, java以及Ada等其他语言,此外还需要Automake工具,以完成自动创建Makefile的工作,Nginx的一些模块 需要依赖第三方库,比如: pcre(支持rewrite),zlib(支持gzip模块)和openssl(支持ssl模块) 等。
[root@nginx nginx-1.24.0]# ./configure --prefix=/usr/local/nginx \
> --user=nginx \
> --group=nginx \
> --with-http_ssl_module \
> --with-http_v2_module \
> --with-http_realip_module \
> --with-http_stub_status_module \
> --with-http_gzip_static_module \
> --with-pcre \
> --with-stream \
> --with-stream_ssl_module \
> --with-stream_realip_module #环境监测
make && make install
[root@nginx nginx-1.24.0]# vim ~/.bash_profile
export PATH=$PATH:/usr/local/nginx/sbin
[root@nginx nginx-1.24.0]# source ~/.bash_profile
[root@nginx nginx-1.24.0]# nginx -V
nginx version: nginx/1.24.0
built by gcc 11.4.1 20231218 (Red Hat 11.4.1-3) (GCC)
built with OpenSSL 3.0.7 1 Nov 2022
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module
[root@nginx nginx-1.24.0]# nginx -v
nginx version: nginx/1.24.0
2.6 平滑升级和回滚
有时候我们需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,而此时 Nginx又在跑着业务无法停掉,这时我们就可能选择平滑升级
2.6.1 平滑升级流程
- 将旧Nginx二进制文件换成新Nginx程序文件(注意先备份)
- 向master进程发送USR2信号
- master进程修改pid文件名加上后缀.oldbin,成为nginx.pid.oldbin master进程用新Nginx文件启动新
- master进程成为旧master的子进程,系统中将有新旧两个Nginx主 进程共同提供Web服务,当前新的请求仍然由旧Nginx的worker进程进行处理,将新生成的master进程的PID存放至新生成的pid文件nginx.pid
- 向旧的Nginx服务进程发送WINCH信号,使旧的Nginx worker进程平滑停止
- 向旧master进程发送QUIT信号,关闭老master,并删除Nginx.pid.oldbin文件
- 如果发现升级有问题,可以回滚∶向老master发送HUP,向新master发送QUIT
2.6.2 平滑升级和回滚实例
注意,新版本也是需要编译的
[root@nginx sbin]# cp nginx nginx.24
[root@nginx sbin]# \cp -f /root/nginx-1.26.1/objs/nginx /usr/local/nginx/sbin/
[root@nginx sbin]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@nginx sbin]# kill -usr2 47949
[root@nginx sbin]# ps aux | grep nginx
root 47949 0.0 0.0 4416 1776 ? Ss 18:46 0:00 nginx: master process nginx
nobody 47950 0.0 0.1 13064 5360 ? S 18:46 0:00 nginx: worker process
root 48009 0.0 0.1 9876 6528 ? S 18:51 0:00 nginx: master process nginx
nobody 48010 0.0 0.0 14208 4868 ? S 18:51 0:00 nginx: worker process
root 48012 0.0 0.0 221664 2176 pts/1 S+ 18:51 0:00 grep --color=auto nginx
[root@nginx sbin]# kill -winch 47949
[root@nginx sbin]# ps aux | grep nginx
root 47949 0.0 0.0 4416 1776 ? Ss 18:46 0:00 nginx: master process nginx
root 48009 0.0 0.1 9876 6528 ? S 18:51 0:00 nginx: master process nginx
nobody 48010 0.0 0.0 14208 4868 ? S 18:51 0:00 nginx: worker process
root 48016 0.0 0.0 221664 2176 pts/1 S+ 18:51 0:00 grep --color=auto nginx
[root@nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.26.1
Date: Thu, 15 Aug 2024 10:51:59 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 15 Aug 2024 03:08:45 GMT
Connection: keep-alive
ETag: "66bd713d-267"
Accept-Ranges: bytes
三、Nginx 核心配置详解
3.1 配置文件说明
Nginx的配置文件的组成部分:
- 主配置文件:nginx.conf
- 子配置文件: include conf.d/*.conf ,fastcgi, uwsgi,scgi 等协议相关的配置文件
- mime.types:支持的mime类型,MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型,MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据,是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动 使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。
3.2 全局配置
Main 全局配置段常见的配置指令分类
- 正常运行必备的配置
- 优化性能相关的配置
- 用于调试及定位问题相关的配置
- 事件驱动相关的配置
实例:实现 nginx 的高并发配置
[root@Nginx ~]# ulimit -n 102400
[root@Nginx ~]# ab -c 5000 -n 10000 http://10.0.0.8/
#默认配置不支持高并发,会出现以下错误日志
[root@Nginx ~]# tail /apps/nginx/logs/error.log
2020/09/24 21:19:33 [crit] 41006#0: *1105860 open() "/apps/nginx/html/50x.html"
failed (24: Too many open files), client: 10.0.0.7, server: localhost, request:
"GET / HTTP/1.0", host: "10.0.0.8"
2020/09/24 21:19:33 [crit] 41006#0: accept4() failed (24: Too many open files)
2020/09/24 21:19:33 [crit] 41006#0: *1114177 open()
"/apps/nginx/html/index.html" failed (24: Too many open files), client: 10.0.0.7,
server: localhost, request: "GET / HTTP/1.0", host: "10.0.0.8"
#修改配置
[root@Nginx ~]# vim /etc/security/limits.conf
* - nproc 100000
[root@Nginx ~]# vim /apps/nginx/conf/nginx.conf
worker_rlimit_nofile 100000;
[root@Nginx ~]# systemctl restart nginx
3.3 核心配置示例
基于不同的IP、不同的端口以及不用得域名实现不同的虚拟主机,依赖于核心模块 ngx_http_core_module实现。
3.3.1 nginx配置中的root和alias
root:指定web的家目录,在定义location的时候,文件的绝对路径等于 root+location
alias:定义路径别名,会把访问的路径重新定义到其指定的路径,文档映射的另一种机制;仅能用于 location上下文,此指令使用较少
location中使用root指令和alias指令的意义不同
root #给定的路径对应于location中的/uri左侧的/
alias #给定的路径对应于location中的/uri的完整路径
[root@nginx]# mkdir -p /usr/local/nginx/conf.d
[root@nginx]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.timinglee.org;
root /data/web/html;
index index.html;
location /test1/ {
root /data/web;
}
location /test2 {
alias /data/web/test1;
}
}
[root@nginx]# mkdir -p /data/web/test1
[root@nginx]# echo www.timinglee.org > /data/web/test1/index.html
[root@nginx]# nginx -s reload
[root@nginx]# vim /usr/local/nginx/conf/nginx.conf #
13 worker_connections 100000;
14 use epoll;
35 include "/usr/local/nginx/conf.d/*.conf";
3.3.2 location 的详细使用
优先级是以什么结尾为最优先(~ .html$和~* HTML$,哪个在前面哪个就优先),第二是不带符号的,第三是以什么开头(^~ /t),优先级最低的是等于符号(= /test)
(~ = *~) > 不带符号 > ^~ > =目录优先级
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.timinglee.org;
root /data/web/html;
index index.html;
location /test {
root /data/web1;
}
location = /test {
root /data/web2;
}
location ^~ /t {
root /data/web3;
}
location ~* HTML$ {
root /data/web4;
}
location ~ .html$ {
root /data/web5;
}
}
[root@nginx ~]# mkdir -p /data/web{1..5}/test
[root@nginx ~]# echo web1 > /data/web1/test/index.html
[root@nginx ~]# echo web2 > /data/web2/test/index.html
[root@nginx ~]# echo web3 > /data/web3/test/index.html
[root@nginx ~]# echo web4 > /data/web4/test/index.html
[root@nginx ~]# echo web5 > /data/web5/test/index.html
[root@nginx ~]# nginx -s reload
文件优先级
3.3.3 Nginx 账户认证功能
由 ngx_http_auth_basic_module 模块提供此功能
[root@nginx ~]# htpasswd -cm /usr/local/nginx/.htpasswd admin
New password:
Re-type new password:
Adding password for user admin
[root@nginx ~]# htpasswd -m /usr/local/nginx/.htpasswd lee
New password:
Re-type new password:
Adding password for user lee
[root@nginx ~]# cat /usr/local/nginx/.htpasswd
admin:$apr1$H3AeQafy$dLY7PkwzcJMK0fVMum6gt1
lee:$apr1$9y2g4Nou$VQAQRthYqucX9/C6uHn6p.
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
[root@nginx ~]# mkdir /data/web/lee
[root@nginx ~]# echo lee > /data/web/lee/index.html
[root@nginx ~]# nginx -s reload
测试结果
3.3.4 自定义错误页面
自定义错误页,同时也可以用指定的响应状态码进行响应, 可用位置:http, server, location, if in location
error_page code ... [=[response]] uri;
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.timinglee.org;
root /data/web/html;
index index.html;
error_page 404 /40x.html;
location /lee {
root /data/web;
auth_basic "login passwd !!";
auth_basic_user_file "/usr/local/nginx/.htpasswd";
}
location = /40x.html {
root /data/web/errorpage;
}
}
[root@nginx ~]# mkdir -p /data/web/errorpage
[root@nginx ~]# echo error page > /data/web/errorpage/40x.html
[root@nginx ~]# nginx -s reload
测试结果
3.3.5 自定义错误日志
可以自定义错误日志
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
[root@nginx ~]# mkdir -p /var/log/test.org
[root@nginx ~]# nginx -s reload
[root@nginx ~]# curl 172.25.254.100
www.timinglee.org
[root@nginx ~]# cat /var/log/test.org/access.log
172.25.254.100 - - [16/Aug/2024:14:29:16 +0800] "GET / HTTP/1.1" 200 18 "-" "curl/7.76.1"
[root@nginx ~]# curl 172.25.254.100/wwwww
error page
[root@nginx ~]# cat /var/log/test.org/error.log
2024/08/16 14:29:54 [error] 49109#0: *24 open() "/data/web/html/wwwww" failed (2: No such file or directory), client: 172.25.254.100, server: www.timinglee.org, request: "GET /wwwww HTTP/1.1", host: "172.25.254.100"
3.3.6 检测文件是否存在
try_files会按顺序检查文件是否存在,返回第一个找到的文件或文件夹(结尾加斜线表示为文件夹),如 果所有文件或文件夹都找不到,会进行一个内部重定向到最后一个参数。只有最后一个参数可以引起一 个内部重定向,之前的参数只设置内部URI的指向。最后一个参数是回退URI且必须存在,否则会出现内 部500错误。
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.timinglee.org;
root /data/web/html;
index index.html;
error_page 404 /40x.html;
error_log /var/log/test.org/error.log;
access_log /var/log/test.org/access.log;
try_files $uri $uri.html $uri/index.html /error/default.html;
location /lee {
root /data/web;
auth_basic "login passwd !!";
auth_basic_user_file "/usr/local/nginx/.htpasswd";
}
location = /40x.html {
root /data/web/errorpage;
}
}
[root@nginx ~]# nginx -s reload
[root@nginx ~]# mkdir /data/web/html/error
[root@nginx ~]# echo error default > /data/web/html/error/default.html
测试结果
3.3.7 长连接配置
[root@nginx ~]# vim /usr/local/nginx/conf/nginx.conf
keepalive_timeout 65;
keepalive_requests 3;
[root@nginx ~]# nginx -s reload
[root@nginx ~]# telnet www.timinglee.org 80
3.3.8 作为下载服务器配置
[root@nginx ~]# mkdir /data/web/download
[root@nginx ~]# dd if=/dev/zero of=/data/web/download/leefile bs=1M count=100
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhost.conf
location /download {
root /data/web;
autoindex on;
autoindex_localtime on;
autoindex_exact_size off;
limit_rate 1024k;
}
[root@nginx ~]# nginx -s reload
测试结果
四、Nginx 高级配置
4.1 Nginx 状态页
- 基于nginx 模块 ngx_http_stub_status_module 实现,
- 在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module
- 否则配置完成之后监测会是提示法错误
注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态
[root@nginx ~]# vim /usr/local/nginx/conf.d/status.conf
server {
listen 80;
server_name status.timinglee.org;
root /data/web/html;
index index.html;
location /status {
stub_status;
allow 172.25.250.1;
deny all;
}
}
4.2 Nginx 压缩功能
Nginx支持对指定类型的文件进行压缩然后再传输给客户端,而且压缩还可以设置压缩比例,压缩后的文 件大小将比源文件显著变小,样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相 应的CPU资源。
Nginx对文件的压缩功能是依赖于模块 ngx_http_gzip_module,默认是内置模块
[root@nginx ~]#vim /usr/local/nginx/conf/nginx.conf
gzip on;
gzip_comp_level 5;
gzip_min_length 1k;
gzip_http_version 1.1;
gzip_vary on;
gzip_types test/plain application/javacript application/x-javasrcipt text/css application/xml test/javascript application/x-httpd-php image/gif image/png;
[root@nginx ~]# nginx -s reload
[root@nginx ~]# cat /usr/local/nginx/logs/access.log > /data/web/html/big.html
[root@nginx ~]# du -sh /usr/local/nginx/logs/access.log
16K /usr/local/nginx/logs/access.log
[root@nginx ~]# echo hello timinglee > /data/web/html/small.html
[root@nginx ~]# curl --head --compressed 172.25.254.100/small.html
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Fri, 16 Aug 2024 08:25:32 GMT
Content-Type: text/html
Content-Length: 16
Last-Modified: Fri, 16 Aug 2024 08:25:11 GMT
Connection: keep-alive
ETag: "66bf0ce7-10"
Accept-Ranges: bytes
[root@nginx ~]# curl --head --compressed 172.25.254.100/big.html
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Fri, 16 Aug 2024 08:25:39 GMT
Content-Type: text/html
Last-Modified: Fri, 16 Aug 2024 08:24:07 GMT
Connection: keep-alive
Vary: Accept-Encoding
ETag: W/"66bf0ca7-37ec"
Content-Encoding: gzip
4.3 Nginx的版本隐藏
用户在访问nginx的时候,我们可以从报文中获得nginx的版本,相对于裸漏版本号的nginx,我们把其隐 藏起来更安全
[root@nginx ~]# vim src/core/nginx.h
#define nginx_version 1026001
#define NGINX_VERSION "1.0"
#define NGINX_VER "HAHA/" NGINX_VERSION
4.4 Nginx 变量使用
- nginx的变量可以在配置文件中引用,作为功能判断或者日志等场景使用
- 变量可以分为内置变量和自定义变量
- 内置变量是由nginx模块自带,通过变量可以获取到众多的与客户端访问相关的值。
4.4.1 内置变量
常用内置变量
$remote_addr;
#存放了客户端的地址,注意是客户端的公网IP
$args;
#变量中存放了URL中的所有参数
#例如:https://search.jd.com/Search?keyword=手机&enc=utf-8
#返回结果为: keyword=手机&enc=utf-8
$is_args
#如果有参数为? 否则为空
$document_root;
#保存了针对当前资源的请求的系统根目录,例如:/webdata/nginx/timinglee.org/lee。
$document_uri;
#保存了当前请求中不包含参数的URI,注意是不包含请求的指令
#比如:http://lee.timinglee.org/var?\id=11111会被定义为/var
#返回结果为:/var
$host;
#存放了请求的host名称
limit_rate 10240;
echo $limit_rate;
#如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置, 则显示0
$remote_port;
#客户端请求Nginx服务器时随机打开的端口,这是每个客户端自己的端口
$remote_user;
#已经经过Auth Basic Module验证的用户名
$request_body_file;
#做反向代理时发给后端服务器的本地资源的名称
$request_method;
#请求资源的方式,GET/PUT/DELETE等
$request_filename;
#当前请求的资源文件的磁盘路径,由root或alias指令与URI请求生成的文件绝对路径,
#如:webdata/nginx/timinglee.org/lee/var/index.html
$request_uri;
#包含请求参数的原始URI,不包含主机名,相当于:$document_uri?$args,
#例如:/main/index.do?id=20190221&partner=search
$scheme;
#请求的协议,例如:http,https,ftp等
$server_protocol;
#保存了客户端请求资源使用的协议的版本,例如:HTTP/1.0,HTTP/1.1,HTTP/2.0等
$server_addr;
#保存了服务器的IP地址
$server_name;
#虚拟主机的主机名
$server_port;
#虚拟主机的端口号
$http_user_agent;
#客户端浏览器的详细信息
$http_cookie;
#客户端的所有cookie信息
$cookie_<name>
#name为任意请求报文首部字段,表示记录请求报文的首部字段,ame的对应的首部字段名需要为小写,如果有 横线需要替换为下划线
#示例:
echo $http_user_agent;
echo $http_host;
$http_<name>
#name为响应报文的首部字段,name的对应的首部字段名需要为小写,如果有横线需要替换为下划线,此变量有问题
echo $sent_http_server;
$arg_<name>
#此变量存放了URL中的指定参数,name为请求url中指定的参数
echo $arg_id;
4.4.2 自定义变量
假如需要自定义变量名称和值,使用指令set $variable value;
五 Nginx Rewrite 相关功能
- Nginx服务器利用 ngx_http_rewrite_module 模块解析和处理rewrite请求
- 此功能依靠 PCRE(perl compatible regular expression),因此编译之前要安装PCRE库
- rewrite是nginx服务器的重要功能之一,用于实现URL的重写,URL的重写是非常有用的功能
- 比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的 链接,就可以设置为访问
- 另外还可以在一定程度上提高网站的安全性。
5.1 ngx_http_rewrite_module 模块指令
5.1.1 if 指令
用于条件匹配判断,并根据条件判断结果选择不同的Nginx配置,可以配置在server或location块中进行 配置,Nginx的if语法仅能使用if做单次判断,不支持使用if else或者if elif这样的多重判断,用法如下:
if (条件匹配) {
action
}
使用正则表达式对变量进行匹配,匹配成功时if指令认为条件为true,否则认为false,变量与表达式之间 使用以下符号链接:
= #比较变量和字符串是否相等,相等时if指令认为该条件为true,反之为false
!= #比较变量和字符串是否不相等,不相等时if指令认为条件为true,反之为false
~ #区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假
!~ #区分大小写字符,判断是否匹配,不满足匹配条件为真,满足匹配条件为假
~* #不区分大小写字符,可以通过正则表达式匹配,满足匹配条件为真,不满足匹配条件为假
!~* #不区分大小字符,判断是否匹配,满足匹配条件为假,不满足匹配条件为真
-f 和 !-f #判断请求的文件是否存在和是否不存在
-d 和 !-d #判断请求的目录是否存在和是否不存在
-x 和 !-x #判断文件是否可执行和是否不可执行
-e 和 !-e #判断请求的文件或目录是否存在和是否不存在(包括文件,目录,软链接)
#注意:
#如果$变量的值为空字符串或0,则if指令认为该条件为false,其他条件为true。
#nginx 1.0.1之前$变量的值如果以0开头的任意字符串会返回false
实例:
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
server {
listen 80;
server_name www.exam.com;
root /data/web/html;
index index.html;
if ( !-e $request_filename ){
echo "$request_filename is not exist";
#return 409;
}
}
}
[root@nginx conf.d]# mkdir -p /data/web/html/test2/
[root@nginx conf.d]# echo test2 > /data/web/html/test2/index.html
[root@nginx conf.d]# curl var.timinglee.org/test2/index.html
test2
[root@nginx conf.d]# rm -rf /data/web/html/test2/index.html
[root@nginx conf.d]# curl var.timinglee.org/test2/index.html
/data/web/html/test2/index.html is not exist
5.1.2 set 指令
指定key并给其定义一个变量,变量可以调用Nginx内置变量赋值给key
另外set定义格式为set $key value,value可以是text, variables和两者的组合。
实例:
[root@nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conf
server {
listen 80;
server_name lee.timinglee.org;
root /webdata/nginx/timinglee.org/lee;
location /test3{
set $name lee;
echo $name;
}
}
5.1.3 break 指令
- 用于中断当前相同作用域(location)中的其他Nginx配置
- 与该指令处于同一作用域的Nginx配置中,位于它前面的配置生效
- 位于后面的 ngx_http_rewrite_module 模块中指令就不再执行
- Nginx服务器在根据配置处理请求的过程中遇到该指令的时候,回到上一层作用域继续向下读取配置
- 该指令可以在server块和locationif块中使用
!!!!注意:
如果break指令在location块中后续指令还会继续执行,只是不执行 ngx_http_rewrite_module 模块的指令,其它指令还会执行
实例:
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
server {
listen 80;
server_name www.exam.com;
root /data/web/html;
index index.html;
location /break {
default_type text/html;
set $name test;
if ( $http_user_agent = curl/7.76.1 ){
break;
}
echo $name;
set $id 666;
echo $id;
}
}
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
[root@nginx conf.d]# nginx -s reload
[root@nginx conf.d]# curl www.test.com/var
172.25.254.100
/data/web/html
/var
www.test.com
49802
GET
/data/web/html/var
/var
http
HTTP/1.1
172.25.254.100
www.exam.com
80
curl/7.76.1
lee
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
[root@nginx conf.d]# nginx -s reload
[root@nginx conf.d]# curl www.test.com/break
test
5.1.4 return指令
指令 return用于完成对请求的处理,并直接向客户端返回响应状态码,比如:可以指定重定向URL(对于特殊重 定向状态码,301/302等) 或者是指定提示文本内容(对于特殊状态码403/500等),处于此指令后的所有配 置都将不被执行,return可以在server、if 和 location块进行配置
语法格式
return code; #返回给客户端指定的HTTP状态码
return code [text]; #返回给客户端的状态码及响应报文的实体内容
#可以调用变量,其中text如果有空格,需要用单或双引号
return code URL; #返回给客户端的URL地址
实例:
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
server {
listen 80;
server_name www.exam.com;
root /data/web/html;
index index.html;
location /return {
default_type text/html;
if (!-e $request_filename ){
return 301 http://www.baidu.com;
}
echo "$request_filename is exist";
}
}
[root@nginx conf.d]# nginx -s reload
[root@nginx conf.d]# curl -I www.test.com/return
HTTP/1.1 301 Moved Permanently
Server: nginx/1.24.0
Date: Sun, 18 Aug 2024 11:14:57 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://www.baidu.com
[root@nginx conf.d]# mkdir -p /data/web/html/return
[root@nginx conf.d]# curl -I www.test.com/return
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Sun, 18 Aug 2024 11:15:43 GMT
Content-Type: text/html
Connection: keep-alive
5.2 rewrite 指令
通过正则表达式的匹配来改变URI,可以同时存在一个或多个指令,按照顺序依次对URI进行匹配, rewrite主要是针对用户请求的URL或者是URI做具体处理
语法格式 :
rewrite regex replacement [flag];
rewrite将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为表达式指定的新的URI
注意:如果在同一级配置块中存在多个rewrite规则,那么会自下而下逐个检查;被某条件规则替换完成 后,会重新一轮的替换检查,隐含有循环机制,但不超过10次;如果超过,提示500响应码,[flag]所表示的 标志位用于控制此循环机制
如果替换后的URL是以http://或https://开头,则替换结果会直接以重定向返回给客户端, 即永久重定向 301
正则表达式格式
. #匹配除换行符以外的任意字符
\w #匹配字母或数字或下划线或汉字
\s #匹配任意的空白符
\d #匹配数字
\b #匹配单词的开始或结束
^ #匹配字付串的开始
$ #匹配字符串的结束
* #匹配重复零次或更多次
+ #匹配重复一次或更多次
? #匹配重复零次或一次
(n) #匹配重复n次
(n,) ` #匹配重复n次或更多次
(n,m) #匹配重复n到m次
*? #匹配重复任意次,但尽可能少重复
+? #匹配重复1次或更多次,但尽可能少重复 ??
?? #匹配重复0次或1次,但尽可能少重复 {n,m}?
{n,m}? #匹配重复n到m次,但尽可能少重复 {n,}?
{n,}? #匹配重复n次以上,但尽可能少重复
\W #匹配任意不是字母,数字,下划线,汉字的字符
\S #匹配任意不是空白符的字符
\D #匹配任意非数字的字符
\B #匹配不是单词开头或结束的位置
[^x] #匹配除了x以外的任意字符
[^lee] #匹配除了magedu 这几个字母以外的任意字符
5.2.1 rewrite flag 使用介绍
利用nginx的rewrite的指令,可以实现url的重新跳转,rewrite有四种不同的flag,分别是redirect(临时 重定向302)、permanent(永久重定向301)、break和last。其中前两种是跳转型的flag,后两种是代理型
- 跳转型指由客户端浏览器重新对新地址进行请求
- 代理型是在WEB服务器内部实现跳转
5.2.2 rewrite案例: 域名永久与临时重定向
域名的临时的调整,后期可能会变,之前的域名或者URL可能还用、或者跳转的目的域名和URL还会跳 转,这种情况浏览器不会缓存跳转,临时重定向不会缓存域名解析记录(A记录),但是永久重定向会缓存。
5.2.2.1 永久重定向301
域名永久型调整,即域名永远跳转至另外一个新的域名,之前的域名再也不使用,跳转记录可以缓存到 客户端浏览器
永久重定向会缓存DNS解析记录, 浏览器中有 from disk cache 信息,即使nginx服务器无法访问,浏览器也 会利用缓存进行重定向
实例:
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
server {
listen 80;
server_name www.test.com;
root /data/web/html;
index index.html;
location / {
root /data/web/var;
index index.html;
rewrite / http://www.timinglee.org permanent;
#rewrite / http://www.timinglee.org redirect;
}
}
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vars.conf
[root@nginx conf.d]# mkdir -p /data/web/var
[root@nginx conf.d]# echo var page// > /data/web/var/index.html
[root@nginx conf.d]# nginx -s reload
[root@nginx conf.d]# curl www.test.com
var page//
[root@nginx conf.d]# curl -I var.timinglee.org
HTTP/1.1 301 Moved Permanently
Server: nginx/1.24.0
Date: Sun, 18 Aug 2024 12:10:21 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://www.timinglee.org
[root@nginx conf.d]# curl -I www.test.com
HTTP/1.1 301 Moved Permanently
Server: nginx/1.24.0
Date: Sun, 18 Aug 2024 12:10:48 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://www.timinglee.org
5.2.3 rewrite 案例: break 与 last
测试要求:访问break请求被rewrite至test1,而访问test1转递请求再次被rewrite发送至test2,此测试last和break 分别有什么区别
5.2.3.1 break和last区别实例
当我们遇到break,location下面的程序全部都会停止运行,他会走rewrite这个语句块中最后那个部分,再加上root部分,将会是真实路径
[root@nginx conf.d]# mkdir -p /data/web/html/{test1,test2,break,last}
[root@nginx conf.d]# echo test1 > /data/web/html/test1/index.html
[root@nginx conf.d]# echo test2 > /data/web/html/test2/index.html
[root@nginx conf.d]# echo last > /data/web/html/last/index.html
[root@nginx conf.d]# echo break > /data/web/html/break/index.html
[root@nginx conf.d]# cat /usr/local/nginx/conf.d/vars.conf
server {
listen 80;
server_name www.exam.com;
root /data/web/html;
index index.html;
location /break {
rewrite ^/break/(.*) /test1/$1 break;
rewrite ^/test1/(.*) /test2/$1;
}
location /last {
rewrite ^/last/(.*) /test1/$1 last;
rewrite ^/test1/(.*) /test2/$1;
}
location /test1 {
default_type text/html;
echo "hahahahahaahahahahahahahahahahahahahah!!!!!!!!";
}
location /test2 {
root /data/web/html;
}
}
[root@nginx conf.d]# nginx -s reload
curl www.test.com/last/index.html
5.2.4 rewrite实例: 自动跳转 https
测试要求:基于通信安全考虑公司网站要求全站 https,因此要求将在不影响用户请求的情况下将http请求全 部自动跳转至 https,另外也可以实现部分 location 跳转
[root@nginx conf.d]# mkdir /usr/local/nginx/certs
[root@nginx conf.d]# openssl req -newkey rsa:2048 -nodes -sha256 -keyout /usr/local/nginx/certs/test.org.key -x509 -days 365 -out /usr/local/nginx/certs/test.org.crt
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
listen 443 ssl;
server_name www.test.com;
root /data/web/html;
index index.html;
ssl_certificate /usr/local/nginx/certs/test.org.crt;
ssl_certificate_key /usr/local/nginx/certs/test.org.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
location / {
if ( $scheme = http ){
rewrite / https://$host redirect;
}
}
}
[root@nginx conf.d]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@nginx conf.d]# nginx -s reload
5.2.5 rewrite 实例: 判断文件是否存在
测试要求:当用户访问到公司网站的时输入了一个错误的URL,可以将用户重定向至官网首页
[root@nginx conf.d]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
listen 443 ssl;
server_name www.test.com;
root /data/web/html;
index index.html;
ssl_certificate /usr/local/nginx/certs/test.org.crt;
ssl_certificate_key /usr/local/nginx/certs/test.org.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
location / {
if ( $scheme = http ){
rewrite /(.*) https://$host/$1 redirect;
}
if ( !-e $request_filename ){
rewrite /(.*) https://$host/index,html redirect;
}
}
}
[root@nginx conf.d]# nginx -s reload
5.3 Nginx 防盗链
防盗链基于客户端携带的referer实现,referer是记录打开一个页面之前记录是从哪个页面跳转过来的标 记信息,如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,这就是盗 链,referer就是之前的那个网站域名,正常的referer信息有以下几种:
none: #请求报文首部没有referer首部,
#比如用户直接在浏览器输入域名访问web网站,就没有referer信息。
blocked: #请求报文有referer首部,但无有效值,比如为空。
server_names: #referer首部中包含本主机名及即nginx 监听的server_name。
arbitrary_string: #自定义指定字符串,但可使用*作通配符。示例: *.timinglee.org www.test.*
regular expression: #被指定的正则表达式模式匹配到的字符串,要使用~开头,例如: ~.*\.test\.com
5.3.1 实现盗链
准备工作:#新建一个主机172.25.254.10,盗取另一台主机www.test,.com/images/1.png的图片
#准备盗链web页面:
[root@node1 html]# vim /var/www/html/index.html
<html>
<head>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<title>盗链</title>
</head>
<body>
<img src="http://www.test.com/images/1.jpg" >
<h1 style="color:red">彭于晏超级帅</h1>
<p><a href=http://www.test.com>帅帅帅帅帅</a>出门见喜</p>
</body>
</html>
[root@node1 html]# systemctl restart httpd
5.3.2 实现防盗链
基于访问安全考虑,nginx支持通过ngx_http_referer_module模块,检查访问请求的referer信息是否有效 实现防盗链功能
实例:定义防盗链:
[root@nginx conf.d]# mkdir -p /data/web/html/image
[root@nginx conf.d]# cd /data/web/html/image
[root@nginx image]# cd /data/web/html
[root@nginx html]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
listen 443 ssl;
server_name www.test.com;
root /data/web/html;
index index.html;
ssl_certificate /usr/local/nginx/certs/test.org.crt;
ssl_certificate_key /usr/local/nginx/certs/test.org.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
location /images {
valid_referers none blocked server_names *.test.com ~/.baidu/.;
if ( $invalid_referer ){
rewrite ^/ http://www.test.com/2.jpg;
}
}
}
六 Nginx 反向代理功能
反向代理:reverse proxy,指的是代理外网用户的请求到内部的指定的服务器,并将数据返回给用户的 一种方式,这是用的比较多的一种方式。
Nginx 除了可以在企业提供高性能的web服务之外,另外还可以将 nginx 本身不具备的请求通过某种预 定义的协议转发至其它服务器处理,不同的协议就是Nginx服务器与其他服务器进行通信的一种规范,主 要在不同的场景使用以下模块实现不同的功能
6.1 实现 http 反向代理
6.1.1 http 协议反向代理
6.1.1.1 反向代理配置参数
6.1.1.2 实战实例: 反向代理单台 web 服务器
测试要求:将用户对域 www.timinglee.org 的请求转发给后端服务器处理
[root@nginx ~]# cat /apps/nginx/conf/conf.d/pc.conf
server {
listen 80;
server_name www.text.com;
location / {
proxy_pass http://172.25.254.10;
}
}
6.2nginx的反向代理
6.2.1 nginx的反向代理及动静分离实现
[root@node1 ~]# yum install php -y
[root@node1 ~]# systemctl restart httpd
[root@node1 ~]# vim /var/www/html/index.php
<?php
phpinfo();
?>
[root@nginx html]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.test.com;
location ~ \.php$ {
proxy_pass http://172.25.254.10:80;
}
location /static {
proxy_pass http://172.25.254.20:8080;
}
}
[root@nginx html]# nginx -s reload
6.2.2 nginx反向代理的缓存功能
[root@nginx html]# vim /usr/local/nginx/conf/nginx.conf
proxy_cache_path /usr/local/nginx/proxy_cache levels=1:2:2 keys_zone=proxycache:20m inactive=120s max_size=1g;
[root@nginx html]# vim /usr/local/nginx/conf.d/vhost.conf
server {
listen 80;
server_name www.test.com;
location ~ \.php$ {
proxy_pass http://172.25.254.10:80;
}
location /static {
proxy_pass http://172.25.254.20:8080;
proxy_cache proxycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 10m;
proxy_cache_valid any 1m;
}
}
[root@nginx html]# nginx -s reload
6.2.3 nginx的反向代理负载均衡
[root@nginx html]# vim /usr/local/nginx/conf.d/vhost.conf
upstream webcluster {
#hash $request_uri consistent;
hash $cookie_lee;
server 172.25.254.10:80 max_fails=3 fail_timeout=15s;
server 172.25.254.20:8080 max_fails=3 fail_timeout=15s;
#server 172.25.254.100:80 backup;
}
server {
listen 80;
server_name www.test.com;
location / {
proxy_pass http://webcluster;
}
}
6.2.4 负载均衡实例: MySQL
后端服务器安装 MySQL
[root@node1 ~]# yum install mariadb-server -y
[root@node1 ~]# vim /etc/my.cnf.d/mariadb-server.cnf
[root@node1 ~]# systemctl start mariadb
[root@node1 ~]# mysql
[root@node1 ~]# create user test@'%' identified by 'test';
[root@node1 ~]# grant all on *.* to test@'%';
nginx配置
[root@nginx ~]# vim /usr/local/nginx/tcpconf.d/dns.conf
stream {
upstream mysql {
server 172.25.254.10:3306 max_fails=3 fail_timeout=15s;
server 172.25.254.20:3306 max_fails=3 fail_timeout=15s;
}
server {
listen 3306;
proxy_timeout 60s;
proxy_pass mysql;
}
}
6.2.5 udp 负载均衡实例: DNS
nginx配置
[root@nginx ~]# vim /usr/local/nginx/tcpconf.d/dns.conf
stream {
upstream dns {
server 172.25.254.10:53 max_fails=3 fail_timeout=15s;
server 172.25.254.20:53 max_fails=3 fail_timeout=15s;
}
server {
listen 53;
proxy_timeout 20s;
proxy_pass dns;
}
}
后端服务器安装 MySQL
[root@node1 ~]# vim /etc/named.rfc1912.zones
zone "test.com" IN {
type master;
file "test.com.zone";
allow-update { none; };
};
[root@node1 named]# vim /etc/named.conf
11 // listen-on port 53 { 127.0.0.1; };
12 // listen-on-v6 port 53 { ::1; };
19 // allow-query { localhost; };
[root@node1 named]# vim test.org.zone
$TTL 1D
@ IN SOA ns.test.com. www.test.com. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS ns.test.com.
ns A 172.25.254.10
www A 172.25.254.10
6.3.2 FastCGI实战实例 : Nginx与php-fpm在同一服务器
源码编译php
yum install -y bzip2 systemd-devel libxml2-devel sqlite-devel libpng-devel libcurl-devel oniguruma-devel
[root@nginx php-8.3.9]# ./configure \--prefix=/usr/local/php --enable-fpm --with-fpm-user=nginx --with-fpm-group=nginx --with-curl --with-iconv --with-mhash --with-zlib --with-openssl --enable-mysqlnd --with-mysqli --with-pdo-mysql --disable-debug --enable-sockets --enable-soap --enable-xml --enable-ftp --enable-gd --enable-exif --enable-mbstring --enable-bcmath --with-fpm-systemd --with-config-file-path=/usr/local/php/etc
make && make install
php相关配置优化
[root@nginx ~]# cd /usr/local/php/etc/
[root@nginx etc]# cp php-fpm.conf.default php-fpm.conf
[root@nginx etc]# vim php-fpm.conf #指定pid文件存放位置
17 pid = run/php-fpm.pid
[root@nginx etc]# cd /usr/local/php/etc/php-fpm.d/
[root@nginx php-fpm.d]# cp www.conf.default www.conf
#生成主配置文件
[root@nginx php-fpm.d]# cd /root/php-8.3.9/
[root@nginx php-8.3.9]# cp php.ini-production /usr/local/php/etc/php.ini
[root@nginx php-8.3.9]# vim /usr/local/php/etc/php.ini #修改时区
989 date.timezone = Asia/Shanghai
[root@nginx php-8.3.9]# vim sapi/fpm/php-fpm.service #生成启动文件
#ProtectSystem=full
[root@nginx php-8.3.9]# cp sapi/fpm/php-fpm.service
[root@nginx php-8.3.9]# systemctl start php-fpm.service
[root@nginx php-8.3.9]# netstat -antlupe | grep php
tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 0 126980 148379/php-fpm: mas
准备php测试页面
[root@nginx ~]# mkdir -p /data/web/php
[root@nginx ~]# vim /data/web/php/index.php
<?php
phpinfo();
?>
Nginx配置转发
Nginx安装完成之后默认生成了与fastcgi的相关配置文件,一般保存在nginx的安装路径的conf目录当 中,比如/apps/nginx/conf/fastcgi.conf、/apps/nginx/conf/fastcgi_params。
[root@nginx ~]# mkdir -p /usr/local/nginx/conf.d
[root@nginx ~]# vim /usr/local/nginx/conf.d/php.conf
server {
listen 80;
server_name www.test.com;
root /data/web/html;
index index.html;
location ~ \.php$ {
root /data/web/php;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
}
[root@nginx ~]# nginx -s reload
访问验证php测试结果
添加php环境变量
vim ~/.bash_profile
export PATH=$PATH:/usr/local/nginx/sbin:/usr/local/php/sbin:/usr/local/php/bin:/usr/loca l/openresty/bin
source ~/.bash_profile
6.3.3 php的动态扩展模块(php的缓存模块)
安装memcache模块
[root@nginx memcache-8.2]# yum install autoconf
[root@nginx memcache-8.2]# phpize
[root@nginx memcache-8.2]# ./configure && make && make install
[root@nginx memcache-8.2]# ls /usr/local/php/lib/php/extensions/no-debug-non-zts
20230831/
memcache.so opcache.so
复制测试文件到nginx发布目录中
[root@nginx ~]# cd memcache-8.2/
[root@nginx memcache-8.2]# cp example.php memcache.php /data/php/
[root@nginx ~]# vim /data/web/php/memcache.php
efine('ADMIN_USERNAME','admin'); // Admin Username
define('ADMIN_PASSWORD','test'); // Admin Password
define('DATE_FORMAT','Y/m/d H:i:s');
define('GRAPH_SIZE',200);
define('MAX_ITEM_DUMP',50);
$MEMCACHE_SERVERS[] = '127.0.0.1:11211'; // add more as an array
#$MEMCACHE_SERVERS[] = 'mymemcache-server2:11211'; // add more as an array
配置php加载memcache模块
[root@nginx etc]# vim /usr/local/php/etc/php.ini
extension=memcache
[root@nginx etc]# systemctl reload php-fpm
[root@nginx etc]# php -m |grep mem
memcache
部署memcached
测试结果
性能对比
[root@nginx ~]# ab -n500 -c10 http://www.test.com/index.php
Concurrency Level: 10
Time taken for tests: 0.259 seconds
Complete requests: 500
Failed requests: 54
[root@nginx ~]# ab -n500 -c10 http://www.test.com/example.php
Concurrency Level: 10
Time taken for tests: 0.289 seconds
Complete requests: 500
Failed requests: 0
6.3.4 php高速缓存
测试要求:在我们安装的nginx中默认不支持memc和srcache功能,需要借助第三方模块来让nginx支持此功能,所 以nginx需要重新编译
[root@nginx images]# vim /usr/local/nginx/conf.d/php.conf
upstream memcache {
server 127.0.0.1:11211;
keepalive 512;
}
server {
listen 80;
server_name www.test.com;
root /data/web/html;
index index.html;
location /memc {
internal;
memc_connect_timeout 100ms;
memc_send_timeout 100ms;
memc_read_timeout 100ms;
set $mem_key $query_string;
set $memc_exptime 300;
memc_pass memcache;
}
location ~ \.php$ {
root /data/web/php;
set $key $uri$args;
srcache_fetch GET /memc $key;
srcache_store PUT /memc $key;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
}
测试结果
[root@nginx ~]# ab -n500 -c10 http://www.test.com/index.php
Concurrency Level: 10
Time taken for tests: 0.289 seconds
Complete requests: 500
Failed requests: 0