Nginx高级部分

news2024/11/25 20:39:14

一、web服务基础介绍

Web 服务基础介绍

正常情况下的单次web服务访问流程:

Apache 经典的 Web 服务端

Apache起初由美国的伊利诺伊大学香槟分校的国家超级计算机应用中心开发

目前经历了两大版本分别是1.X和2.X

其可以通过编译安装实现特定的功能

1.Apache prefork 模型 预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024 每个子进程有一个独立的线程响应用户请求 相对比较占用内存,但是比较稳定,可以设置最大和最小进程数 是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景 优点:稳定

缺点:每个用户请求需要对应开启一个进程,占用资源较多,并发性差,不适用于高并发场景 

Apache worker 模型

一种多进程和多线程混合的模型

有一个控制进程,启动多个子进程

每个子进程里面包含固定的线程

使用线程程来处理请求 

当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求

由于其使用了线程处理请求,因此可以承受更高的并发  

优点:相比prefork 占用的内存较少,可以同时处理更多的请求

缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超 时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用(该问题在 prefork模式下,同样会发生)

Apache event模型 

Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll)

每个进程响应多个请求,在现在版本里的已经是稳定可用的模式

它和worker模式很像,最大的区别在于,它解决了keepalive场景下长期被占用的线程的资源浪费问题 (某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)

event MPM中,会有一个专门的线程来管理这些keepalive类型的线程

当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场 景下的请求处理能力

优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keepalive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放

缺点:没有线程安全控制

Nginx-高性能的 Web 服务端 

 阻塞型 I/O 模型(blocking IO)

  • 阻塞IO模型是最简单的I/O模型,用户线程在内核进行IO操作时被阻塞
  • 用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间。内核等到数据包到达后,然 后将接收的数据拷贝到用户空间,完成read操作
  • 用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个I/O请求的过程中,用户线 程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够 

优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源

缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销 较apache 的preforck使用的是这种模式。

同步阻塞:程序向内核发送I/O请求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回,则进 程将一直等待并不再接受新的请求,并由进程轮询查看I/O是否完成,完成后进程将I/O结果返回给 Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看I/O是否完成,这种 方式简单,但是比较慢,用的比较少。 

