Linux的Nginx

作者:

在

功能

  • 静态的web资源服务器 HTML,image,js,CSS,txt 等静态资源
  • 结合 FastCGI/uWSGI/SCGI 等协议反向代理动态资源请求
  • HTTP/HTTPS 协议的反向代理
  • imap4/pop3 协议的反向代理
  • TCP/UDP 协议的反向代理

基本特征

  • 模块化设计,较好的扩展性
  • 高可靠性高性能高于 Apache
  • 支持热部署:不停机更新配置文件,升级版本,更换日志文件
  • 低内存消耗:10000个 keep-alive 连接模式下的非活动连接,仅需2.5M内存
  • 事件驱动epoll,异步非阻塞IO,内存映射等等特性

Nginx 基础

对于 Nginx 的安装,我们常用的方法为在线安装和源码包编译安装

其中:

  • yum 下载的相关文件都是放在默认的位置,简单高效
  • 编译安装可以自定义版本和功能,更适合业务的定制化

YUM 安装

# 安装
[root@localhost ~]# yum provides nginx
[root@localhost ~]# yum install nginx -y
# 启动
[root@localhost ~]# systemctl start nginx && systemctl enable nginx
# 关闭防火墙和SELINUX
# 测试
[root@localhost ~]# curl -I 127.0.0.1
HTTP/1.1 200 OK
Server: nginx/1.20.1

相关文件

文件 说明
/etc/nginx/nginx.conf 主配置文件
/var/log/nginx/ 日志文件目录
/usr/share/nginx/html/ 默认站点根目录
/usr/lib/systemd/system/nginx.service Systemd服务文件
/var/run/nginx.pid 主进程ID文件
/usr/share/nginx/modules 动态加载模块目录
/etc/nginx/conf.d/ 子配置文件目录

主配置文件(/etc/nginx/nginx.conf)

image-20260818165715944

CPU亲和性设置

worker_processes 8; # 指定 8 个 worker 进程

worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

这段配置是 Nginx 中针对多核 CPU 服务器进行的核心性能优化。它通过手动绑核,确保每个 Nginx 工作进程(Worker)独占一个 CPU 核心。

下面我来逐行拆解:

1. worker_processes 8;

  • 含义:启动 8 个 Nginx 工作进程。
  • 背景:Nginx 采用事件驱动+异步非阻塞模型,通常建议将 Worker 数量设为 CPU 核心数(这里是 8 核),以充分利用多核并行处理能力。

2. worker_cpu_affinity 00000001 ... 10000000;

  • 含义:将 8 个 Worker 进程分别绑定到 8 个不同的 CPU 核心上运行。
  • 核心作用:防止 CPU 缓存频繁失效(避免进程在不同核心间“跳跃”),提升 CPU 缓存命中率,从而降低延迟并提高吞吐量。

3. 掩码(Mask)深度解析

配置中的 00000001 等是 CPU 亲和性掩码,采用二进制位图表示。

规则:从右向左读取,第 N 位(从 0 开始)为 1,代表允许该 Worker 运行在第 N 号 CPU 核心上。

Worker 序号 掩码 (二进制) 对应核心 (从右往左数)
第 1 个 00000001 CPU 0
第 2 个 00000010 CPU 1
第 3 个 00000100 CPU 2
第 4 个 00001000 CPU 3
第 5 个 00010000 CPU 4
第 6 个 00100000 CPU 5
第 7 个 01000000 CPU 6
第 8 个 10000000 CPU 7

主配置文件内容