非阻塞型 I/O 模型 (nonblocking IO

用户线程发起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 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 模型效率较低,可能延迟更大,因为单个连接处理 需要 2 次系统调用,占用时间会有增加

IO多路复用适用如下场合: 

  • 当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
  • 当一个客户端同时处理多个套接字时,此情况可能的但很少出现 当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到I/O复用
  • 当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
  • 当一个服务器要处理多个服务或多个协议,一般要使用I/O复用
信号驱动式 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完成后内核再通知进程 

异步 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复用,因此异步非阻塞使用最多的一种通信方式。

五种io对比: 

每种I/O模型都有其适用的场景和优缺点,在选择合适的I/O模型时需要考虑应用的需求和性能目标。在现代Web开发中,异步I/O模型越来越受欢迎,因为它能够在不牺牲性能的情况下支持高并发这五种 I/O 模型中,越往后,阻塞越少,理论上效率也是最优前四种属于同步 I/O,因为其中真正的 I/O 操作(recvfrom)将阻塞进程/线程,只有异步 I/O 模型才与 POSIX 定义的异步 I/O 相匹配

零拷贝 

零拷贝(Zero-Copy)是一种优化技术,它通过减少数据在不同内存区域之间的复制次数来提高系统的性能。在传统的I/O模型中,数据通常需要经历多次拷贝才能从源头到达目的地。这些拷贝不仅消耗CPU资源,还会增加上下文切换的开销,影响整体的系统性能。

下面是如何通过零拷贝技术解决传统I/O模型中的问题:

### 传统I/O模型的问题
1. **多次数据拷贝**:
   - 数据从磁盘读入时,通常先拷贝到内核空间的缓冲区;
   - 然后从内核空间的缓冲区拷贝到用户空间的缓冲区;
   - 如果数据还需要通过网络发送出去,那么它会再次从用户空间的缓冲区拷贝到内核空间的网络缓冲区。

2. **多次上下文切换**:
   - 每个系统调用(如read和write)都会引发一次从用户态到内核态的上下文切换;
   - 上下文切换会消耗CPU周期。

3. **额外的内存分配和管理**:
   - 每次拷贝都需要分配新的内存缓冲区,并且在拷贝完成后释放旧的缓冲区。

### 零拷贝技术的解决方案
1. **减少数据拷贝次数**:
   - 零拷贝技术尽量避免数据在用户空间和内核空间之间的拷贝;
   - 例如,可以使用DMA(Direct Memory Access)直接将数据从硬件设备(如磁盘)读取到内核缓冲区,而无需再拷贝到用户空间。

2. **减少上下文切换次数**:
   - 通过减少系统调用的数量来减少上下文切换;
   - 例如,使用sendfile等系统调用可以在一次调用中完成从文件读取到网络发送的整个过程。

3. **简化内存管理**:
   - 避免不必要的内存分配和释放操作;
   - 利用直接内存映射或者共享内存区域来减少内存管理的开销。

### 具体实现方法
- **Sendfile**:通过sendfile系统调用可以直接从文件描述符读取数据并通过另一个文件描述符发送出去,无需中间的用户空间缓冲区。
- **Mmap/Memory Mapping**:通过内存映射文件的方式,使文件内容直接映射到进程的地址空间,从而避免了用户空间和内核空间之间的数据拷贝。
- **Direct I/O**:某些文件系统支持直接I/O,允许应用程序直接读写磁盘而绕过内核缓冲区,这样可以减少一次数据拷贝。
- **Ring Buffers**:使用环形缓冲区来存储数据,应用程序和内核都可以访问同一段内存,从而减少数据拷贝。

通过这些技术手段,零拷贝可以显著提高数据传输的效率,降低CPU和内存的使用率,进而提升应用程序的整体性能。

二、Nginx的架构与安装

nginx的架构和进程

 Nginx 进程结构

web请求处理机制

  • 多进程方式:服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直 到用户关闭连接,这样的优势是处理速度快,子进程之间相互独立,但是如果访问过大会导致服务 器资源耗尽而无法提供请求
  • 多线程方式:与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程和此客 户端进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了web服务器 对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可 以相互访问同样的内存地址空间,所以他们相互影响,一旦主进程挂掉则所有子线程都不能工作 了,IIS服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。

nginx进程间通信 

Nginx 是一款广泛使用的高性能 Web 和反向代理服务器,它采用了异步非阻塞的事件驱动架构来处理大量的并发连接。Nginx 的工作模式基于多进程或多线程模型,其中每个进程或线程独立运行并处理不同的客户端连接。为了协调这些进程之间的工作,Nginx 实现了多种进程间的通信机制。下面是 Nginx 进程间通信的主要方式及特点:

### 1. 主进程与工作进程之间的通信
- **主进程**负责监听端口并接收客户端连接请求。
- **工作进程**负责处理具体的客户端请求。

#### 特点:
- **配置更新**:当配置文件发生变化时,主进程负责重新加载配置,并通过信号通知工作进程。
- **平滑重启**:在重启过程中,主进程创建一个新的主进程和一组新的工作进程,然后优雅地关闭旧的工作进程,确保服务不中断。

### 2. 工作进程之间的通信
- **工作进程**之间通常是无状态的,它们各自独立处理请求。
- **共享内存**:Nginx 使用共享内存段来实现工作进程间的通信和同步。

#### 特点:
- **共享内存**:用于存储全局状态信息,如计数器、统计信息等。
- **锁机制**:使用原子操作或轻量级锁来保证共享内存中数据的一致性。

### 3. 使用 Unix 域套接字进行通信
- **Unix 域套接字**:一种用于进程间通信的方法,它允许在同一主机上的进程之间传递数据。

#### 特点:
- **模块化设计**:Nginx 支持通过模块扩展功能,这些模块可以通过 Unix 域套接字与 Nginx 的主进程或工作进程通信。
- **负载均衡模块**:如 Nginx Plus 中的动态负载均衡模块,可以使用 Unix 域套接字来调整后端服务器的权重。
- **监控和管理工具**:如 Nginx 的 Stream 模块,可以使用 Unix 域套接字进行监控和管理操作。

### 4. 信号机制
- **信号**:操作系统提供的机制,用于在进程之间传递简单的信息。

#### 特点:
- **配置重载**:通过发送 `SIGHUP` 信号来通知 Nginx 重新加载配置文件。
- **进程控制**:如发送 `SIGTERM` 信号来优雅地终止进程。

### 5. 文件共享
- **文件共享**:在某些情况下,Nginx 可能需要共享文件句柄或文件内容。

#### 特点:
- **共享文件句柄**:例如,在使用 sendfile 等系统调用时,文件描述符可以直接从一个进程传递给另一个进程。
- **缓存机制**:使用共享内存或文件系统缓存来存储静态内容,减少重复加载。

### 总结
Nginx 的进程间通信主要依赖于以下几种机制:
- **主进程负责配置更新和平滑重启**。
- **工作进程之间通过共享内存和锁机制进行通信**。
- **Unix 域套接字用于模块化扩展和高级功能**。
- **信号机制用于控制和管理进程**。
- **文件共享用于优化数据传输**。

通过这些机制,Nginx 能够高效地处理高并发请求,并保持稳定的服务质量。

Nginx 启动和 HTTP 连接建立

Nginx 是一款流行的高性能 Web 和反向代理服务器,它的启动流程和 HTTP 连接建立过程是其高效处理大量并发连接的关键。下面是这两个过程的总结:

### Nginx 启动流程
1. **主进程初始化**:
   - 创建一个主进程(master process)。
   - 主进程解析配置文件,并根据配置初始化服务器的各种参数和设置。
   - 初始化信号处理机制,以便能够处理来自用户的信号(如 SIGHUP、SIGTERM 等)。

2. **打开监听套接字**:
   - 主进程为每个监听的端口创建并绑定一个套接字。
   - 设置套接字为监听模式,准备接受客户端连接请求。

3. **创建工作进程**:
   - 根据配置文件中的指令创建指定数量的工作进程(worker processes)。
   - 每个工作进程都会复制主进程的监听套接字,并开始监听客户端连接请求。

4. **配置重载**:
   - 如果配置文件发生变化,主进程会在收到 SIGHUP 信号后重新加载配置文件。
   - 平滑重启:创建新的主进程和工作进程,然后优雅地关闭旧的工作进程。

5. **进入运行状态**:
   - 主进程负责管理所有工作进程,并监听来自用户的信号。
   - 工作进程负责处理客户端的连接请求。

### HTTP 连接建立过程
1. **客户端发起连接**:
   - 客户端(如浏览器)通过 TCP 协议向 Nginx 的监听端口发起连接请求。
   - Nginx 的工作进程之一接收这个连接请求。

2. **建立 TCP 连接**:
   - 通过三次握手完成 TCP 连接的建立。
     - 第一步:客户端发送 SYN 包给服务器。
     - 第二步:服务器回复 ACK+SYN 包给客户端。
     - 第三步:客户端发送 ACK 包给服务器。

3. **HTTP 请求处理**:
   - 客户端发送 HTTP 请求报文到已建立的 TCP 连接。
   - Nginx 的工作进程读取请求报文并解析。
   - 根据配置文件中的路由规则处理请求,可能包括转发到后端服务器或直接提供静态内容等。

4. **响应生成**:
   - 根据请求的内容,Nginx 生成相应的 HTTP 响应。
   - 如果请求需要从后端服务器获取数据,Nginx 会作为代理转发请求并等待后端服务器的响应。

5. **发送响应**:
   - 将生成的 HTTP 响应通过已建立的 TCP 连接发送回客户端。

6. **关闭连接**:
   - 根据 HTTP 协议版本的不同,连接可能会被关闭(HTTP/1.0)或保持打开(HTTP/1.1)以供后续请求使用。
   - 对于 HTTP/2 和 HTTP/3,连接可以保持更长时间,以支持多路复用多个请求。

### 总结
- **启动流程**:Nginx 通过主进程初始化配置和创建工作进程来准备处理连接请求。
- **连接建立**:客户端通过 TCP 三次握手与 Nginx 建立连接,然后发送 HTTP 请求,Nginx 处理请求并生成响应,最后通过相同的 TCP 连接发送回客户端。

通过这种高效的启动流程和连接处理机制,Nginx 能够支持大量的并发连接,并保持高性能和稳定性。

Nginx 安装

Nginx版本
  • Mainline version 主要开发版本,一般为奇数版本号,比如1.19
  • Stable version 当前最新稳定版,一般为偶数版本,如:1.20
  • Legacy versions 旧的稳定版,一般为偶数版本,如:1.18

Nginx安装可以使用yum或源码安装,但是推荐使用源码编译安装 

yum的版本比较旧

编译安装可以更方便自定义相关路径

使用源码编译可以自定义相关功能,更方便业务的上的使用

在使用源码安装前,我们需要下载一些依赖

dnf install gcc pcre-devel zlib-devel openssl-devel -y

在这之后我们需要创建nginx用户,解压我们的nginx压缩包

useradd -s /sbin/nologin -M nginx
tar zxf nginx-1.24.0.tar.gz
./configure --prefix=/usr/local/nginx \
--user=nginx \ # 指定nginx运行用户
--group=nginx \ # 指定nginx运行组
--with-http_ssl_module \ # 支持https://
--with-http_v2_module \ # 支持http版本2
--with-http_realip_module \ # 支持ip透传
--with-http_stub_status_module \ # 支持状态页面 
--with-http_gzip_static_module \ # 支持压缩 
--with-pcre \ # 支持正则
--with-stream \ # 支持tcp反向代理
--with-stream_ssl_module \ # 支持tcp的ssl加密
--with-stream_realip_module # 支持tcp的透传ip

把nginx命令添加到环境变量里

vim ~/.bash_profile
export PATH=$PATH:/usr/local/nginx/sbin

平滑升级和回滚

有时候我们需要对Nginx版本进行升级以满足对其功能的需求,例如添加新模块,需要新功能,它允许管理员在不停止服务的前提下更新 Nginx 版本或更改配置。平滑升级的核心思想是在新旧两个主进程之间进行平滑过渡,以确保服务的连续性和可用性,而此时 Nginx又在跑着业务无法停掉,这时我们就可能选择平滑升级

创建新主进程:

  • 当需要升级时,先启动一个新的 Nginx 主进程,并加载最新的配置文件。
  • 新的主进程会创建相应数量的工作进程来处理新的连接请求。

优雅关闭旧进程:

  • 一旦新的主进程及其工作进程准备好接收请求,就可以通过发送 SIGHUP 信号给旧的主进程来触发平滑升级。
  • 接收到信号后,旧的主进程会停止接收新的连接请求,并等待正在处理的请求完成。
  • 一旦所有旧的工作进程都完成了它们的任务,旧的主进程会终止这些工作进程,并最终退出。 

 切换监听套接字:

  • 在整个过程中,新的主进程及其工作进程开始接管新连接,而旧的主进程则逐渐释放资源。
  • 最终,新的主进程接管所有监听套接字,成为新的服务提供者。

验证升级

升级完成后,应该验证新的 Nginx 实例是否正常运行,并确认所有的服务都没有受到干扰。

通过这种方式,Nginx 可以在不影响用户访问的情况下进行版本更新或配置变更,极大地提高了系统的可靠性和用户体验。平滑升级是 Nginx 高可用性的一个重要特性,使得 Nginx 成为了部署高流量网站和服务的理想选择。 

tar zxf nginx-1.26.1.tar.gz
cd nginx-1.26.1/

开始编译

./configure --with-http_ssl_module --withhttp_v2_module
 --with-http_realip_module --with-http_stub_status_module --withhttp_gzip_static_module
 --with-pcre --with-stream --with-stream_ssl_module --
with-stream_realip_module

只要make无需要make install

make

查看两个版本

 ll objs/nginx /usr/local/nginx/sbin/nginx
-rwxr-xr-x 1 root root 1239416 Jul 18 15:08 objs/nginx
-rwxr-xr-x 1 root root 5671488 Jul 18 11:41 /usr/local/nginx/sbin/nginx

把之前的旧版的nginx命令备份

cd /usr/local/nginx/sbin/
cp nginx nginx.24

把新版本的nginx命令复制过去

\cp -f /root/nginx/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 48732 #nginx worker ID
#USR2 平滑升级可执行程序,将存储有旧版本主进程PID的文件重命名为nginx.pid.oldbin,并启动新的
nginx
#此时两个master的进程都在运行,只是旧的master不在监听,由新的master监听80
#此时Nginx开启一个新的master进程,这个master进程会生成新的worker进程,这就是升级后的Nginx进
程,此时老的进程不会自动退出,但是当接收到新的请求不作处理而是交给新的进程处理。

 过滤一下端口

[root@Nginx sbin]# ps aux | grep nginx
root       48732 0.0 0.1   9868 2436 ?       Ss   14:17   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
nobody     48733 0.0 0.2 14200 4868 ?       S   14:17   0:00 nginx: worker 
process
root       52075 0.0 0.3   9876 6528 ?       S   15:41   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
nobody     52076 0.0 0.2 14208 4868 ?       S   15:41   0:00 nginx: worker 
process

用curl -I 查看一下版本

[root@Nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.24.0 ##依旧是旧版本生生效
Date: Thu, 18 Jul 2024 07:45:58 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 18 Jul 2024 03:41:13 GMT
Connection: keep-alive
ETag: "66988ed9-267"
Accept-Ranges: bytes

这个时候我们就可以开始回收旧版本了

[root@Nginx sbin]# kill -WINCH 48732
[root@Nginx sbin]# ps aux | grep nginx
root       48732 0.0 0.1   9868 2436 ?       Ss   14:17   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
root       52075 0.0 0.3   9876 6528 ?       S   15:41   0:00 nginx: master 
process /usr/local/nginx/sbin/nginxnobody     52076 0.0 0.2 14208 4868 ?       S   15:41   0:00 nginx: worker 
process

这个时候我们使用curl -I可以发现,已经变成新版本了

[root@Nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.26.1 #新版本生效
Date: Thu, 18 Jul 2024 07:59:45 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 18 Jul 2024 03:41:13 GMT
Connection: keep-alive
ETag: "66988ed9-267"
Accept-Ranges: bytes

如果升级的版本发现问题需要回滚,可以重新拉起旧版本的worker

cp nginx nginx.26

ls查看一下可以发现现在有三个版本

[root@Nginx sbin]# ls
nginx nginx.24 nginx.26

使用mv命令改名 

mv nginx.24 nginx

接下来进行回滚

[root@Nginx sbin]# kill -HUP 48732
[root@Nginx sbin]# ps aux | grep nginx
root       48732 0.0 0.1   9868 2436 ?       Ss   14:17   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
root       52075 0.0 0.3   9876 6528 ?       S   15:41   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
nobody     52076 0.0 0.2 14208 5124 ?       S   15:41   0:00 nginx: worker 
process
nobody     52130 0.0 0.2 14200 4868 ?       S   16:30   0:00 nginx: worker 
process
[root@Nginx sbin]# kill -WINCH 52075
[root@Nginx sbin]# ps aux | grep nginx
root       48732 0.0 0.1   9868 2436 ?       Ss   14:17   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
root       52075 0.0 0.3   9876 6528 ?       S   15:41   0:00 nginx: master 
process /usr/local/nginx/sbin/nginx
nobody     52130 0.0 0.2 14200 4868 ?       S   16:30   0:00 nginx: worker 
process
root       52137 0.0 0.1 221664 2176 pts/0   S+   16:31   0:00 grep --
color=auto nginx

这个时候我们使用curl -I 命令查看,已经回滚成功了:

[root@Nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.24.0 ##版本回滚完成
Date: Thu, 18 Jul 2024 08:31:51 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 18 Jul 2024 03:41:13 GMT
Connection: keep-alive
ETag: "66988ed9-267"
Accept-Ranges: bytes

三 、Nginx 核心配置详解

配置文件说明

如果我们遇到了什么无法解决的问题,去查询文档是一个不错的习惯,下面是nginx的官方帮助文档,需要的话可以去翻阅

  • nginx 官方帮助文档:http://nginx.org/en/docs/
  • 主配置文件:nginx.conf
  • 子配置文件: include conf.d/*.conf
  • fastcgi, uwsgi,scgi 等协议相关的配置文件
  • mime.types:支持的mime类型,MIME(Multipurpose Internet Mail Extensions)多用途互联网邮 件扩展类型,MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据,是设定某 种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动 使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。

主配置文件结构:四部分

main block:主配置段,即全局配置段,对http,mail都有效

#事件驱动相关的配置 event

{

...

}  

#http/https 协议相关配置段 http {

...

}

#默认配置文件不包括下面两个块 #mail 协议相关配置段

mail {

...

}

#stream 服务器相关配置段 stream {

...

}  

全局配置说明

user nginx nginx;    #启动Nginx工作进程的用户和组

worker_processes [number | auto];   #启动Nginx工作进程的数量,一般设为和CPU核心数相同

worker_cpu_affinity 00000001 00000010 00000100 00001000 | auto ;

#将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进 程独占以一核心CPU,但是可以保证此进程不运行在其他核心上,这就极大减少了nginx的工作进程在不同的 cpu核心上的来回跳转,减少了CPU对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务 器的性能。

CPU MASK:

00000001:0号CPU

00000010:1号CPU

10000000:7号CPU

#示例

worker_cpu_affinity 0001 0010 0100 1000;第0号---第3号CPU

worker_cpu_affinity 0101 1010;

#示例

worker_processes  4;

worker_cpu_affinity 00000010 00001000 00100000 10000000;

[root@centos8 ~]# ps axo pid,cmd,psr | grep nginx

31093 nginx: master process /apps   1

34474 nginx: worker process         1

34475 nginx: worker process         3

34476 nginx: worker process         5

34477 nginx: worker process         7

#错误日志记录配置,语法:error_log file [debug | info | notice | warn | error | crit | alert | emerg]

#error_log logs/error.log; #error_log logs/error.log notice; error_log /usr/local/nginx/logs/error.log error;

#pid文件保存路径 pid      

/usr/local/nginx/logs/nginx.pid; worker_priority 0; #工作进程优先级,-20~20(19) worker_rlimit_nofile 65536;   #所有worker进程能打开的文件数量上限,

                                              #包括:Nginx的所有连接(例如与代理服务器的连接等

                                              #而不仅仅是与客户端的连接 #另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件 数的限制

                                                #最好与ulimit -n 或者limits.conf的值保持一致,

#修改pam限制

[root@Nginx ~]# sudo -u nginx ulimit -n 1024

[root@Nginx ~]# vim /etc/security/limits.conf *               -       nofile         100000 [root@Nginx ~]# sudo -u nginx ulimit -n 100000 daemon off;   #前台运行Nginx服务用于测试、docker等环境。

master_process off|on; #是否开启Nginx的master-worker工作模式,仅用于开发调试场景,默认为 on

events {

                        worker_connections 65535;   #设置单个工作进程的最大并发连接数  

                        use epoll; #使用epoll事件驱动,  

                                        #Nginx支持众多的事件驱动,  

                                        #比如:select、poll、epoll,只能设置在events模块中 设置      

                        accept_mutex on;   

                                        #on为同一时刻一个请求轮流由work进程处理,  

                                        #而防止被同时唤醒所有worker  

                                        #避免多个睡眠进程被唤醒的设置,默认为off  

                                        #新请求会唤醒所有worker进程,此过程也称为"惊群"  

                                        #因此nginx刚安装完以后要进行适当的优化。建议设置为on                             multi_accept on;

                                        #on时Nginx服务器的每个工作进程可以同时接受多个新的网 络连接                                         #此指令默认为off,           

                                        #即默认为一个工作进程只能一次接受一个新的网络连接  

                                        #打开后几个同接受多个。建议设置为on  

}

http 配置块 

#在响应报文中将指定的文件扩展名映射至MIME对应的类型

include           /etc/nginx/mime.types;

default_type     application/octet-stream;                 #除mime.types中的类型外

                                                                                 #指定其它文件的默认MIME类型,浏览 器一般会提示下载

types {  

text/html html;  

image/gif gif;  

image/jpeg jpg;

示例:识别php文件为text/html 

[root@Nginx ~]# vim /usr/local/nginx/html/lee.php
<?php
   phpinfo();
?>
[root@Nginx ~]# curl -I 172.25.254.100/lee.php
HTTP/1.1 200 OK
Server: nginx/1.26.1
Content-Type: application/octet-stream
Content-Length: 24
Last-Modified: Fri, 19 Jul 2024 09:38:52 GMT
Connection: keep-alive
ETag: "669a342c-18"
Accept-Ranges: bytes
[root@Nginx ~]# vim /usr/local/nginx/conf/nginx.conf
default_type text/html;
[root@Nginx ~]# nginx -s reload
[root@Nginx ~]# curl -I 172.25.254.100/lee.php
HTTP/1.1 200 OK
Server: nginx/1.26.1
Date: Fri, 19 Jul 2024 09:49:49 GMT
Content-Type: text/html
Content-Length: 24
Last-Modified: Fri, 19 Jul 2024 09:38:52 GMT
Connection: keep-alive
ETag: "669a342c-18"
Accept-Ranges: bytes

核心配置示例

精确匹配

在server部分使用location配置一个web界面,例如:当访问nginx 服务器的/logo.jpg的时候要显示指定 html文件的内容,精确匹配一般用于匹配组织的logo等相对固定的URL,匹配优先级最高

 mkdir /webdata/nginx/timinglee.org/lee/images -p
 ls /webdata/nginx/timinglee.org/lee/images
 vim /usr/local/nginx/conf.d/vhosts.conf
server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
location = /logo.png {
       root /webdata/nginx/timinglee.org/lee/images;
   }
}

上传logo.jpg图片到/webdata/nginx/timinglee.org/lee/images,重启Nginx并访问测试

访问测试:http://www.timinglee.org/logo.png

区分大小写

~ 实现区分大小写的模糊匹配. 以下范例中, 如果访问uri中包含大写字母的logo.PNG,则以下location匹配logo.png条件不成功 因为 ~ 区分大小写,当用户的请求被执行匹配时发现location中定义的是小写的png, 本次访问的uri匹配失败,后续要么继续往下匹配其他的location(如果有),要么报错给客户端

server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
   location ~ /logo.PNG {
       root /webdata/nginx/timinglee.org/lee/images;
   }
}

重启Nginx并访问测试 http://www.timinglee.org/logo.PNG

访问失败,系统中没有logo.PNG文件

不区分大小写 

~* 用来对用户请求的uri做模糊匹配,uri中无论都是大写、都是小写或者大小写混合,此模式也都会匹 配,通常使用此模式匹配用户request中的静态资源并继续做下一步操作,此方式使用较多

注意: 此方式中,对于Linux文件系统上的文件仍然是区分大小写的,如果磁盘文件不存在,仍会提示404

server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
   location ~* /logo.PNG {
       root /webdata/nginx/timinglee.org/lee/images;
   }
}

 重启Nginx并访问测试 #http://www.timinglee.org/logo.png

URI开始

首先我们先准备实验环境:

 mkdir /webdata/nginx/timinglee.org/lee/images/images{1,2}  
 echo image1 >  /webdata/nginx/timinglee.org/lee/images/images1/index.html
 echo image1 > /webdata/nginx/timinglee.org/lee/images/images2/index.html

然后我们编写子配置文件

server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
   location ^~ /images {
       root /webdata/nginx/timinglee.org/lee/images;
       index index.html;
   }
   location /images1 {
       root /webdata/nginx/timinglee.org/lee/images;
   }
}

重启Nginx并访问测试,实现效果是访问/images1和/images2返回内容一样

[root@node100 ~]# curl 172.25.254.200/images1/
image1
[root@node100 ~]# curl 172.25.254.200/images2/
image1

文件名后缀

上传一个图片到/webdata/nginx/timinglee.org/lee/image

mkdir /webdata/nginx/timinglee.org/lee/images

编写子配置文件

server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
   location ~* \.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|wmf|js|css)$ {
       root /webdata/nginx/timinglee.org/lee/images;
       index index.html;
   }
}

重启Nginx并访问测试 172.25.254.200/logo.png

优先级

server {
   listen 80;
   server_name lee.timinglee.org;
   location / {
       root /webdata/nginx/timinglee.org/lee/html;
   }
   location ^~ /images {
       root /webdata/nginx/timinglee.org/lee/images;
       index index.html;
   }
   location /images1 {
       root /webdata/nginx/timinglee.org/lee/images;
   }
 location ~* \.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|wmf|js)$ {
   root /data/nginx/static3;
   index index.html;
 }
}

匹配优先级:=, ^~, ~/~*,/ location优先级:(location =) > (location ^~ 路径) > (location ~,~* 正则顺序) > (location 完整路径) > (location 部分起始路径) > (/)

生产使用案例 

有了前面的基础之后,我们来尝试做一个生产使用的案例,直接匹配网站根会加速Nginx访问处理

ocation = /index.html {
   ......;
}
location / {
   ......;
}
静态资源配置方法1
location ^~ /static/ {
   ......;
}
静态资源配置方法2,应用较多
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
   ......;
}
多应用配置
location ~* /app1 {
     ......;
}
location ~* /app2 {
     ......;
}

四、Nginx 高级配置 

Nginx 状态页

  • 基于nginx 模块 ngx_http_stub_status_module 实现
  •  在编译安装nginx的时候需要添加编译参数 --with-http_stub_status_module 否则配置完成之后监测会是提示法错误

注意: 状态页显示的是整个服务器的状态,而非虚拟主机的状态 

配置事例:

location /nginx_status {
  stub_status;
   auth_basic           "auth login";
   auth_basic_user_file /apps/nginx/conf/.htpasswd;
   allow 192.168.0.0/16;
   allow 127.0.0.1;
   deny all;
 }

 状态页用于输出nginx的基本状态信息:

 输出信息示例:

Active connections: 291
server accepts handled requests
 16630948 16630948 31070465
 上面三个数字分别对应accepts,handled,requests三个值
Reading: 6 Writing: 179 Waiting: 106
Active connections: #当前处于活动状态的客户端连接数
 #包括连接等待空闲连接数=reading+writing+waiting
 
accepts: #统计总值,Nginx自启动后已经接受的客户端请求连接的总数。
handled: #统计总值,Nginx自启动后已经处理完成的客户端请求连接总数
 #通常等于accepts,除非有因worker_connections限制等被拒绝的
连接
 
requests: #统计总值,Nginx自启动后客户端发来的总的请求数
Reading: #当前状态,正在读取客户端请求报文首部的连接的连接数
 #数值越大,说明排队现象严重,性能不足
 
Writing: #当前状态,正在向客户端发送响应报文过程中的连接数,数值越大,说明
访问量很大
Waiting: #当前状态,正在等待客户端发出请求的空闲连接数
 开启 keep-alive的情况下,这个值等于active – 
(reading+writing)

Nginx 变量使用.

  • nginx的变量可以在配置文件中引用,作为功能判断或者日志等场景使用 .
  • 变量可以分为内置变量和自定义变量 .
  • 内置变量是由nginx模块自带,通过变量可以获取到众多的与客户端访问相关的值

常用内置变量 

$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为任意请求报文首部字部cookie的key名

$http_

#name为任意请求报文首部字段,表示记录请求报文的首部字段,ame的对应的首部字段名需要为小写,如果有 横线需要替换为下划线

#示例: echo $http_user_agent; echo $http_host;

$sent_http_

#name为响应报文的首部字段,name的对应的首部字段名需要为小写,如果有横线需要替换为下划线,此变量有 问题 echo $sent_http_server;

$arg_

#此变量存放了URL中的指定参数,name为请求url中指定的参数 echo $arg_id;

示例:

[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 /var {
   default_type text/html;
   echo $remote_addr;
   echo $args;
   echo $document_root;
   echo $document_uri;
   echo $host;
   echo $http_user_agent;
   echo $request_filename;
   echo $scheme;
   echo $scheme://$host$document_uri?$args;
   echo $http_cookie;
   echo $cookie_key2;
   echo $http_Accept;
 }
}
 
[root@client ~]# curl -b "title=lee;key1=lee,key2=timinglee" 
"lee.timinglee.org/var?search=lee&&id=666666"
172.25.254.20
search=lee&&id=666666
/webdata/nginx/timinglee.org/lee
/var
lee.timinglee.org
curl/7.29.0
/webdata/nginx/timinglee.org/lee/var
http
http://lee.timinglee.org/var?search=lee&&id=666666
title=lee;key1=lee,key2=timinglee
timinglee
*/*

自定义变量

假如需要自定义变量名称和值,使用指令set $variable value;

语法格式:

Syntax: set $variable value;

Default: —

Context: server, location, if

示例:

set $name timinglee;
echo $name;
set $my_port $server_port;
echo $my_port;
echo "$server_name:$server_port";
[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 /var {
   default_type text/html;
   set $name timinglee;
   echo $name;
   set $web_port $server_port;
   echo $web_port;
 }
}
测试输出
[root@client ~]# curl lee.timinglee.org/var
timinglee
80

 五、Nginx Rewrite 相关功能

  • Nginx服务器利用 ngx_http_rewrite_module
  • 模块解析和处理rewrite请求 此功能依靠 PCRE(perl compatible regular expression),因此编译之前要安装PCRE库
  • rewrite是nginx服务器的重要功能之一,用于实现URL的重写,URL的重写是非常有用的功能
  • 比如它可以在我们改变网站结构之后,不需要客户端修改原来的书签,也无需其他网站修改我们的 链接,就可以设置为访问
  • 另外还可以在一定程度上提高网站的安全性。

if 指令 

官方文档:

Module ngx_http_rewrite_module

用于条件匹配判断,并根据条件判断结果选择不同的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  

示例:

location /test {
   index index.html;
   default_type text/html;
   if ( $scheme = http ){
     echo "if ---------> $scheme";
   }
   if ( $scheme = https ){
     echo "if ---------> $scheme";
   }
 }
 location /test2 {
   if ( !-e $request_filename ){
       echo "$request_filename is not exist";
       return 409;
   }
 }

 测试结果:

[root@client ~]# curl lee.timinglee.org/test/
if ---------> http
[root@client ~]# curl lee.timinglee.org/test2/test
/webdata/nginx/timinglee.org/lee/test2/test is not exist

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;
 }
}

测试:

[root@client ~]# curl lee.timinglee.org/test3
lee

break 指令

用于中断当前相同作用域(location)中的其他Nginx配置

与该指令处于同一作用域的Nginx配置中,位于它前面的配置生效

位于后面的 ngx_http_rewrite_module 模块中指令就不再执行 Nginx服务器在根据配置处理请求的过程中遇到该指令的时候,回到上一层作用域继续向下读取配置

该指令可以在server块和locationif块中使用

注意: 如果break指令在location块中后续指令还会继续执行,只是不执行 ngx_http_rewrite_module 模块的指令,其它指令还会执行

使用语法如下:

[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 /break{
     default_type text/html;
     set $name lee;
     echo $name;
     break;
     set $port $server_port;
     echo $port;
   }
}
[root@client ~]# curl lee.timinglee.org/break #当未添加break时
lee
80
[root@client ~]# curl lee.timinglee.org/break #添加break后
lee

 return 指令

return用于完成对请求的处理,并直接向客户端返回响应状态码,比如:可以指定重定向URL(对于特殊重 定向状态码,301/302等) 或者是指定提示文本内容(对于特殊状态码403/500等),处于此指令后的所有配 置都将不被执行,return可以在server、if 和 location块进行配置

语法格式:

return code;                    #返回给客户端指定的HTTP状态码

return code [text];           #返回给客户端的状态码及响应报文的实体内容

                                       #可以调用变量,其中text如果有空格,需要用单或双引号

return code URL;            #返回给客户端的URL地址

示例:

server {
 listen 80;
 server_name lee.timinglee.org;
 root /webdata/nginx/timinglee.org/lee;
 location /return {
     default_type text/html;
     if ( !-e $request_filename){
       return 301 http://www.baidu.com;
       #return 666 "$request_filename is not exist";
     }
     echo "$request_filename is exist";
   }
}
测试:
[root@client ~]# curl lee.timinglee.org/return
/webdata/nginx/timinglee.org/lee/return is exist
[root@client ~]# curl lee.timinglee.org/return1
/webdata/nginx/timinglee.org/lee/return1 is not exist
#测试return 301 http://www.baidu.com;
可在浏览器直接访问lee.timinglee.org/return1

 return示例:

location /test {
 default_type application/json;
 return 200 '{"status:"success"}';
}

Nginx 防盗链

防盗链基于客户端携带的referer实现,referer是记录打开一个页面之前记录是从哪个页面跳转过来的标 记信息,如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,这就是盗 链,referer就是之前的那个网站域名,正常的referer信息有以下几种:

none:                 #请求报文首部没有referer首部,

                             #比如用户直接在浏览器输入域名访问web网站,就没有referer信息。 blocked:             #请求报文有referer首部,但无有效值,比如为空。

server_names:   #referer首部中包含本主机名及即nginx 监听的server_name。 arbitrary_string:  #自定义指定字符串,但可使用*作通配符。

示例:

*.timinglee.org www.timinglee.

* regular expression

#被指定的正则表达式模式匹配到的字符串,要使用~开头,例如: ~.*\.timinglee\.com

正常通过搜索引擎搜索web 网站并访问该网站的referer信息如下:

172.25.254.1 - - [22/Jul/2024:09:27:36 +0800] "GET /favicon.ico HTTP/1.1" 404 149 
"http://lee.timinglee.org/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) 
Gecko/20100101 Firefox/115.0"
2024/07/22 09:27:36 [error] 34596#0: *205 open() 
"/webdata/nginx/timinglee.org/lee/favicon.ico" failed (2: No such file or 
directory), client: 172.25.254.1, server: lee.timinglee.org, request: "GET 
/favicon.ico HTTP/1.1", host: "lee.timinglee.org", referrer: 
"http://lee.timinglee.org/"

 实现盗链:

在一个web 站点盗链另一个站点的资源信息,比如:图片、视频等

#新建一个主机172.25.254.20,盗取另一台主机lee.timinglee.org/images/logo.png的图片
[root@client ~]# yum install httpd -y
[root@client html]# vim /var/www/html/index.html
#准备盗链web页面:
<html>
<head>
   <meta http-equiv=Content-Type content="text/html;charset=utf-8">
   <title>盗链</title>
</head>
 <body>
   <img src="http://lee.timinglee.org/images/logo.png" >
   <h1 style="color:red">欢迎大家</h1>
   <p><a href=http://lee.timinglee.org>狂点老李</a>出门见喜</p>
 </body>
</html>

#重启apache并访问http://172.25.254.20 测试
#验证两个域名的日志,是否会在被盗连的web站点的日志中出现以下盗链日志信息:
[root@Nginx ~]# cat /usr/local/nginx/logs/access.log
172.25.254.1 - - [22/Jul/2024:09:50:01 +0800] "GET /images/logo.png HTTP/1.1" 304 
0 "http://172.25.254.20/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 
Edg/126.0.0.0"
172.25.254.1 - - [22/Jul/2024:09:50:18 +0800] "GET / HTTP/1.1" 304 0 
"http://172.25.254.20/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 
Edg/126.0.0.0"

实现防盗链

基于访问安全考虑,nginx支持通过ngx_http_referer_module模块,检查访问请求的referer信息是否有效 实现防盗链功能

官方文档:

https://nginx.org/en/docs/http/ngx_http_referer_module.html

示例: 定义防盗链:

[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 /images {
   valid_referers none blocked server_names *.timinglee.org ~\.baidu\.;
   if ($invalid_referer){
     #return 403;
     rewrite ^/ http://lee.timinglee.org/daolian.png permanent;
   }
 }
}
#重启Nginx并访问测试
http://172.25.254.20

 六、Nginx 反向代理功能

指定 location 实现反向代理

server {
 listen 80;
 server_name www.timinglee.org;
 location / {
   proxy_pass http://172.25.254.30;
 }
 location ~ /static {
   proxy_pass http://172.25.254.20:8080;
 }
}
#后端web服务器必须要有相对于的访问URL
[root@apache20 ~]# mkdir /var/www/html/static
[root@apache20 ~]# echo static 172.25.254.20 > /var/www/html/static/index.html
[root@apache30 ~]# echo 172.25.254.30 > /var/www/html/index.html
#重启Nginx并访问测试:
[2024-07-25 17:09.35] ~
[Administrator.DESKTOP-P19CNDN] ➤ curl www.timinglee.org/static/
static 172.25.254.20
                                                                                 
        
[2024-07-25 17:09.39] ~
[Administrator.DESKTOP-P19CNDN] ➤ curl www.timinglee.org
172.25.254.30

[root@Nginx ~]# vim /apps/nginx/conf.d/vhost.conf
server {
 listen 80;
 server_name www.timinglee.org;
 location / {
   proxy_pass http://172.25.254.30;
 }
 location ~ \.(png|jpg|gif) {
   proxy_pass http://172.25.254.20:8080;
 }
}

缓存功能

缓存功能默认关闭状态,需要先动配置才能启用

proxy_cache zone_name | off; 默认off
#指明调用的缓存,或关闭缓存机制;Context:http, server, location
#zone_name 表示缓存的名称.需要由proxy_cache_path事先定义
proxy_cache_key string;
#缓存中用于“键”的内容,默认值:proxy_cache_key $scheme$proxy_host$request_uri;

proxy_cache_valid [code ...] time;
#定义对特定响应码的响应内容的缓存时长,定义在http{...}中
 示例:
 proxy_cache_valid 200 302 10m;
 proxy_cache_valid 404 1m;

proxy_cache_path;
#定义可用于proxy功能的缓存;Context:http 
proxy_cache_path path [levels=levels] [use_temp_path=on|off] 
keys_zone=zone_name:size [inactive=time] [max_size=size] [manager_files=number] 
[manager_sleep=time] [manager_threshold=time] [loader_files=number] 
[loader_sleep=time] [loader_threshold=time] [purger=on|off] 
[purger_files=number] [purger_sleep=time] [purger_threshold=time];
#示例:在http配置定义缓存信息
proxy_cache_path /var/cache/nginx/proxy_cache #定义缓存保存路径,proxy_cache会自动创建
   levels=1:2:2 #定义缓存目录结构层次
   #1:2:2可以生成
2^4x2^8x2^8=2^20=1048576个目录
   keys_zone=proxycache:20m     #指内存中缓存的大小,主要用于存放key和metadata
(如:使用次数)
   #一般1M可存放8000个左右的key
   
   inactive=120s       #缓存有效时间
   max_size=10g;     #最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值
#调用缓存功能,需要定义在相应的配置段,如server{...};或者location等
proxy_cache proxycache;
proxy_cache_key $request_uri; #对指定的数据进行MD5的运算做为缓存的key
proxy_cache_valid 200 302 301 10m; #指定的状态码返回的数据缓存多长时间
proxy_cache_valid any 1m;   #除指定的状态码返回的数据以外的缓存多长时间,必须设置,
否则不会缓存
proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | 
http_502 | http_503 | http_504 | http_403 | http_404 | off ; #默认是off
#在被代理的后端服务器出现哪种情况下,可直接使用过期的缓存响应客户端
#示例
proxy_cache_use_stale error http_502 http_503;
proxy_cache_methods GET | HEAD | POST ...;
#对哪些客户端请求方法对应的响应进行缓存,GET和HEAD方法总是被缓存

非缓存场景压测 

#准备后端httpd服务器
[root@apache20 app1]# pwd
/var/www/html/static
[root@apache20 static]# cat /var/log/messages > ./log.html #准备测试页面
[root@apache30 ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.html
Concurrency Level:     100
Time taken for tests:   23.238 seconds
Complete requests:     1000
Failed requests:       0
Total transferred:     2011251000 bytes
HTML transferred:       2010991000 bytes
Requests per second:   43.03 [#/sec] (mean)
Time per request:       2323.789 [ms] (mean)
Time per request:       23.238 [ms] (mean, across all concurrent requests)
Transfer rate:         84521.97 [Kbytes/sec] received

准备缓存配置

[root@Nginx ~]# vim /apps/nginx/conf/nginx.conf
@@@@内容省略@@@@
 #gzip on;
 proxy_cache_path /apps/nginx/proxy_cache levels=1:2:2 keys_zone=proxycache:20m 
inactive=120s max_size=1g; #配置在nginx.conf http配置段
[root@Nginx ~]# vim /apps/nginx/conf.d/vhost.conf
 location ~ /static { #要缓存的URL 或者放在server配置项对所有URL都进行缓存
   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;  #必须指定哪些响应码的缓存
 }
#/data/nginx/proxycache/ 目录会自动生成
[root@Nginx ~]# ll /apps/nginx/proxy_cache/ -d
drwx------ 3 nginx root 4096 7月 25 20:07 /apps/nginx/proxy_cache/
[root@Nginx ~]# tree /apps/nginx/proxy_cache/
/data/nginx/proxycache/
0 directories, 0 files

访问并验证缓存文件

#访问web并验证缓存目录
[root@apache30 ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.html
[root@centos8 ~]# ab -n 2000 -c200 http://www.magedu.org/static/log.html    
Concurrency Level:     100
Time taken for tests:   10.535 seconds
Complete requests:     1000
Failed requests:       0
Total transferred:     2011251000 bytes
HTML transferred:       2010991000 bytes
Requests per second:   94.92 [#/sec] (mean)
Time per request:       1053.507 [ms] (mean)
Time per request:       10.535 [ms] (mean, across all concurrent requests)
Transfer rate:         186435.60 [Kbytes/sec] received
#验证缓存目录结构及文件大小
[root@Nginx ~]# tree /apps/nginx/proxy_cache/
/apps/nginx/proxy_cache/
└── e
   └── 50
       └── 99
           └── 319432ef3663735a9d3cb4e0c1d9950e
3 directories, 0 files

七、nginx 二次开发版本

openresty

 Nginx 是俄罗斯人发明的, Lua 是巴西几个教授发明的,中国人章亦春把 LuaJIT VM 嵌入到 Nginx 中,
实现了 OpenResty 这个高性能服务端解决方案
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方
模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服
务和动态网关。
OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将Nginx 
有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言
调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高
性能 Web 应用系统。
OpenResty 由于有功能强大且方便的的API,可扩展性更强,如果需要实现定制功能,OpenResty是个不错的
选择

编译安装 openresty 

[root@Nginx ~]#dnf -yq install gcc pcre-devel openssl-devel perl
[root@Nginx ~]#useradd -r -s /sbin/nologin nginx
[root@Nginx ~]#cd /usr/local/src
[root@Nginx src]#wget https://openresty.org/download/openresty-1.17.8.2.tar.gz
[root@Nginx src]#tar xf openresty-1.17.8.2.tar.gz 
[root@Nginx src]#cd openresty-1.17.8.2/
[root@Nginx openresty-1.17.8.2]#./configure \
--prefix=/apps/openresty \
--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 openresty-1.17.8.2]#make && make install
[root@Nginx openresty-1.17.8.2]#ln -s /apps/openresty/bin/* /usr/bin/
[root@Nginx openresty-1.17.8.2]#openresty -v
nginx version: openresty/1.17.8.2
[root@Nginx openresty-1.17.8.2]#openresty 
[root@Nginx openresty-1.17.8.2]#ps -ef |grep nginx

测试:

[root@Nginx ~]#curl 10.0.0.18

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2061295.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

<数据集>航拍牧场牛羊识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;1021张 标注数量(xml文件个数)&#xff1a;1021 标注数量(txt文件个数)&#xff1a;1021 标注类别数&#xff1a;3 标注类别名称&#xff1a;[cattle, cow, sheep] 序号类别名称图片数框数1cattle29741282cow6740…

网络安全漏洞防护技术原理与应用

网络安全漏洞概述 概念 定义&#xff1a;又称脆弱性&#xff0c;简称漏洞。一般是致使网络信息系统安全策略相冲突的缺陷&#xff08;安全隐患&#xff09; 影响&#xff1a;机密性受损、完整性破坏、可用性降低、抗抵赖性缺失、可控制性下降、真实性不保等 范围&#xff…

Kerberos认证以及黄金票据白银票据的简单介绍

目录 1. 什么是Kerberos认证2. Kerberos认证流程3. 票据伪造4. 金票和银票的原理5. 金票和银票的区别6. 针对Kerberos的攻击 1. 什么是Kerberos认证 Kerberos是一种网络身份认证的协议&#xff0c;协议设计目的是通过使用秘钥加密技术为客户端/服务器应用程序提供强身份验证&am…

【vue3|第24期】深入了解useRouter:方法、属性与使用示例

日期&#xff1a;2024年8月20日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

安装CUDA、CUDNN、pytorch

2.1 安装CUDA 2.1.1找到CUDA的版本号 2.1.1.1 屏幕的左下角&#xff0c;找到NVIDIA控制面板的图标&#xff0c;如下图所示&#xff1a; 图2.1.1.1 NVIDIA控制面板图标 2.1.1.2 打开NVIDIA控制面板&#xff0c;找到系统信息&#xff0c;如下图所示&#xff1a; 图2.1.1.2 系…

Qt中英文支持

目的 就是想让QT编的软件支持中英文。 情况 1、首先配置项目的pro文件&#xff1a; 这样就会生成相应的翻译配置文件&#xff0c;当前是&#xff1a; translate1_cn.ts&#xff1a;中文的配置文件&#xff0c;因为一般默认就是中文&#xff0c;所以一般中文的翻译文件是不需…

GPT的token是怎么计算的

百花齐放的AI模型 自从ChatGPT横空出世 计算机进入了AI时代 国内也诞生了不少优秀的替代品 讯飞系、通义系、文心等等 国内排名最高的阿里通义 720亿参数的模型Qwen-72B 力压Llama 2等国内外开源大模型 登顶Hugging Face的开源大模型排行榜首 包括C-Eval、CMMLU、Gaok…

electron-vite封装UI级的消息提示

说明 Electron Vite Vue3 Element Plus Electron中写提示有两种方案&#xff1a; 系统级&#xff1a;electron带的dialog相关APIUI级&#xff1a;UI框架内部的提示&#xff0c;如ElMessage、ElMessageBox、ElNotification等 今天来封装一下UI级别的提示 代码 效果图 源…

tomcat利用 nginx 反向代理

利用 nginx 反向代理功能&#xff0c;实现图中的代理功能&#xff0c;将用户请求全部转发至指定的同一个 tomcat 主机 利用 nginx 指令 proxy_pass 可以向后端服务器转发请求报文 , 并且在转发时会保留客户端的请求报文中的 host首部 实现 tomcat 中的负载均衡 动态服务器的…

day6JS-DOM(文档对象模型)

DOM树 DOM 操作 1. 获取元素 1.1 根据id名获取元素 document.getElementById("id名"); 案例&#xff1a; <body><div id"box">div盒子</div><h1>一级标题</h1><script>console.log(document.getElementById(&quo…

软件测试费用怎么算?湖南软件测评公司简析

随着信息技术的迅猛发展&#xff0c;软件在各行各业中的应用日益广泛&#xff0c;软件测试的重要性愈加凸显。特别是对于各种规模的企业&#xff0c;确保软件质量和安全性不仅是提升竞争力的关键因素&#xff0c;更是维护用户信赖的基础。 软件测试作为软件开发过程中的关键环…

IO进程线程8月21日

1&#xff0c;思维导图 2&#xff0c;登录 #ifndef __LOG_H__ #define __LOG_H__ #include<myhead.h> typedef struct {char name[20];char pwd[20]; }str;int regist();int login(); #endif#include"log.h" int login() {char a[20]"\n";str p,s;…

IPC进程间通信方式及网络通信

一、IPC进程间通信方式 1.共享内存&#xff08;最高效的进程间通信方式&#xff09; 其允许两个或多个进程共享一个给定的存储区&#xff0c;这一段存储区可以被两个或以上的进程映射至自己的地址空间中&#xff0c;一个进程写入共享内存的信息&#xff0c;可以被其他使用这个…

SQL Server数据库查询常用语句汇总

这里&#xff0c;汇总了在使用SQL Server数据库的过程中经常会写的查询语句。 情形1 根据时间查询最新一条数据 &#xff08;dtime类型为datetime&#xff09; select top 1 名称 as name,列名1 as parname1,列名2 as parname2,dtime,col1,col2 from demo order by dtime …

算法基础及例题

1、双指针 维护区间信息、子序列匹配、利用序列有序性、单项链表找环双指针 - OI Wiki (oi-wiki.org) 盛最多水的容器https://leetcode.cn/problems/container-with-most-water/ public class Solution {public int maxArea(int[] height) {int l 0, r height.length - 1;int…

docker 导出导入镜像

这两天我在自己的虚拟机器上想docker pull tomcat 但是一直失败&#xff0c;那么我想到了一个思路就是在阿里云上的服务器上把镜像打包&#xff0c;然后倒导入的本地的虚拟机上。 查看当前的镜像&#xff1a; 我们现在想要打包哪个镜像呢&#xff0c;比如打包&#xff1a;tom…

线段树-点修区查

翻博客的时候突然发现线段树好像一个没有&#xff0c;我就准备把线段树给讲一下 分三个章节 点修区查 区修区查 区修区查&#xff08;带乘法&#xff09; 今天这一章比较简单&#xff0c;最多就区查稍微要动一点脑子 题目简介 输入n和m&#xff0c;n代表数的个数&#x…

python多进程multiprocessing(共享字典)

一&#xff0c;线程与进程的区别 纲领&#xff1a;进程是资源分配的最小单位&#xff0c;线程是CPU调度的最小单位 听老师傅说&#xff0c;在python上线程作用不大&#xff0c;最好还是使用多进程。 二&#xff0c;简单使用multiprocessing 检测一下电脑性能如何&#xff1a…

Mac电脑遇到DNS解析失败,ip可以访问,域名无法访问

当Mac电脑遇到DNS解析失败的问题时&#xff0c;可以尝试以下几个解决方法‌&#xff1a; 1.检查网络连接‌&#xff1a;确保Mac已连接到可用的网络&#xff0c;并且网络连接正常。可以尝试重新连接Wi-Fi或使用有线连接来排除网络问题。 2.清除DNS缓存‌&#xff1a;打开终端应…

技术革新,智能恢复:2024年AI驱动的数据恢复软件

数据的脆弱性不容忽视&#xff0c;误删、格式化、硬盘损坏等意外情况时有发生&#xff0c;让我们面临数据丢失的风险。幸运的是现在有不少数据恢复精灵应运而生。今天&#xff0c;就让我们一起探索2024年大家都在用的数据恢复精灵工具。 1.福昕数据恢复 链接直达&#xff1a;…