[root@localhost ~]# grep -Ev '^$|^#|s*#' /etc/nginx/nginx.conf
# 全局配置块
## 运行用户
user nginx;
## 工作进程数
worker_processes auto;  # 默认就是八个
## 错误日志
error_log   /var/log/nginx/error.log;
## pid文件存放位置
pid    /run/nginx.pid;
##  动态加载模块配置
include   /usr/share/nginx/modules/*.conf;
# events配置块
events {
    # 设置单个工作进程最大并发连接数
    worker_connections 1024;
}
# http配置块
http {
    ## 日志格式定义
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    ## 访问日志
    access_log  /var/log/nginx/access.log  main;
    ## 启用 sendfile() - 利用操作系统内核的零拷贝技术:即数据拷贝无需切换至用户空间
    sendfile            on;
    ## 启用 TCP_NOPUSH - 优化网络包传输:即数据包填满时发送
    tcp_nopush          on;
    ## 启用  TCP_NODELAY - 减少延迟:即基于keepalive连接,尽快发送小数据包
    tcp_nodelay         on;
    ## keepalive 超时时间
    keepalive_timeout   65;
    ## 设置用于存储MIME类型映射的哈希表最大大小
    types_hash_max_size 4096;
    ## 定义文件扩展名与MIME类型的映射
    include             /etc/nginx/mime.types;
    ## 定义响应包默认MIME类型
    default_type        application/octet-stream;
    ## 加载子配置文件
    include /etc/nginx/conf.d/*.conf;
    # server配置块
    server {
        ## 监听ipv4和ipv6 80端口
        listen       80;
        listen       [::]:80;
        ## 定义虚拟主机的名称-域名
        server_name  _;
        ## 根目录
        root         /usr/share/nginx/html;
        ## 加载默认站点子配置文件
        include /etc/nginx/default.d/*.conf;
        ## 定义错误页
        error_page 404 /404.html;
        # location配置块
        location = /404.html {
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }
}

# 默认配置文件不包括下面两个块
# mail协议相关配置段
mail {
  ...
}
# stream服务器相关配置段
stream {
  ...
}

虚拟主机(就是后端网站)

基于不同的IP、不同的端口以及不同的域名实现不同的虚拟主机,依赖于核心模块 ngxhttp core_module 实现

案例分析

  • 定义 PC 站点,根目录为 /data/nginx/site01,直接通过 x.x.x.x:8001
  • 定义 Mobile 站点,根目录为 /data/nginx/site02,通过自定义域名访问
  • 定义 Test 站点,根目录为 /data/nginx/site03, 仅能通过本地 127.0.0.1:8003 访问
  • 定义 Test 站点,访问 127.0.0.1:8003/status,根目录为 /data/nginx
[root@localhost ~]# mkdir -pv /data/nginx/site0{1..3}/
[root@localhost ~]# mkdir -pv /data/nginx/status
[root@localhost ~]# echo "Running 3 websites" > /data/nginx/status/index.html
[root@localhost ~]# echo "Hello PC Website!" > /data/nginx/site01/index.html
[root@localhost ~]# echo "Hello Moblie Website!" > /data/nginx/site02/index.html
[root@localhost ~]# echo "Hello Local Test Website!" > /data/nginx/site03/index.html
[root@localhost ~]# cat << EOF > /etc/nginx/conf.d/vhost.conf
# PC
server {
    listen  8001;
    location  / {
        root  /data/nginx/site01;
    }
}
# Mobile
server {
    listen 80;
    server_name m.test.com;
    location / {
        root /data/nginx/site02;
    }
}
# Test
server {
    listen 127.0.0.1:8003;
    location / {
        root /data/nginx/site03;
    }
    location /status {
        root /data/nginx;  
    }
}
EOF
# 检查配置文件并重新加载
[root@localhost ~]# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@localhost ~]# nginx -s reload
# 测试验证
[root@localhost ~]# curl 192.168.226.128:8001
Hello PC Website!
[root@localhost ~]# curl -H 'Hostname: p.test.com' 192.168.226.128
Hello Moblie Website!
[root@localhost ~]# curl 192.168.226.128:8003
curl: (7) Failed to connect to 192.168.226.128 port 8003: Connection refused
# 定义 location 时:文件的绝对路径等于 root+location
[root@localhost ~]# curl -L 127.0.0.1:8003/status
Running 3 websites

1. 配置文件逐段详解

这个文件定义了三个独立的 server 块(虚拟主机),分别监听不同的端口或域名。


第一部分:PC 网站

nginx

# PC
server {
    listen  8001;
    location  / {
        root  /data/nginx/site01;
    }
}
  • 监听:所有 IP 地址的 8001 端口。
  • 逻辑:只要访问服务器的 8001 端口,无论用什么域名,都会命中这个块。
  • 根目录:root 指定了文件系统的绝对路径。当你访问 http://IP:8001/ 时,Nginx 会去 /data/nginx/site01/ 下找 index.html。
  • 验证:你的第一个 curl 192.168.226.128:8001 成功,就是因为匹配到了这里。

第二部分:Mobile 网站(域名虚拟主机)

nginx

# Mobile
server {
    listen 80;
    server_name m.test.com;
    location / {
        root /data/nginx/site02;
    }
}
  • 监听:默认的 HTTP 端口 80。
  • 关键点:server_name m.test.com。这意味着只有 HTTP 请求头中的 Host 字段为 m.test.com 时,才会进入这个块。
  • 验证:你的第二个 curl -H 'Hostname: p.test.com' 192.168.226.128 使用了自定义请求头,返回了 Hello Moblie Website!,说明命中了该配置并正确返回了 site02 的内容。

第三部分:Test 网站(本地回环限制)

nginx

# Test
server {
    listen 127.0.0.1:8003;
    location / {
        root /data/nginx/site03;
    }
    location /status {
        root /data/nginx;  
    }
}
  • 监听:127.0.0.1:8003。
    • 重点:这里指定了具体的 IP 地址 127.0.0.1(即 localhost),而没有像前两个那样写 listen 80(默认代表 0.0.0.0,即所有网卡)。
    • 结果:这个服务只监听在本地环回接口上,不对外网的网卡(如 192.168.226.128)开放。
  • 根目录与 Location 匹配:
    • location /:根目录指向 /data/nginx/site03。
    • location /status:注意,这里的 root 是 /data/nginx。
    • Nginx 路径拼接规则:当访问 /status 时,Nginx 会去查找 root + location 的路径,即 /data/nginx + /status = /data/nginx/status/index.html。
  • 命中网站

    1. 第一个端口网站

    image-20260818191230896

    1. 第二个在 linux 上用域名访问

    ​ image-20260819084451941

    3.本地环回网站访问

    image-20260819085348031

    1. 重定向访问

    image-20260819090218420

    关于 -L 参数

    您问的 -L 在 curl 命令中是指:

    bash

    curl -L 127.0.0.1:8003/status
    参数 含义
    -L 跟随重定向(Follow Redirects)

    具体作用:

    • 如果服务器返回 301/302 重定向响应,-L 会让 curl 自动跳转到新的地址。
    • 不加 -L,curl 只会显示重定向的响应头,不会自动访问新地址。

    为什么设计成这样?

    工具 设计理念
    浏览器 用户体验优先,自动完成跳转
    curl 精确调试优先,让开发者看到完整的 HTTP 交互过程(包括状态码、响应头等)

    curl 不加 -L 的目的是让您看到原始响应,方便调试重定向逻辑。如果需要自动跳转,手动加 -L 即可。


    Location

    Nginx 的 location 指令是配置请求路由的核心机制,其匹配规则基于 URI 和修饰符,优先级由匹配类型和顺序共同决定。

    匹配类型与语法

    location [ = | ~ | ~* | ^~ ] uri { ... }
    修饰符 匹配类型 语法示例 说明
    = 精确匹配 location = /path 仅当 URI 完全等于 /path 时匹配,优先级最高
    ^~ 前缀匹配 location ^~ /images/ 匹配以 /images/ 开头的 URI,停止后续正则检查
    ~ 正则匹配 location ~ .php$ 区分大小写,正则表达式匹配,按配置文件顺序匹配
    ~* 正则匹配 location ~* .(jpg,png)$ 不区分大小写,正则表达式匹配,按配置文件顺序匹配
    无修饰符 前缀匹配 location /static/ 匹配以 /static/ 开头的 URI,需继续正则检查
    / 通用匹配 location / 匹配所有请求,优先级最低

    优先级:= ^~ ~/~* 无修饰符 /

    ==匹配规则可以ai辅助写出来,不用自己记==


    访问控制

    Nginx 的访问控制是保障服务器安全的核心机制,通过多种方式精细化管控请求入口

    基于 IP

    典型场景

    # 黑名单:默认允许,拒绝特定IP
    location /admin {
        deny 192.168.226.128;  # 拒绝单个 IP
        deny 10.0.0.0/8;     # 拒绝网段
        allow all;           # 允许其他 IP
    }
    
    # 白名单:默认拒绝,允许可信IP
    location /api {
        allow 192.168.1.0/24;  # 允许网段
        allow 172.16.1.1;      # 允许单个 IP
        deny all;              # 拒绝其他
    }

    基于 HTTP 认证

    典型场景

    # 生成密码文件
    ## 首次创建
    htpasswd -c /etc/nginx/.htpasswd admin
    ## 追加用户
    htpasswd -c /etc/nginx/.htpasswd root
    
    # 后台管理
    location /admin {
        auth_basic "Tip: input password!";                 # 认证提示语
        auth_basic_user_file /etc/nginx/.htpasswd;   # 密码文件路径
    }

    案例分析

    仅能够通过 admin 用户本地访问后台管理,其他所有IP和用户均不允许

    # 准备实验环境
    [root@localhost ~]# htpasswd -c /etc/nginx/.htpasswd admin
    New password:
    Re-type new password:
    Adding password for user admin
    [root@localhost ~]# mkdir -pv /data/nginx/site04/admin
    [root@localhost ~]# echo "site04" > /data/nginx/site04/admin/index.html
    [root@localhost ~]# cat << EOF > /etc/nginx/conf.d/site04.conf
    server {
        listen  8004;
        # 精细化控制:基于请求属性
        if ($http_user_agent ~* bot) {
            return 403;
        }
        location  /admin {
            root  /data/nginx/site04;
            auth_basic "Tip: input password!";
            auth_basic_user_file /etc/nginx/.htpasswd;
            allow 127.0.0.1;
            deny all;
        }
    }
    EOF
    [root@localhost ~]# nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    [root@localhost ~]# nginx -s reload
    # 测试验证
    client01 ~ % curl -I 172.16.175.129:8004/admin
    HTTP/1.1 403 Forbidden
    [root@localhost ~]# curl -u admin:123 http://127.0.0.1:8004/admin/
    site04
    [root@localhost ~]# curl -I -u root:123 http://127.0.0.1:8004/admin/
    HTTP/1.1 401 Unauthorized
    ## 模拟 bot 访问
    [root@localhost ~]# curl -I -u root:123 -H 'User-agent: bot' http://127.0.0.1:8004/admin/
    HTTP/1.1 403 Forbidden

    实验目标

    搭建一个带有访问控制和密码认证的 Nginx 站点:

    • 只有本地(127.0.0.1)能访问 /admin 目录
    • 访问时需要输入用户名和密码
    • 自动拦截爬虫/机器人(User-Agent 包含 “bot”)

    第一步:创建密码文件

    bash

    htpasswd -c /etc/nginx/.htpasswd admin

    逐词拆解:

    部分 含义
    htpasswd Apache HTTP 服务器自带的密码生成工具(Nginx 也使用它)
    -c create,创建新文件(如果文件已存在会覆盖)
    /etc/nginx/.htpasswd 密码文件的存放路径(.htpasswd 是约定俗成的文件名)
    admin 要创建的用户名

    交互过程:

    text

    New password:          # 输入密码(比如 123)
    Re-type new password:  # 再次输入确认
    Adding password for user admin  # 创建成功

    生成的文件内容:

    bash

    cat /etc/nginx/.htpasswd
    # 输出类似:admin:$apr1$random...(加密后的密码)

    image-20260819104618741

    image-20260819104648116

    第 3 行:# 精细化控制,防止爬虫:基于请求属性

    部分 含义
    # 注释符,这一行只是给人看的说明,Nginx 不执行

    作用:告诉阅读配置文件的人,下面这段代码是用来防止爬虫的。


    第 4-6 行:if ($http_user_agent ~* bot) { return 403; }

    nginx

    if ($http_user_agent ~* bot) {
        return 403;
    }
    部分 含义
    if 条件判断语句
    $http_user_agent Nginx 内置变量,存储客户端的 User-Agent(浏览器标识)
    ~* 正则匹配修饰符:~* 表示不区分大小写匹配
    bot 要匹配的关键词(如 Googlebot、Baiduspider)
    return 403 返回 HTTP 状态码 403 Forbidden(禁止访问)

    通俗理解:

    text

    如果 客户端的 User-Agent 中包含 "bot"(不区分大小写)
        那么 直接返回 403 禁止访问

    第 9 行:auth_basic "Tip: input password!";

    部分 含义
    auth_basic 开启 HTTP 基本认证(弹出用户名密码输入框)
    "Tip: input password!" 认证对话框显示的提示文字

    浏览器效果:

    text

    ┌──────────────────────────────┐
    │  需要身份验证                  │
    │  Tip: input password!         │
    │  用户名: [__________]         │
    │  密码:  [__________]         │
    │  [确定]  [取消]               │
    └──────────────────────────────┘

    第 10 行:auth_basic_user_file /etc/nginx/.htpasswd;

    部分 含义
    auth_basic_user_file 指定存放用户名和密码的文件路径
    /etc/nginx/.htpasswd 密码文件的位置

    密码文件内容(用 htpasswd 命令生成):

    text

    admin:$apr1$abc123$xyz789...   ← 加密存储的密码

    第 11 行:allow 127.0.0.1;

    部分 含义
    allow 允许特定 IP 访问
    127.0.0.1 本地回环地址(只有本机能访问)

    通俗理解:只允许本机(127.0.0.1)访问这个目录。


    第 12 行:deny all;

    部分 含义
    deny 拒绝特定 IP 访问
    all 所有 IP 地址

    执行逻辑:

    text

    先检查 allow 127.0.0.1 → 如果是 127.0.0.1,放行
    再检查 deny all → 如果不是 127.0.0.1,拒绝

    结果:只有 127.0.0.1 能访问,其他 IP 全部被拒绝。

    image-20260819110914905

    四个测试命令讲解

    测试 1:外部 IP 访问 → 403 Forbidden

    bash

    [root@localhost ~]# curl 192.168.226.128:8004/admin -I
    HTTP/1.1 403 Forbidden
    Server: nginx/1.20.1
    Date: Wed, 19 Aug 2026 02:58:02 GMT
    Content-Type: text/html
    Content-Length: 153
    Connection: keep-alive

    命令拆解

    部分 含义
    curl HTTP 请求工具
    192.168.226.128:8004/admin 用物理网卡 IP(非 127.0.0.1)访问 /admin 路径
    -I 只显示响应头,不下载页面内容

    失败原因:来源 IP 不是 127.0.0.1,被 deny all 拦截。


    测试 2:本地 + 正确密码 → ✅ 成功

    bash

    [root@localhost ~]# curl -u admin:123 http://127.0.0.1:8004/admin/
    site04

    命令拆解

    部分 含义
    -u admin:123 user,提供用户名 admin,密码 123
    http://127.0.0.1:8004/admin/ 用本地回环地址访问(注意末尾有 /)
    site04 返回的页面内容

    成功原因:来源 IP 是 127.0.0.1(通过 allow)+ 用户名密码正确(通过 auth_basic)。


    测试 3:本地 + 错误用户名 → 401 Unauthorized

    bash

    [root@localhost ~]# curl -I -u root:123 http://127.0.0.1:8004/admin/
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Basic realm="Tip: input password!"

    命令拆解

    部分 含义
    -u root:123 用户名 root,密码 123
    -I 只显示响应头

    失败原因:用户名 root 不在 .htpasswd 密码文件中,认证失败。


    测试 4:本地 + 正确密码 + 模拟爬虫 → 403 Forbidden

    bash

    [root@localhost ~]# curl -I -u admin:123 -H 'User-agent: bot' http://127.0.0.1:8004/admin/
    HTTP/1.1 403 Forbidden

    命令拆解

    部分 含义
    -u admin:123 提供了正确的用户名和密码(但没用上)
    -H 'User-agent: bot' Header,自定义请求头,设置 User-Agent 为 bot
    -I 只显示响应头

    失败原因:User-Agent 包含 “bot”,被外层的 if 语句提前拦截,返回 403,认证环节未执行。


    401 vs 403 区别

    状态码 含义 触发场景
    401 Unauthorized 未认证 用户名密码错误或缺失
    403 Forbidden 禁止访问 IP被拒绝 或 User-Agent被拦截

    记忆要点:

    • 401 = 认证问题(”你是谁?”)
    • 403 = 权限问题(”你谁都不行!”)

    完整执行流程图

    text

                        用户请求
                            ↓
            ┌───────────────────────────────────┐
            │  第 1 层:检查 User-Agent         │
            │  是否包含 "bot"?                  │
            └───────────────────────────────────┘
                  ↓                    ↓
                是 (测试4)            否 (测试1、2、3)
                  ↓                    ↓
             403 Forbidden    ┌─────────────────────┐
             (爬虫拦截)       │ 第 2 层:检查 IP    │
                              │ 是否 127.0.0.1?     │
                              └─────────────────────┘
                                    ↓          ↓
                                   是          否 (测试1)
                                    ↓          ↓
                              ┌─────────────────────┐
                              │ 第 3 层:检查密码    │
                              │ 用户名密码正确?     │
                              └─────────────────────┘
                                    ↓          ↓
                                   是          否 (测试3)
                                    ↓          ↓
                             ┌─────────────────────┐
                             │ 成功返回 index.html │
                             │ (测试2 ✅)          │
                             └─────────────────────┘

    日志相关

    访问日志

    记录客户端请求信息(IP、请求方法、状态码、响应大小等)

    相关配置指令

    Syntax:    access_log path [format [buffer=size] [gzip[=level]] [flush=time] [if=condition]];
               access_log off;
    Default:   access_log logs/access.log combined;
    Context:   http, server, location, if in location, limit_except
    
    
    Syntax:    log_format name [escape=default|json|none] string ...;
    Default:   log_format combined "...";
    Context:   http

    官方案例

    # 定义日志格式
    log_format compression '$remote_addr - $remote_user [$time_local] '
                           '"$request" $status $bytes_sent '
                           '"$http_referer" "$http_user_agent" "$gzip_ratio"';
    # 定义访问日志
    access_log /spool/logs/nginx-access.log compression buffer=32k;

    错误日志

    记录服务器错误、警告及调试信息

    相关配置指令

    Syntax:    error_log file [level];
    Default:   error_log logs/error.log error;
    Context:   main, http, mail, stream, server, location
    
    # 日志级别:debug info notice warn error crit alert emerg

    案例分析:参考主配置文件中日志相关配置

    常用变量(不用记,用到了翻一下就行)

    内置变量

    常用内置变量

    $remote_addr;
    # 存放了客户端的地址,注意是客户端的公网IP
    $proxy_add_x_forwarded_for;
    # 此变量表示将客户端IP追加请求报文中X-Forwarded-For首部字段,多个IP之间用逗号分隔,如果请求中没有X-Forwarder-For,就使用$remote_addr
    $args;
    # 变量中存放了URL中的参数
    $document_root;
    # 保存了针对当前资源的系统根目录
    $document_uri;
    # 保存了当前请求中不包含参数的URI,注意是不包含请求的指令,比如/img/logo.png
    $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等等
    $request_filename;
    # 当前请求的资源文件的磁盘路径,由root或alias指令与URL请求生成的文件绝对路径
    # /apps/nginx/html/www/index.html
    $request_uri;
    # 包含请求参数的原始URI,不包含主机名,相当于:$document_uri?$args
    $scheme;
    # 请求的协议,例如:http,https,ftp等等
    $server_protocol;
    # 保存了客户端请求资源使用的协议版本,例如:HTTP/1.0,HTTP/1.1,HTTP/2.0等等
    $server_addr;
    # 保存了服务器的IP地址
    $server_name;
    # 请求的服务器的主机名
    $server_port;
    # 请求的服务器的端口号
    $http_<name>
    # name为任意请求报文首部字段,表示记录请求报文的首部字段
    $http_user_agent;
    # 客户端浏览器的详细信息
    $http_cookie;
    # 客户端的cookie信息
    $cookie_<name>
    # name为任意请求报文首部字段cookie的key名

    自定义变量

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

    语法格式:

    Syntax:set $varible value;
    Default: -
    Context: server, location, if

    范例:

    [root@localhost ~]# vim /apps/nginx/conf.d/www.conf
    set $name zs;
    echo $name;
    set $my_port $server_port;
    echo $my_port;
    echo "$server_name:$server_port";

    一般情况下,日志保持默认即可

    项目 默认配置 说明
    访问日志 access_log logs/access.log combined; 记录所有请求,格式为标准 combined
    错误日志 error_log logs/error.log error; 记录 error 及以上级别

    大多数场景下,默认配置已经足够使用。

    网页压缩技术

    网页压缩技术主要通过 ngx_http_gzip_module 模块实现,它能显著减少传输数据量,提升网站加载速度和用户体验

    # 启用或禁用 gzip 压缩,默认关闭
    gzip on | off;
    # 压缩比由低到高1到9,默认为1
    gzip_comp_level level;
    # 禁用 IE6 gzip 功能
    gzip_disable "MSIE [1-6].";
    # gzip 压缩的最小文件,小于设置值的文件将不会压缩
    gzip_min_length 1k;
    # 启用压缩功能时,协议的最小版本,默认 HTTP/1.1
    gzip_http_version 1.0 | 1.1;
    # 指定 Nginx 服务需要向服务器申请的缓存空间的个数和大小,平台不同,默认:32 4k或者 16 8k
    gzip_buffers number size;
    # 指明仅对哪些类型的资源执行压缩操作,默认为 gzip_types text/html,不用显示指定,否则出错
    gzip_types mime-type ...;
    # 如果启用压缩,是否在相应报文首部插入 "vary: Accept-Encoding",一般建议打开
    gzip_vary on | off

    案例分析:参考 Apache 对应案例的测试验证思路,其实压缩正常用不到,用到了再说,一样的思路,不会改变网站服务内容


    Nginx 第三方模块

    第三方模块是对Nginx的功能扩展,第三方模块需要在编译安装Nginx的时候使用参数–add-module=PATH指定路径添加,有的模块是由公司的开发人员针对业务需求定制开发的,有的模块是开源爱好者开发好之后上传到GitHub进行开源的模块,Nginx支持第三方模块需要从源码重新编译支持.

    开源 echo模块:https://github.com/openresty/echo-nginx-module

    [root@localhost nginx-1.22.0]# vim /apps/nginx/conf.d/echo.conf
    server {
        listen 80;
        server_name echo.test.com;
    
        location /main {
            index index.html;
            default_type text/html;
            echo "hello world,main-->";
            echo $remote_addr;
            echo_reset_timer;
            # 将计时器开始时间重置为当前时间
    
            echo_location /sub1;
            echo_location /sub2;
            echo "took $echo_timer_elapsed sec for total.";
    }
        location /sub1 {
            echo_sleep 2;
            echo hello;
    }
        location /sub2 {
            echo_sleep 1;
            echo world;
    }
    }
    
    
    # 重新编译nginx,以前的配置文件不会丢失
    [root@localhost ~]# cd /usr/local/src
    [root@localhost src]# wget https://github.com/openresty/echo-nginx-module/archive/refs/heads/master.zip
    [root@localhost src]# unzip echo-nginx-module-master.zip
    [root@localhost src]# mv echo-nginx-module-master echo-nginx-module
    [root@localhost src]# cd nginx-1.22.0
    [root@localhost nginx-1.22.0]# ./configure --prefix=/apps/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 
    --add-module=/usr/local/src/echo-nginx-module
    [root@localhost nginx-1.22.0]# make -j 2 && make install
    
    
    # 进行访问测试
    [root@localhost ~]# echo "192.168.226.128 echo.test.com" >> /etc/hosts
    [root@localhost ~]# curl echo.test.com/main
    hello world,main-->
    192.168.226.128
    hello
    world
    took 3.008 sec for total.

    实验理解

    一、什么是 Nginx 第三方模块

    Nginx 本身是一个核心程序,只提供基础的 Web 服务器功能。第三方模块是对 Nginx 的功能扩展,可以增加额外特性。

    特点 说明
    来源 公司内部开发(业务定制)或开源社区(GitHub)
    安装方式 需要在编译安装 Nginx 时通过 --add-module=PATH 指定模块源码路径
    与业务的关系 完全没关系,只是为了增强 Nginx 的功能(如调试、测试、特殊响应处理)

    二、Echo 模块简介

    Echo 模块(echo-nginx-module)是由 OpenResty 作者章亦春(agentzh)开发的开源模块。

    核心功能:在 Nginx 配置文件中,像写 Shell 脚本一样输出内容、控制流程、进行子请求等。

    常用指令速查

    指令 作用
    echo 输出字符串(类似 Shell 的 echo)
    echo_sleep 非阻塞睡眠(秒)
    echo_location 串行执行子请求(上一个完成后再执行下一个)
    echo_location_async 并行执行子请求(同时发起多个)
    echo_reset_timer 重置计时器起点
    $echo_timer_elapsed 从计时起点到现在的耗时(秒)

    三、配置文件详解

    nginx

    server {
        listen 80;
        server_name echo.test.com;
    
        location /main {
            index index.html;
            default_type text/html;
            echo "hello world,main-->";
            echo $remote_addr;
            echo_reset_timer;
    
            echo_location /sub1;
            echo_location /sub2;
    
            echo "took $echo_timer_elapsed sec for total.";
        }
    
        location /sub1 {
            echo_sleep 2;
            echo hello;
        }
    
        location /sub2 {
            echo_sleep 1;
            echo world;
        }
    }

    逐段拆解

    代码 含义
    listen 80; 监听 80 端口
    server_name echo.test.com; 域名为 echo.test.com

    /main 路径执行顺序

    nginx

    echo "hello world,main-->";        # 1. 输出第一行
    echo $remote_addr;                 # 2. 输出客户端 IP
    echo_reset_timer;                  # 3. 重置计时器(从现在开始计时)
    echo_location /sub1;               # 4. 串行执行 /sub1(等它完成后继续)
    echo_location /sub2;               # 5. 串行执行 /sub2(等它完成后继续)
    echo "took $echo_timer_elapsed sec for total.";  # 6. 输出总耗时

    关键:echo_location 是串行的,必须等 /sub1 完成后才执行 /sub2。

    /sub1 路径

    nginx

    echo_sleep 2;    # 非阻塞睡眠 2 秒
    echo hello;      # 输出 "hello"

    /sub2 路径

    nginx

    echo_sleep 1;    # 非阻塞睡眠 1 秒
    echo world;      # 输出 "world"

    四、执行时间线

    text

    时间 0s:  /main 开始
             → 输出 "hello world,main-->"
             → 输出 "192.168.226.128"
             → echo_reset_timer (计时器归零)
             → 开始执行 /sub1
    
    时间 0-2s: /sub1 睡眠 2 秒
             2s 时: 输出 "hello",/sub1 完成
    
    时间 2-3s: 开始执行 /sub2,睡眠 1 秒
             3s 时: 输出 "world",/sub2 完成
    
    时间 3s: 回到 /main,输出 "took 3.008 sec for total."

    总耗时:2 秒(sub1)+ 1 秒(sub2)= 约 3 秒

    Nginx 添加第三方模块功能,必须重新编译。


    为什么必须重新编译?

    Nginx 官方提供的二进制包(如 yum install nginx)是一个固定功能的版本,只包含官方内置模块。第三方模块(如 Echo 模块)是额外的 C 语言代码,必须和 Nginx 核心代码一起编译成可执行文件才能使用。

    流程本质:

    text

    Nginx源码 + 第三方模块源码 → 编译 → 新的nginx二进制文件

    编译时通过 --add-module=/path/to/module 把第三方模块”绑”进 Nginx 里。


    编译 vs 不编译的对比

    方式 优点 缺点
    yum/apt 安装 方便快捷,自动管理依赖 功能固定,无法添加第三方模块
    源码编译 完全可控,可添加任意模块 需要手动编译,升级麻

    其他配置项

    Alias

    alias: 定义路径别名,会把访问的路径重新定义到其指定的路径,文档映射的另一种机制,仅能用于location上下文,此指令使用较少

    案例:

    [root@localhost conf]# vim /apps/nginx/conf.d/test.conf
    server {
      listen 80;
      server_name a.test.com;
    
      location /about/ {
      # 使用alias的时候uri后面加了斜杠,下面的路径也必须加,不然403错误
        alias /apps/nginx/html/about/;
        # 当访问about的时候,会显示alias定义的/apps/nginx/html/about/里面的内容
      }
    }
    
    # 重启Nginx并访问测试
    [root@localhost ~]# systemctl reload nginx
    [root@localhost ~]# curl a.test.com/about/
    about
    即使不写 root,Nginx 默认 root 是编译时的 html 目录(通常是 /usr/local/nginx/html 或 /etc/nginx/html)

    用公式表达

    text

    【没有 alias 时】
    请求:/about/index.html
         ↓
    实际路径 = root + /about/index.html
         ↓
    例如:/var/www + /about/index.html = /var/www/about/index.html
    
    
    【有 alias 时】
    请求:/about/index.html
         ↓
    实际路径 = alias(直接把 /about/ 替换掉)
         ↓
    例如:/apps/nginx/html/about/index.html

    text

    【发起请求】
    a.test.com/about/index.html
             ↓
    【匹配 location】
    location /about/ {   ← 匹配成功!
        alias /apps/nginx/html/about/;
    }
             ↓
    【处理逻辑】
    因为匹配到了 /about/,所以 URL 中 /about/ 这个部分被 alias 定义的路径替换
             ↓
    【最终读取】
    /apps/nginx/html/about/index.html

    请求 /about/ 的时候,因为这个 location 被匹配到了,所以不用管外面的 root 是什么,直接用 alias 里写的路径去替换 URL 中的 /about/ 部分,然后去那个路径找文件。


    自定义错误页面

    定义错误页,以指定的响应状态码进行响应,可用位置: HTTP, server, location, if in location

    范例:

    [root@localhost static3]# vim /apps/nginx/conf.d/test.conf
    server {
        listen 80;
        server_name a.test.com;
        auth_basic "login password";
        auth_basic_user_file /apps/nginx/conf/.htpasswd;
        error_page 404 /40x.html;
        location = /1.jpg {
            index index.html;
            root /apps/nginx/static1;
        }
        location /40x.html{
            root /apps/nginx/html;
        }
    }
    [root@localhost html]# echo "<h1>404 not found</h1>" > /apps/nginx/html/40x.html       # 自定义错误界面内容 

    范例:

    error_page 404 /index.html;
    # 如果404,就跳转到主页

    核心原理

    error_page 的作用是:当 Nginx 遇到指定的 HTTP 错误码时,返回另一个 URI(内部重定向),而不是直接返回错误页面。


    你的配置拆解

    nginx

    server {
        listen 80;
        server_name a.test.com;
    
        error_page 404 /40x.html;          # ① 404 时跳转到 /40x.html
    
        location = /1.jpg {
            index index.html;
            root /apps/nginx/static1;      # ② 图片在这
        }
    
        location /40x.html {
            root /apps/nginx/html;         # ③ 错误页在这
        }
    }

    完整工作流程

    步骤 发生什么
    ① 用户访问 a.test.com/不存在的文件.jpg
    ② Nginx 在所有 location 中找这个文件,没找到
    ③ Nginx 触发 404 错误
    ④ 看到 error_page 404 /40x.html; → 内部重定向到 /40x.html
    ⑤ 请求 /40x.html 匹配到 location /40x.html
    ⑥ 使用 root /apps/nginx/html; → 查找 /apps/nginx/html/40x.html
    ⑦ 返回该文件内容(你写的 <h1>404 not found</h1>)给用户

    关键:内部重定向 vs 外部跳转

    text

    【error_page】
    用户请求 → 404错误 → Nginx内部重新请求 /40x.html → 返回内容
    URL 不变!用户看不到跳转
            ↓
    【外部跳转(rewrite)】
    用户请求 → Nginx返回 302 → 浏览器跳转到新URL → URL改变

    长连接配置

    [root@localhost html]# vim /apps/nginx/conf/nginx.conf
    
    keepalive_timeout timeout [header_timeout];
    # 设定保持连接超时时长,0表示禁止长连接,默认为75s,通常配置在http字段作为站点全局配置
    keepalive_requests number;
    # 在一次长连接上所允许请求的资源的最大数量,默认为100次,建议适当调大,比如:500

    范例:

    [root@localhost html]# vim /apps/nginx/conf/nginx.conf
    http {
    ...
    keepalive_requests 3;
    keepalive_timeout 65 60;
    # 开启长连接后,返回客户端的会话保持时间为60s,单次长连接累计请求达到指定次数请求或65秒就会被断开,后面的60为发送给客户端应答报文头部中显示的超时时间设置为60s,如不设置客户端将不显示超时时间。
    keep-Alive:timeout=60;
    # 浏览器收到的服务器返回的报文
    # 如果设置为keepalive_timeout 0表示关闭会话保持功能,将如下显示:
    Connection:close  # 浏览器收到的服务器返回的报文
    # 使用命令测试
    [root@localhost html]# telnet a.test.com 80
    Trying 192.168.226.128...
    Connected to a.test.com.
    Escape character is '^]'.
    GET / HTTP/1.1
    HOST: a.test.com

    image-20260819192055206

    image-20260819192707706


    作为下载服务器

    ngx_http_autoindex_module模块处理以斜杠字符”/”结尾的请求,并生成目录列表可以作为下载服务配置使用

    官方文档:https://nginx.org/en/docs/http/ngx_http_autoindex_module.html

    相关指令:

    autoindex on|off;
    # 自动文件索引功能,默认off
    autoindex_exact_size on|off;
    # 计算文件确切大小(单位bytes),off显示大概大小(单位K、M),默认on
    autoindex_localtime on|off;
    # 显示本机时间而非GMT(格林威治)时间,默认off
    autoindex_format html|xml|json|jsonp;
    # 显示索引的页面分割,默认html
    limit_rate rate;
    # 限制响应客户端传输速率(除GET和HEAD以外的所有方法),单位B/s,既bytes/second,默认值0,表示无限制,此指令由ng

    范例:实现下载站点

    [root@localhost ~]# mkdir -p /apps/nginx/html/www/download
    [root@localhost ~]# cd /apps/nginx/html/www/download
    [root@localhost download]# touch f1
    [root@localhost download]# touch f2
    [root@localhost ~]# vim /etc/nginx/conf.d/www.conf  #默认的yum安装
    server {
        listen 80;
        server_name file.test.com;
        location /download {
            autoindex on;    
            # 自动索引功能,开启才会展示出文件列表
            autoindex_exact_size off;    
            # 关闭详细文件大小统计,让文件大小显示MB,GB单位,默认为b
            autoindex_localtime on;    
            # on表示显示本机时间
            limit_rate 1024k;      
            # 限速,默认不限速
            root /apps/nginx/html/www;
        }
    }
    
    
    # 修改windwos里面的hosts
    192.168.226.128 file.test.com
    测试http://file.test.com/download/ 

    image-20260821095005960

    windows的本地hosts使用

    你用管理员身份打开,然后修改后保存到桌面,把文件拖到这个路径,==替换原本的hosts==,这样就保存了

    image-20260821095228087

    image-20260821095322300


    Nginx 状态页

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

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

    [root@localhost ~]# vim /etc/nginx/conf.d/www.conf
    server {
        listen 80;
        server_name status.test.com;
    
        location /status {
            stub_status on;
            # 开启nginx状态页显示
            allow 192.168.226.0/24;
            deny all;
        }
    }
    
    [root@localhost ~]# vim /etc/hosts
    192.168.226.128 status.test.com
    [root@localhost download]# curl http://status.test.com/status
    Active connections: 1
    server accepts handled requests
     1 1 1
    Reading: 0 Writing: 1 Waiting: 0

    image-20260821110241918

    image-20260821110607828

    image-20260821110708911

    其他

    其他配置参数很多没必要记,遇到了再说,再学一下具体配置参数,不用慌,主要配置参数会就行


    HTTPS 功能

    Web网站的登录页面都是使用HTTPS加密传输的,加密数据以保障数据的安全,HTTPS能够加密信息,以免敏感信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议,HTTPS其实是有两部分组成:HTTP + SSL/ TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据。

    配置参数

    Nginx的HTTPS功能基于模块ngx_http_ssl_module实现,因此如果是编译安装的Nginx要使用参数ngx_http_ssl_module开启ssI功能,但是作为Nginx的核心功能,yum安装的Nginx默认就是开启的,编译安装的Nginx需要指定编译参数–with-http_ssl_module开启

    官方文档:https://nginx.org/en/docs/http/ngx_http_ssl_module.html

    配置参数如下:

    ssl on | off;
    listen 443 ssl;
    # 为指定的虚拟主机配置是否启用ssl功能,此功能在1.15.0废弃,使用listen [ssl]替代。
    ssl_certificate /path/to/file;
    # 当前虚拟主机使用使用的公钥文件,一般是crt文件
    ssl_certificate_key /path/to/file;
    # 当前虚拟主机使用的私钥文件,一般是key文件
    ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
    # 支持ssl协议版本,早期为ssl现在是TSL,默认为后三个
    ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
    # 配置ssl缓存
        off: 
        # 关闭缓存
        none: 
        # 通知客户端支持ssl session cache,但实际不支持
        builtin[:size]:
        # 使用OpenSSL内建缓存,为每worker进程私有
        [shared:name:size]:
        # 在各worker之间使用一个共享的缓存,需要定义一个缓存名称和缓存空间大小,一兆可以存储4000个会话信息,多个虚拟主机可以使用相同的缓存名称。
    ssl_session_timeout time;
    # 客户端连接可以复用ssl session cache中缓存的有效时长,默认5m

    自签名证书

    • 生成ca证书
    [root@localhost ~]# cd /etc/nginx
    [root@localhost nginx]# mkdir certs && cd certs
    [root@localhost certs]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout ca.key -x509 -days 3650 -out ca.crt
    • 生成证书请求文件
    [root@localhost certs]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout iproute.cn.key -out iproute.cn.csr
    • 签发证书
    [root@localhost certs]# openssl x509 -req -days 36500 -in iproute.cn.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out iproute.cn.crt
    [root@localhost certs]# cat iproute.cn.crt ca.crt > iproute.crt
    • 验证证书内容
    [root@localhost certs]# openssl x509 -in iproute.cn.crt -noout -text

    证书配置

    [root@localhost certs]# vim /etc/nginx/conf.d/ssl.conf
    server {
        listen 80;
        listen 443 ssl;
        ssl_certificate  /etc/nginx/certs/iproute.crt;
        ssl_certificate_key  /etc/nginx/certs/iproute.cn.key;
        ssl_session_cache  shared:sslcache:20m;
        ssl_session_timeout  10m;
        root /usr/share/nginx/html;              # yum 安装默认网站目录 /usr/share/nginx/html
    }

    实践

    image-20260821120521753


    Nginx 架构和进程

    Nginx 架构

    Nginx 采用了 master-worker 架构,由 master 进程负责通信和调度,worker 进程响应具体的请求Nginx 进程模型

    Nginx 的进程模型是其高性能和高并发的核心,采用多进程架构(默认模式),主要由 Master 进程和多个 Worker 进程组成,结合异步非阻塞 I/O 模型实现高效请求处理

    image-20260806155507239

    Master 进程:

    • 对外接口:接收外部的操作(信号)
    • 对内转发:根据外部的操作的不同,通过信号管理 worker
    • 监控:监控 worker 进程的运行状态,worker 进程异常终止后,自动重启 worker 进程
    • 配置加载:读取 Nginx 配置文件并验证其有效性和正确性
    • 管理连接:建立、绑定和关闭 socket 连接
    • 按照配置生成、管理和结束工作进程
    • 接受外界指令,比如重启、升级及退出服务器等指令
    • 不中断服务,实现平滑升级,重启服务并应用新的配置
    • 开启日志文件,获取文件描述符
    • 不中断服务,实现平滑升级,升级失败进行回滚处理

    Worker 进程:

    • 所有 Worker 进程都是平等的,接受处理客户的请求
    • 实际处理:网络请求,由 Worker 进程处理
    • Worker进程数量:在nginx.conf 中配置,一般设置为核心数,充分利用 CPU 资源,同时,避免进程数量过多,避免进程竞争 CPU 资源,增加上下文切换的损耗。
    • 将请求依次送入各个功能模块进行处理
    • I/O调用,获取响应数据
    • 与后端服务器通信,接收后端服务器的处理结果
    • 缓存数据,访问缓存索引,查询和调用缓存数据
    • 发送请求结果,响应客户的请求
    • 接收主程序指令,比如重启、升级和退出等

    工作细节

    image-Nginx进程模型工作细节01

    image-20260806155546773

    image-20260821124558347

    Nginx 进程间通信

    图示如下

    image-20260806155602509

    信号:Master 进程通过信号控制 Worker 进程的生命周期及配置更新,典型场景:SIGHUP(重新加载配置文件)、SIGTERM(优雅关闭 Worker 进程)、SIGUSR1(重新打开日志文件) 管道:Master 进程与 Worker 进程之间单向传递指令,Master 进程传递 Worker ID、指令、索引、文件描述符等等,Worker 进程监听管道事件并执行具体事项 共享内存:Worker 进程间共享数据,典型场景:限流计数、缓存状态等等

    text

                        ┌─────────────────┐
                        │   Master 进程   │  ← root 运行,管理者
                        │   PID: 12345    │
                        └────────┬────────┘
                                 │
                  ┌──────────────┼──────────────┐
                  │              │              │
                  ▼              ▼              ▼
         ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
         │ Worker 1    │ │ Worker 2    │ │ Worker 3    │  ← 普通用户运行
         │ PID: 12346  │ │ PID: 12347  │ │ PID: 12348  │    处理客户端请求
         └─────────────┘ └─────────────┘ └─────────────┘
                  │              │              │
                  └──────────────┼──────────────┘
                                 ▼
                        ┌─────────────────┐
                        │   客户端请求     │
                        │   (用户访问)     │
                        └─────────────────┘

    生产环境黄金原则

    1. 永远不要 kill Master 进程,除非你知道自己在做什么
    2. 用 nginx -s reload 而不是 kill 来重载配置
    3. 用 systemctl 管理服务,而不是手动 pkill
    4. 如果非要 kill Worker,用 kill(SIGTERM)而不是 kill -9,让 Worker 优雅退出
    5. 测试环境随便玩,生产环境要谨慎

    一句话总结

    Kill Worker → Master 自动复活,基本无感 Kill Master → 服务失控,不会自动恢复,需要人工干预 Kill 全部 → 服务完全中断,需手动重启


    多进程 vs 多线程:稳定性与效率的博弈

    1. 多线程(代表:Apache Worker 模式)

    核心逻辑:一个进程里开几百个线程,每个线程处理一个请求。

    优势(效率高):

    • 内存占用低:线程间共享内存空间,创建开销极小。几百个线程占用的内存,可能还不如几十个进程多。
    • 数据传递快:线程间通信极其简单(直接读写共享变量),不需要像进程那样走复杂的 IPC(进程间通信)。

    劣势(稳定性差):

    • 单点故障:只要其中一个线程出现内存越界或段错误导致崩溃,整个进程内的几百个线程全部被迫终止,服务直接断崖式宕机。
    • 锁竞争开销:因为共享内存,多线程操作共享变量时必须加锁(互斥锁/读写锁)。高并发下锁竞争严重时会频繁触发上下文切换,极大拖累整体吞吐量(这是多线程最大的性能瓶颈)。

    2. 多进程(代表:Nginx)

    核心逻辑:一个进程只处理一部分请求(Master 管调度,Worker 各干各的)。

    优势(稳定性优先):

    • 故障隔离:每个 Worker 进程拥有独立的内存地址空间。哪怕一个 Worker 因为 PHP 执行死循环或者段错误而崩溃,其他 Worker 进程完全不受影响,只有该 Worker 正在处理的少量请求会失败,Master 还会立即重新拉起一个新的 Worker 进程补上,服务整体可用性不受影响。

    劣势(资源消耗大):

    • 内存开销高:创建进程的开销比线程大得多,且每个进程都要加载独立的资源(如 Nginx 的配置、日志 buffer 等),内存占用较高。
    • 进程间通信(IPC)成本高:进程间交换数据得走 Socket 或共享内存,没有线程间直接读写共享变量那么高效。

    为什么 Nginx 敢用”更吃资源”的多进程?

    按道理说,多进程又重、通信又慢,为什么 Nginx 这么火?因为 Nginx 扬长避短了:

    1. 用资源换稳定性:现在的服务器内存动不动几十 GB,多开几个 Worker 进程(通常等于 CPU 核数)对内存来说根本不是事。用可控的内存开销,换来 99.999% 级别的服务可用性,这笔投入完全值得。
    2. 绕开锁竞争瓶颈:Nginx 让每个 Worker 进程独享自己的连接列表(epoll 队列),每个 Worker 独立监听和接收连接,基本上不需要进程间加锁。而多线程最头疼的”锁竞争”问题,在 Nginx 这里被优雅地规避了。
    3. CPU 缓存亲和性:多进程模式下,可以通过配置(worker_cpu_affinity)将每个 Worker 进程绑定到固定的 CPU 核心上,L1/L2 缓存命中率极高。多线程模式在多个核心间频繁切换上下文,反而容易导致缓存失效(Cache Miss),降低性能。

    最终结论

    在互联网高并发场景下,”稳定可用”的优先级远高于”极致省内存”。用户宁愿接受少量的性能损耗,也不能接受服务因为某个请求异常而彻底返回 502。

    Apache(多线程)追求的是压榨硬件性能的效率;而 Nginx(多进程)追求的是不因单个故障而全面崩溃的稳定性。


    性能优化

    CPU性能相关

    user    nginx nginx;    #启动Nginx工作进程的用户和组
    worker_processes [number | auto];   #启动Nginx工作进程的数量,一般设为和CPU核心数相同
    worker_cpu_affinity 0001 0010 0100 1000;  # 将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进程独占一核心CPU,但是可以保障此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,减少了cpu对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。
    CPU MASK:0001    0号CPU
              0010    1号CPU
              0100    2号CPU
              1000    3号CPU
    [root@localhost ~]# watch -n.5 'ps axo pid,cmd,psr,nice |grep nginx'

    image-20260821165919510


    工作优先级与文件并发数

    worker_priority 0;   #工作进程优先级(-20~19)
    worker_rlimit_nofile 65536;     #所有worker进程能打开的文件数量上线,包括:Nginx的所有连接(例如与代理服务器的连接等),而不仅仅是与客户端的连接,另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件数的限制,最好与ulimit -n的值保持一致
    [root@localhost ~]# vim /etc/security/limits.conf
    *        soft    nofile    102400
    *        hard    nofile    102400
    
    
    # 案例-修改优先级
    root@localhost nginx-1.22.0]# vim /etc/nginx/conf/nginx.conf
    worker_priority -20;
    
    #重载配置
    [root@localhost nginx-1.22.0]# nginx -t
    [root@localhost nginx-1.22.0]# systemctl restart nginx
    
    # 查看优先级
    [root@localhost ~]# watch -n.5 'ps axo pid,cmd,psr,nice |grep nginx'
    Every 0.5s: ps axo pid,cmd,psr,nice |grep nginx                   Mon Jul 15 08:11:44 2024
    
     22950 nginx: master process /apps   1   0
     22951 nginx: worker process         0 -20
     22952 nginx: worker process         1 -20
     22953 nginx: worker process         2 -20
     22954 nginx: worker process         3 -20
     23004 grep nginx                    3   0

    image-20260821171325341

    image-20260821171422465

    其他优化配置

    daemon off;    # 前台运行nginx服务,用于测试、docker等环境
    master_process off|on;    # 是否开启Nginx的master-worker工作模式,仅用于开发调试场景,默认为on
    events {
      worker_connections 65536;    # 设置单个工作进程的最大并发连接数
      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
    }

    默认主配置文件

    image-20260821182810499

    Nginx高并发性能调优

    Events 模块性能调优详解

    你截图里的 events 块配置了四个关键参数,都是 Nginx 高并发性能调优的核心。

    events 块完整配置

    nginx

    events {
        worker_connections 65536;   # 每个 Worker 最大并发连接数
        use epoll;                  # 事件驱动模型
        accept_mutex on;            # 接受互斥锁,防止惊群
        multi_accept on;            # 多接受,一次接受多个连接
    }

    1. worker_connections

    作用:设置每个 Worker 进程能够同时打开的最大连接数(含所有连接,不只客户端)。

    计算公式:

    text

    Nginx 最大并发连接数 = worker_processes × worker_connections

    举例:

    • worker_processes = auto(假设服务器 4 核 → 4 个 Worker)
    • worker_connections = 65536
    • 最大并发 = 4 × 65536 = 262,144 个连接

    设置建议:

    场景 推荐值
    低并发(个人博客) 1024 或 2048
    中并发(企业官网) 4096 或 8192
    高并发(互联网应用) 65536 或更大

    注意事项:

    • 该值不能超过系统 ulimit -n 的限制(单个进程最大文件描述符数)
    • 调整前先检查:ulimit -n
    • 如需调整系统限制:ulimit -n 65536 或修改 /etc/security/limits.conf

    2. use epoll

    作用:指定 Nginx 使用的事件驱动模型,用于高效管理大量并发连接。

    常见事件驱动模型对比:

    模型 适用场景 特点
    select 老旧系统、低并发 轮询检查,连接数受限(1024),效率低
    poll 老旧系统、低并发 类似 select,但无 1024 限制,效率一般
    epoll Linux 系统(推荐) 事件驱动,O(1) 复杂度,只处理活跃连接,高并发首选
    kqueue FreeBSD/macOS 系统 类似 epoll,用于 BSD 系统
    /dev/poll Solaris 系统 类似 epoll,用于 Solaris

    epoll 为什么高效?

    • select/poll:轮询所有连接,哪怕 100 万个连接只有 1 个活跃,也得全部遍历一遍 → O(n)
    • epoll:只通知活跃的连接,100 万个连接只有 1 个活跃 → 只处理这 1 个 → O(1)

    配置建议:

    nginx

    use epoll;   # Linux 系统直接写,Nginx 通常会自适应

    3. accept_mutex

    作用:控制多个 Worker 进程是否轮流接受新连接,防止”惊群”问题。

    什么是惊群?

    假设有 4 个 Worker 进程都在睡觉(阻塞等待新连接到来)

    新连接到达 → 内核唤醒所有 4 个 Worker → 4 个 Worker 同时抢这个新连接 → 只有 1 个抢到,其他 3 个被唤醒后啥也没干又回去睡觉

    • 惊群问题:每次唤醒所有 Worker,造成多次无意义的上下文切换,浪费 CPU 资源

    accept_mutex 的作用:

    设置 行为
    accept_mutex on(推荐) Worker 轮流接收新连接,同一时间只有一个 Worker 被唤醒,避免惊群
    accept_mutex off 所有 Worker 同时竞争新连接,可能触发惊群,高并发下性能下降

    为什么开启?

    text

    假设瞬间有 100 个新连接同时到达
    
    multi_accept off → Worker 每次只取 1 个 → 需要重复 100 次 accept() 系统调用
    multi_accept on  → Worker 每次取多个(如 10 个)→ 只需 10 次 accept() 系统调用
    • 减少系统调用次数 → 降低 CPU 开销 → 提升吞吐量

    配置建议:

    nginx

    multi_accept on;   # 建议开启,提升高并发下的接受效率

    补充优化:与 worker_priority 配合

    还有一个 worker_priority -20; 参数,这是设置 Worker 进程的 nice 值:

    nginx

    worker_priority -20;   # nice 值范围 -20(最高优先级)到 19(最低优先级)
    • -20 表示 Worker 进程获得更高的 CPU 调度优先级
    • 适合对响应时间要求极高的场景
    • 注意:设置过低(如 -20)可能影响系统其他进程,生产环境建议 -5 到 -10 即可

    LNMP架构

    LNMP 架构概述

    LNMP是一套技术的组合,L=Linux、N=Nginx、M=MySQL、P=PHP

    如何工作

    • 首先 Nginx 服务是不能处理动态请求,那么当用户发起动态请求时,Nginx 无法处理
    • 当用户发起 HTTP 请求,请求会被 Nginx 处理,如果是静态资源请求 Nginx 则直接返回,如果是动态请求 Nginx 则通过 fastcgi 协议转交给后端的 PHP 程序处理

    image-20260826122042523

    image-20260826122339397

    部署安装

    # 安装 nginx:如果编译安装可以跳过
    [root@localhost ~]# yum install nginx -y
    # 安装 php 全家桶
    [root@localhost ~]# yum install php* -y
    # 更新 php-fmp listen 配置项
    [root@localhost ~]# vim /etc/php-fpm.d/www.conf
    ......
    ;listen = /run/php-fpm/www.sock
    listen = 9000
    ......
    [root@localhost ~]# systemctl enable --now php-fpm
    # 安装 marinedb 数据库
    [root@localhost ~]# yum install -y mariadb-server mariadb
    [root@localhost ~]# systemctl enable --now mariadb
    # 设置数据库 root 用户密码
    [root@localhost ~]# mysqladmin password '123456'
    [root@localhost ~]# mysql -uroot -p123456 -e "show databases;"
    +--------------------+
    | Database           |
    +--------------------+
    | information_schema |
    | mysql              |
    | performance_schema |
    +--------------------+
    # 准备测试页面
    [root@localhost ~]# mkdir -pv /data/nginx/lnmp
    [root@localhost ~]# vim /data/nginx/lnmp/index.php
    <?php
        phpinfo();
    ?>
    
    # 测试 index.php 运行正确
    [root@localhost ~]# php /data/nginx/lnmp/index.php 
    # 添加 nginx vhost 配置文件:将 php 请求转给 php-fpm 处理
    [root@localhost ~]# vim /etc/nginx/conf.d/php.conf
    server {
        listen 80;
        server_name php.iproot.cn;
        root /data/nginx/lnmp;
    
        location / {
            index index.php index.html;
            }
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
            }
    }
    
    [root@localhost ~]# nginx -s reload
    # 测试验证
    [root@localhost ~]# curl -I -H 'Hostname: php.iproute.cn' 172.16.175.129
    HTTP/1.1 200 OK
    Server: nginx/1.20.1

    相关配置

    设置 fastcgi 服务器地址

    Syntax: fastcgi_pass address;
    Default:-
    Context:location,if in location
    # 语法示例:可以指定为域名或IP地址以及端口
    fastcgi_pass location:9000;
    fastcgi_pass unix:/tmp/fastcgi.socket;

    设置 fastcgi 默认首页文件

    Syntax: fastcgi_index name;
    Default:-
    Context:http,server,location

    设置 fastcgi_param 传递变量

    Syntax: fastcgi_param parameter value [if_not_empty];
    Default:-
    Context:http,server,location
    #语法示例
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME /code$fastcgi_script_name;

    实践

    image-20260826124723887

    image-20260826124751633

    image-20260826125336860

    image-20260826133331417

    1核心内容:Nginx 站点配置文件

    屏幕中央显示的是Nginx的一个虚拟主机(Server Block)配置文件内容。这个文件通常位于/etc/nginx/conf.d/目录下。

    我们来逐行解读这段配置:

    配置行 含义解析
    server { 定义一个虚拟主机配置块的开始。
    listen 80; 监听本机的80端口(即标准的HTTP网页请求端口)。
    server_name php.iproot.cn; 设置域名。当用户访问 php.iproot.cn 这个域名时,Nginx就会使用这个配置块来处理请求。
    root /data/nginx/Lnmp; 设置网站的根目录。所有静态文件(如HTML、图片)会从这个路径下寻找。
    location / { 匹配根路径/的访问规则。
    index index.php index.html; 设置默认首页文件。当用户访问目录时,会按顺序查找index.php,如果不存在则找index.html。
    location ~ .php$ { 这是一个正则匹配,专门处理所有以.php结尾的URL请求。
    fastcgi_pass 127.0.0.1:9000; 核心:将PHP脚本的解析请求转发给本机(127.0.0.1)的9000端口。这通常意味着服务器上运行着PHP-FPM服务,用来执行PHP代码。
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 告诉PHP-FPM要执行哪个PHP文件。 – $document_root 就是前面定义的 /data/nginx/Lnmp – $fastcgi_script_name 是请求的PHP文件名(比如/index.php)
    include fastcgi_params; 引入一个包含其他FastCGI相关参数(如PATH_INFO等)的标准文件,便于简化配置。

    2. 关于PHP-FPM配置

    屏幕左侧的目录树显示了 /etc/php-fpm.d/ 和其下的 www.conf。

    • www.conf 是 PHP-FPM(PHP FastCGI 进程管理器)的主配置文件之一。
    • 它定义了这个PHP-FPM池(pool)如何运行,包括监听的端口或套接字、进程数量、运行用户等。
    • 这里提到的 127.0.0.1:9000,正是www.conf文件中通常设置的listen = 127.0.0.1:9000,正好与Nginx配置里的fastcgi_pass地址相对应。

    3. 这个架构的整体工作流程

    当你在浏览器访问 http://php.iproot.cn/index.php 时:

    1. 浏览器通过DNS解析到你的服务器IP(192.168.226.128)。
    2. Nginx接收到请求,根据 server_name 匹配到该配置块。
    3. 根据 location ~ .php$ 规则,Nginx判断这是PHP请求。
    4. Nginx通过 fastcgi_pass 将请求转交给本机的PHP-FPM(监听9000端口)。
    5. PHP-FPM执行 /data/nginx/Lnmp/index.php 文件。
    6. PHP执行后返回结果给Nginx,Nginx再返回给浏览器。

    为什么必须用 fastcgi_pass?

    因为 Nginx 本身不支持直接解析 PHP。

    Nginx 是一个 Web 服务器,它擅长:

    • 处理静态文件(HTML、CSS、JS、图片)
    • 反向代理
    • 负载均衡

    但它不能执行 PHP 代码。

    PHP 代码必须由 PHP-FPM(或 PHP-CGI)来执行,而 Nginx 和 PHP-FPM 之间通过 FastCGI 协议通信,fastcgi_pass 就是告诉 Nginx:”把所有 PHP 请求通过 FastCGI 协议发给这个地址”。


    对比 Apache 的差异(便于理解)

    Web 服务器 PHP 处理方式
    Apache 可以通过 mod_php 模块内置执行 PHP(不需要转发)
    Nginx 必须通过 fastcgi_pass 转发给 PHP-FPM(无法内置)

    访问

    1. curl访问

    image-20260826134145649

    1. 浏览器访问

    image-20260826134610666

    image-20260826135459300

    image-20260826135551073

    ==总结==:全部成功代表LNMP环境没问题


    数据库管理应用

    为了方便的使用数据库,我们可以安装数据库图形化管理工具 phpmyadmin

    安装部署

    # 为数据库管理工具创建虚拟主机
    [root@localhost ~]# vim /etc/nginx/conf.d/mysql.conf
    server {
        listen 80;
        server_name mysql.iproot.cn;
        root /code/phpmyadmin;
    
        location / {
            index index.php index.html;
        }
    
        location ~ .php$ {
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
    [root@localhost ~]# nginx -t
    # 下载phpmyadmin源码
    [root@localhost ~]# cd /code/
    [root@localhost code]# wget https://files.phpmyadmin.net/phpMyAdmin/5.1.1/phpMyAdmin-5.1.1-all-languages.zip
    # 解压软件包,并且重命名
    [root@localhost phpmyadmin]# unzip phpMyAdmin-5.1.1-all-languages.zip
    [root@localhost phpmyadmin]# mv phpMyAdmin-5.1.1-all-languages phpmyadmin
    # 添加session文件夹权限
    [root@localhost phpmyadmin]# chown -R 777 /var/lib/php/session

    image-20260826142356321

    1. 你登录 phpMyAdmin

    text

    输入账号密码 → MySQL 验证通过
        ↓
    PHP 创建 Session 文件:sess_abc123
    内容:
      user_id = 1
      username = admin
      is_logged_in = 1
      login_time = 2026-08-26 11:00:00
        ↓
    返回浏览器:Cookie 里存了 Session ID = abc123

    2.Session 的核心机制

    text

    【第一次访问】
    用户浏览器 ──→ Nginx ──→ PHP
                              ↓
                        创建 Session 文件
                        /var/lib/php/session/sess_随机字符串
                              ↓
                        返回 Set-Cookie: PHPSESSID=随机字符串
    用户浏览器 ←──────────────────┘
        ↓
    浏览器保存 Cookie
    
    【第二次访问】
    用户浏览器(带着 Cookie: PHPSESSID=随机字符串)──→ Nginx ──→ PHP
                                                                  ↓
                                                         找到对应的 Session 文件
                                                         读取里面的数据
                                                                  ↓
                                                         "哦,你是 admin"

    Session 的本质就是:服务端的一个文件(或数据库记录),用来存储特定用户的临时数据。

    image-20260826160633952

    image-20260826160927261

    image-20260826160936936

    image-20260826161039242


    博客系统

    部署虚拟主机

    # 为博客创建虚拟主机
    [root@localhost ~]# vim /etc/nginx/conf.d/typecho.conf
    server {
        listen 80;
        server_name blog.iproot.cn;
        root /code/typecho;
        index index.php index.html;
    
        location ~ .*.php(/.*)*$ {
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
            include fastcgi_params;
        }
    }
    
    # 检查nginx配置文件,并且重启
    [root@localhost ~]# nginx -t
    [root@localhost ~]# systemctl restart nginx
    
    # 创建typecho目录
    [root@localhost ~]# mkdir /code/typecho
    [root@localhost ~]# cd /code/typecho
    
    [root@localhost ~]# wget https://github.com/typecho/typecho/releases/latest/download/typecho.zip
    
    # 解压源码
    [root@localhost ~]# unzip typecho.zip

    image-20260826161503298

    image-20260826162039442

    创建数据库

    点击数据库,输入数据库名之后,就可以点击创建

    image-20260826162943501

    安装博客系统

    下面就可以开始进入网站安装的部分了,访问博客系统页面

    image-20260826162539262

    赋予网站根目录下usr/uploads目录权限

    [root@localhost typecho]# chmod a+w usr/uploads/

    image-20260826163028750

    在安装的pwd下 touch config.inc.php文件

    image-20260826163255979

    image-20260826163501610

    image-20260826163607130

    网盘服务

    部署虚拟主机

    # 为网盘创建虚拟主机
    [root@localhost themes]# vim /etc/nginx/conf.d/kod.conf
    server {
            listen 80;
            server_name kod.iproot.cn;
            root /code/kod;
            index index.php index.html;
    
            location ~ .*.php(/.*)*$ {
                    root /code/kod;
                    fastcgi_pass   127.0.0.1:9000;
                    fastcgi_index  index.php;
                    fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
                    include fastcgi_params;
            }
    }
    
    # 检查nginx配置文件,并且重启
    [root@localhost ~]# nginx -t
    [root@localhost ~]# systemctl restart nginx
    
    # 下载源代码然后解压重命名
    [root@localhost ~]# mkdir /code/kod
    [root@localhost ~]# cd /code/kod
    [root@localhost kod]# wget https://static.kodcloud.com/update/download/kodbox.1.23.zip
    
    # 解压源码
    [root@localhost kod]# unzip kodbox.1.23.zip

    image-20260826164413962

    创建数据库

    image-20260826164856680

    安装网盘系统

    浏览器访问此站点,我们发现目录权限,这个比较重要

    image-20260826165309423

    # 设置权限
    [root@localhost kod]# chown -R 777 /code/kod

    image-20260826165952286

    image-20260826170157993

    可道云网盘系统

    image-20260826170527027