找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2515

积分

0

好友

357

主题
发表于 昨天 01:08 | 查看: 0| 回复: 0

概述

1.1 背景介绍

在多年的网站运维经历中,不合理的架构设计导致的性能问题屡见不鲜。例如,一个加载时间动辄数秒的电商网站,用户流失率会居高不下。此时,仅优化代码往往收效甚微,而对架构进行“动静分离”改造,则可能成为解决问题的关键。

所谓动静分离,其核心思想是将网站的静态资源(如CSS、JavaScript、图片、字体文件)与动态内容(由PHP、Python、Java等后端语言生成的页面或API响应)区分开来处理。静态资源直接由高效的Web服务器(如Nginx)或CDN快速响应,动态请求才被转发至后端应用服务器。这种架构带来的益处是多方面的:

  • Nginx处理静态文件的性能远超传统应用服务器。
  • 后端服务器得以从繁重的静态请求中解脱,负载显著降低。
  • 静态资源可以充分利用浏览器本地缓存和CDN的边缘缓存。
  • 动态服务可以独立进行横向扩展,架构更灵活。

1.2 技术特点

动静分离架构具备以下几个核心技术特点:
高效的资源分发
Nginx利用sentfile系统调用,能够在内核态直接完成文件数据传输,避免了用户态与内核态之间的数据拷贝开销,处理静态文件时单机QPS可达十万级别。
灵活的路由规则
通过location指令,可以精确匹配不同类型的请求(如使用正则表达式、前缀匹配等),并为其指定不同的处理逻辑。
多级缓存体系
结合浏览器缓存、CDN缓存以及Nginx自身的缓存,配合ETagLast-Modified等HTTP缓存协商机制,可以构建一个智能、高效的缓存链路。
零停机部署
通过为静态资源添加版本号或哈希值的方式命名,可以实现资源更新时的无缝切换,用户访问不受影响。

1.3 适用场景

动静分离架构尤其适用于以下几类场景:
内容型网站
新闻门户、博客、文档站点等静态内容占比高的网站,优化效果最为显著。
电商平台
海量的商品图片、前端样式和脚本资源,分离后能极大减轻应用服务器的压力。
移动应用后端
可以将App的API接口请求与静态资源(如启动图、热更新包)分流到不同的处理链路。
高并发场景
在秒杀、大促等流量高峰期间,动静分离能有效避免静态资源请求冲垮后端核心服务。

1.4 环境要求

组件 版本要求 说明
操作系统 Rocky Linux 9 / Ubuntu 24.04 LTS 推荐使用Rocky Linux 9作为生产环境
Nginx 1.26.x / 1.27.x 建议使用mainline分支获取最新特性
后端服务 PHP-FPM 8.3 / Tomcat 10 / Gunicorn 根据实际技术栈选择
存储 NFS / MinIO / 阿里云OSS 静态资源集中存储方案
内存 最低4GB Nginx缓存需要足够内存
磁盘 SSD推荐 静态资源I/O密集,SSD效果明显

详细步骤

2.1 准备工作

安装Nginx
在Rocky Linux 9环境下安装:

# 添加Nginx官方仓库
cat > /etc/yum.repos.d/nginx.repo << 'EOF'
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true

[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
EOF

# 启用mainline分支(可选,获取最新版本)
dnf config-manager --enable nginx-mainline

# 安装Nginx
dnf install nginx -y

# 查看版本
nginx -v
# nginx version: nginx/1.27.3

在Ubuntu 24.04环境下安装:

# 安装依赖
apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring -y

# 导入官方GPG key
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null

# 添加仓库
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
    | tee /etc/apt/sources.list.d/nginx.list

# 设置仓库优先级
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | tee /etc/apt/preferences.d/99nginx

# 安装
apt update && apt install nginx -y

创建目录结构

# 静态资源目录
mkdir -p /data/static/{css,js,images,fonts,uploads}
mkdir -p /data/www/html

# 日志目录
mkdir -p /var/log/nginx/{static,dynamic}

# 缓存目录
mkdir -p /var/cache/nginx/{static,proxy}

# 设置权限
chown -R nginx:nginx /data/static /data/www
chown -R nginx:nginx /var/cache/nginx
chmod -R 755 /data/static /data/www

规划URL结构
在配置前,需要清晰规划URL的匹配规则。以下是一个常见方案:

静态资源URL:
- /static/*          -> 所有静态资源
- /assets/*          -> 前端构建产物
- /uploads/*         -> 用户上传文件
- *.css, *.js        -> 样式和脚本
- *.jpg, *.png, *.gif, *.webp -> 图片
- *.woff, *.woff2, *.ttf      -> 字体

动态请求URL:
- /api/*             -> API接口
- /admin/*           -> 管理后台
- /*.php             -> PHP脚本
- 其他未匹配的请求   -> 转发后端

2.2 核心配置

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

# Nginx主配置文件
# 针对动静分离场景优化

user nginx;
# worker进程数,设置为CPU核心数
worker_processes auto;
# 绑定CPU亲和性,减少上下文切换
worker_cpu_affinity auto;

error_log /var/log/nginx/error.log warn;
pid /run/nginx.pid;

# 工作进程可打开的最大文件描述符数
worker_rlimit_nofile 65535;

events {
    # 单个worker的最大连接数
    worker_connections 65535;
    # 使用epoll事件模型(Linux)
    use epoll;
    # 允许一次接受多个连接
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # 自定义日志格式,区分静态和动态请求
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for" '
                    'rt=$request_time uct="$upstream_connect_time" '
                    'uht="$upstream_header_time" urt="$upstream_response_time"';

    log_format static '$remote_addr [$time_local] "$request" '
                      '$status $body_bytes_sent '
                      'rt=$request_time';

    access_log /var/log/nginx/access.log main;

    # 开启高效文件传输
    sendfile on;
    # 配合sendfile使用,减少网络包数量
    tcp_nopush on;
    # 禁用Nagle算法,减少延迟
    tcp_nodelay on;

    # 连接超时设置
    keepalive_timeout 65;
    keepalive_requests 1000;

    # 客户端请求限制
    client_max_body_size 100m;
    client_body_buffer_size 128k;
    client_header_buffer_size 4k;
    large_client_header_buffers 4 32k;

    # 隐藏版本号
    server_tokens off;

    # Gzip压缩(静态资源)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 5;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript
               application/json application/javascript application/xml
               application/xml+rss application/x-javascript
               image/svg+xml font/woff font/woff2;

    # 静态文件缓存
    open_file_cache max=10000 inactive=60s;
    open_file_cache_valid 30s;
    open_file_cache_min_uses 2;
    open_file_cache_errors on;

    # 代理缓存配置
    proxy_cache_path /var/cache/nginx/proxy
                     levels=1:2
                     keys_zone=proxy_cache:100m
                     max_size=10g
                     inactive=7d
                     use_temp_path=off;

    # upstream后端服务器组
    upstream backend_servers {
        # 使用IP哈希保持会话
        # ip_hash;

        # 后端服务器列表
        server 127.0.0.1:8080 weight=5 max_fails=3 fail_timeout=30s;
        server 127.0.0.1:8081 weight=5 max_fails=3 fail_timeout=30s;

        # 备用服务器
        server 127.0.0.1:8082 backup;

        # 长连接配置
        keepalive 32;
        keepalive_requests 1000;
        keepalive_timeout 60s;
    }

    # 引入站点配置
    include /etc/nginx/conf.d/*.conf;
}

站点配置文件 /etc/nginx/conf.d/example.conf

# 动静分离站点配置
# 站点:example.com

server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    # 强制HTTPS(生产环境启用)
    # return 301 https://$server_name$request_uri;

    root /data/www/html;
    index index.html index.htm;

    # 访问日志
    access_log /var/log/nginx/static/access.log static;
    error_log /var/log/nginx/static/error.log warn;

    # ==================== 静态资源处理 ====================

    # 静态资源目录 - 最常用的匹配方式
    location /static/ {
        alias /data/static/;

        # 开启高效传输
        sendfile on;
        tcp_nopush on;

        # 缓存控制:静态资源缓存1年
        expires 1y;
        add_header Cache-Control "public, immutable";

        # 允许跨域访问(字体文件需要)
        add_header Access-Control-Allow-Origin *;

        # 关闭访问日志减少I/O
        access_log off;

        # 找不到文件时返回404,不要转发到后端
        try_files $uri =404;
    }

    # 前端构建产物(带hash的文件名)
    location /assets/ {
        alias /data/www/html/assets/;

        # 带hash的文件可以永久缓存
        expires max;
        add_header Cache-Control "public, immutable";

        access_log off;
        try_files $uri =404;
    }

    # 用户上传文件
    location /uploads/ {
        alias /data/static/uploads/;

        # 上传文件缓存时间短一些,方便更新
        expires 7d;
        add_header Cache-Control "public";

        # 禁止执行脚本,安全考虑
        location ~ \.(php|jsp|py|pl|sh)$ {
            deny all;
        }

        try_files $uri =404;
    }

    # 图片文件
    location ~* \.(jpg|jpeg|gif|png|ico|webp|bmp|svg)$ {
        root /data/static/images;

        expires 30d;
        add_header Cache-Control "public";

        # 开启图片防盗链
        valid_referers none blocked server_names *.example.com;
        if ($invalid_referer) {
            return 403;
        }

        # 图片不存在时返回默认图片
        try_files $uri /images/default.png =404;

        access_log off;
    }

    # CSS/JS文件
    location ~* \.(css|js)$ {
        root /data/static;

        # CSS/JS缓存1个月
        expires 30d;
        add_header Cache-Control "public";

        # 字符集设置
        charset utf-8;

        access_log off;
        try_files $uri =404;
    }

    # 字体文件
    location ~* \.(woff|woff2|ttf|otf|eot)$ {
        root /data/static/fonts;

        expires 1y;
        add_header Cache-Control "public, immutable";

        # 字体需要CORS
        add_header Access-Control-Allow-Origin *;

        access_log off;
        try_files $uri =404;
    }

    # ==================== 动态请求处理 ====================

    # API接口转发
    location /api/ {
        # 记录详细日志
        access_log /var/log/nginx/dynamic/api.log main;

        # 反向代理到后端
        proxy_pass http://backend_servers;

        # 代理头设置
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # HTTP/1.1支持长连接
        proxy_http_version 1.1;
        proxy_set_header Connection "";

        # 超时设置
        proxy_connect_timeout 30s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;

        # 缓冲设置
        proxy_buffering on;
        proxy_buffer_size 4k;
        proxy_buffers 8 32k;
        proxy_busy_buffers_size 64k;

        # API响应一般不缓存
        add_header Cache-Control "no-store, no-cache, must-revalidate";
    }

    # 管理后台
    location /admin/ {
        access_log /var/log/nginx/dynamic/admin.log main;

        # 限制访问IP
        allow 10.0.0.0/8;
        allow 192.168.0.0/16;
        deny all;

        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }

    # PHP处理(如果需要)
    location ~ \.php$ {
        access_log /var/log/nginx/dynamic/php.log main;

        root /data/www/html;

        # 安全检查:确保文件存在
        try_files $uri =404;

        # FastCGI配置
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;

        # FastCGI缓冲
        fastcgi_buffer_size 32k;
        fastcgi_buffers 8 32k;
        fastcgi_busy_buffers_size 64k;

        # 超时
        fastcgi_connect_timeout 30s;
        fastcgi_send_timeout 60s;
        fastcgi_read_timeout 60s;
    }

    # 默认动态请求处理
    location / {
        # 先尝试静态文件,找不到再转发后端
        try_files $uri $uri/ @backend;
    }

    # 后端处理
    location @backend {
        access_log /var/log/nginx/dynamic/backend.log main;

        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_http_version 1.1;
        proxy_set_header Connection "";

        proxy_connect_timeout 30s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # ==================== 安全配置 ====================

    # 禁止访问隐藏文件
    location ~ /\. {
        deny all;
        access_log off;
        log_not_found off;
    }

    # 禁止访问备份文件
    location ~* \.(bak|swp|old|orig|save)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # robots.txt
    location = /robots.txt {
        allow all;
        access_log off;
        log_not_found off;
    }

    # favicon.ico
    location = /favicon.ico {
        access_log off;
        log_not_found off;
        expires 30d;
    }
}

2.3 启动和验证

配置检查

# 检查配置语法
nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

# 查看完整配置(调试用)
nginx -T

# 查看编译参数
nginx -V

启动服务

# 启动Nginx
systemctl start nginx

# 设置开机自启
systemctl enable nginx

# 查看状态
systemctl status nginx

# 查看进程
ps aux | grep nginx

验证动静分离效果

# 创建测试静态文件
echo "body { color: #333; }" > /data/static/css/test.css
echo "console.log('test');" > /data/static/js/test.js
echo '<html><body>Dynamic Content</body></html>' > /data/www/html/index.html

# 测试静态CSS访问
curl -I http://localhost/static/css/test.css
# HTTP/1.1 200 OK
# Cache-Control: public, immutable
# Expires: Thu, 01 Jan 2026 00:00:00 GMT

# 测试静态JS访问
curl -I http://localhost/static/js/test.js

# 测试动态页面
curl -I http://localhost/

# 查看响应头中的缓存设置差异

性能测试

# 安装ab测试工具
dnf install httpd-tools -y  # Rocky Linux
apt install apache2-utils -y  # Ubuntu

# 测试静态文件性能
ab -n 10000 -c 100 http://localhost/static/css/test.css
# Requests per second: 85000+ (取决于硬件)

# 测试动态请求性能
ab -n 1000 -c 50 http://localhost/api/test

示例代码和配置

3.1 完整配置示例

多域名动静分离配置
在实际生产环境中,为静态资源配置独立域名是常见的优化手段,这能带来诸多好处:突破浏览器对同一域名的并发连接数限制、避免静态请求携带无用的Cookie信息、更便于CDN加速配置等。

# 静态资源域名配置 static.example.com
server {
    listen 80;
    server_name static.example.com;

    root /data/static;

    # 全局缓存设置
    expires 30d;
    add_header Cache-Control "public";

    # 关闭access_log提升性能
    access_log off;

    # 所有请求都是静态文件
    location / {
        try_files $uri =404;
    }

    # 带版本号的资源永久缓存
    location ~* \.(css|js)\?v= {
        expires max;
        add_header Cache-Control "public, immutable";
    }

    # 防盗链
    valid_referers none blocked server_names
                   *.example.com example.com;
    if ($invalid_referer) {
        return 403;
    }
}

# 主站配置 www.example.com
server {
    listen 80;
    server_name www.example.com example.com;

    root /data/www/html;
    index index.html;

    access_log /var/log/nginx/www.access.log main;

    # 静态资源重定向到静态域名
    location ~* \.(css|js|jpg|jpeg|png|gif|ico|woff|woff2)$ {
        return 301 http://static.example.com$request_uri;
    }

    # 其他请求走后端
    location / {
        try_files $uri $uri/ @backend;
    }

    location @backend {
        proxy_pass http://backend_servers;
        include proxy_params;
    }
}

代理参数复用文件 /etc/nginx/proxy_params

# 通用代理参数配置
# 避免在每个location中重复配置

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;

# HTTP/1.1长连接
proxy_http_version 1.1;
proxy_set_header Connection "";

# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;

# 缓冲设置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

# 失败重试
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_next_upstream_tries 3;
proxy_next_upstream_timeout 10s;

3.2 实际应用案例

案例一:电商网站动静分离
以下是对一个日均PV达500万的电商网站进行动静分离改造的架构示例。改造带来了显著的性能与成本优化。

指标 改造前 改造后 提升幅度
首页加载时间 3.2s 0.8s 75%
静态资源响应时间 200ms 15ms 92%
后端服务器CPU 85% 35% 59%
带宽成本 100% 40% 60%
# 电商网站配置架构
# 三层结构:CDN -> Nginx -> 应用服务器

# 商品图片处理
location /goods/ {
    alias /data/images/goods/;

    # 图片处理参数(配合image_filter模块)
    # 实时生成缩略图
    location ~* /goods/(\d+)_(\d+)x(\d+)\.(jpg|png)$ {
        alias /data/images/goods/;
        set $image_id $1;
        set $width $2;
        set $height $3;
        set $ext $4;

        # 优先返回已生成的缩略图
        try_files /cache/${image_id}_${width}x${height}.${ext}
                  @generate_thumb;
    }

    expires 30d;
    add_header Cache-Control "public";
}

# 商品详情页(半静态化)
location ~* ^/item/(\d+)\.html$ {
    set $item_id $1;

    # 优先返回静态化的页面
    try_files /cache/item/$item_id.html @dynamic_item;

    expires 5m;
    add_header Cache-Control "public, must-revalidate";
}

location @dynamic_item {
    proxy_pass http://backend_servers;
    include proxy_params;

    # 开启代理缓存
    proxy_cache proxy_cache;
    proxy_cache_valid 200 5m;
    proxy_cache_key "$scheme$host$request_uri";

    add_header X-Cache-Status $upstream_cache_status;
}

案例二:前后端分离SPA应用
现代前端应用(React/Vue/Angular)打包后本质上是静态文件,天然契合动静分离架构。

# SPA应用配置
server {
    listen 80;
    server_name app.example.com;

    root /data/www/spa/dist;
    index index.html;

    # 前端构建产物(带hash)
    location /assets/ {
        expires max;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # 前端路由支持
    # 所有路由都返回index.html,由前端路由处理
    location / {
        try_files $uri $uri/ /index.html;

        # index.html不缓存,保证用户获取最新版本
        location = /index.html {
            expires -1;
            add_header Cache-Control "no-store, no-cache, must-revalidate";
        }
    }

    # API转发到后端
    location /api/ {
        proxy_pass http://api_servers;
        include proxy_params;

        # API跨域设置
        add_header Access-Control-Allow-Origin $http_origin;
        add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
        add_header Access-Control-Allow-Headers "Authorization, Content-Type";
        add_header Access-Control-Allow-Credentials true;

        # OPTIONS预检请求直接返回
        if ($request_method = OPTIONS) {
            return 204;
        }
    }

    # WebSocket支持(如果需要)
    location /ws/ {
        proxy_pass http://websocket_servers;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;

        proxy_read_timeout 86400s;
        proxy_send_timeout 86400s;
    }
}

案例三:多租户SaaS平台

# 多租户静态资源隔离
# 每个租户有独立的静态目录

map $host $tenant_id {
    default "default";
    ~^(?<tenant>.+)\.example\.com$ $tenant;
}

server {
    listen 80;
    server_name *.example.com;

    # 租户静态资源目录
    set $static_root /data/tenants/$tenant_id/static;

    location /static/ {
        alias $static_root/;

        # 检查租户目录是否存在
        if (!-d $static_root) {
            return 404;
        }

        expires 30d;
        access_log off;
    }

    # 租户自定义主题
    location /theme/ {
        alias /data/tenants/$tenant_id/theme/;
        expires 1d;
    }

    # 公共资源(所有租户共享)
    location /common/ {
        alias /data/common/;
        expires 30d;
        access_log off;
    }

    location / {
        proxy_pass http://backend_servers;
        proxy_set_header X-Tenant-ID $tenant_id;
        include proxy_params;
    }
}

最佳实践和注意事项

4.1 最佳实践

静态资源版本控制
为了避免因浏览器缓存旧文件导致的功能异常,必须对静态资源进行版本控制。推荐以下两种方案,其中第二种更优:

# 方案一:查询参数版本号
/static/app.js?v=1.2.3
/static/style.css?v=20250107

# 方案二:文件名hash(推荐)
/assets/app.8a7f3e2d.js
/assets/style.b4c2a1f9.css

方案二(文件名哈希)的优势在于:CDN对带查询参数URL的缓存策略可能不一致,而基于文件内容的哈希可以实现真正的永久缓存,且现代前端构建工具(如Webpack、Vite)都原生支持。

Location匹配优先级
理解Nginx的location匹配优先级至关重要,错误的顺序可能导致预期外的行为。匹配优先级从高到低依次为:

# 1. 精确匹配 =
location = /favicon.ico { }

# 2. 前缀匹配 ^~(匹配后停止搜索正则)
location ^~ /static/ { }

# 3. 正则匹配 ~(区分大小写)
location ~ \.(jpg|png)$ { }

# 4. 正则匹配 ~*(不区分大小写)
location ~* \.(jpg|png)$ { }

# 5. 普通前缀匹配(最长匹配)
location /api/ { }

# 6. 通用匹配
location / { }

合理使用try_files
try_files指令是处理文件检查的推荐方式,应避免使用低效且复杂的if判断。

# 好的写法
location / {
    try_files $uri $uri/ @backend;
}

# 不好的写法(多余的判断)
location / {
    if (-f $request_filename) {
        break;
    }
    if (-d $request_filename) {
        break;
    }
    proxy_pass http://backend;
}

利用浏览器缓存层级
针对不同类型的资源,设置差异化的缓存策略。

# 长期不变的资源:字体、第三方库
location ~* \.(woff|woff2|ttf|otf|eot)$ {
    expires max;  # 10年
    add_header Cache-Control "public, immutable";
}

# 经常变化的资源:业务图片
location ~* \.(jpg|jpeg|png|gif|webp)$ {
    expires 30d;
    add_header Cache-Control "public";
    # 支持协商缓存
    etag on;
}

# 频繁更新的资源:HTML入口
location ~* \.html$ {
    expires -1;
    add_header Cache-Control "no-cache, must-revalidate";
    etag on;
}

4.2 注意事项

常见错误表

错误场景 错误配置 正确配置 影响
静态目录权限 chmod 777 chmod 755 + chown nginx 安全风险
alias路径 alias /data/static alias /data/static/ 路径拼接错误
root vs alias location /img/ { root /data/static/img/; } location /img/ { alias /data/static/img/; } 404错误
正则捕获 location ~ /goods/(.*).jpg location ~ ^/goods/(.*).jpg$ 匹配范围过大
expires设置 expires 30d;(放在server级别) 放在具体location中 动态请求被缓存
CORS头重复 多处add_header 统一位置配置 浏览器报错

安全注意事项

# 1. 禁止目录遍历
autoindex off;

# 2. 禁止访问敏感文件
location ~* \.(htaccess|htpasswd|ini|log|sh|sql|bak)$ {
    deny all;
}

# 3. 限制上传目录执行权限
location /uploads/ {
    location ~ \.(php|jsp|py|pl|cgi)$ {
        return 403;
    }
}

# 4. 防止MIME类型嗅探
add_header X-Content-Type-Options "nosniff";

# 5. 限制请求方法
if ($request_method !~ ^(GET|HEAD|POST)$) {
    return 405;
}

性能陷阱

# 陷阱1:过多的if判断
# Nginx的if是"evil"的,能用try_files替代就不用if
# 错误示例
if (-f $request_filename) {
    set $is_static 1;
}
if ($is_static = 1) {
    expires 30d;
}
# 正确做法
try_files $uri $uri/ @backend;

# 陷阱2:gzip对已压缩文件二次压缩
# 图片、视频已经压缩过,再gzip会浪费CPU
gzip_types text/plain text/css application/json application/javascript;
# 不要加 image/jpeg image/png 等

# 陷阱3:access_log缓冲不当
# 高并发时日志I/O会成为瓶颈
access_log /var/log/nginx/access.log main buffer=32k flush=5s;

# 陷阱4:open_file_cache配置过大
# 内存有限,缓存项过多会适得其反
open_file_cache max=10000 inactive=60s;  # 根据实际文件数调整

故障排查和监控

5.1 故障排查

问题一:静态文件404

# 排查步骤
# 1. 确认文件存在
ls -la /data/static/css/test.css

# 2. 确认Nginx用户权限
sudo -u nginx cat /data/static/css/test.css

# 3. 检查SELinux(Rocky Linux常见问题)
getenforce
# 如果是Enforcing,执行:
restorecon -Rv /data/static/
# 或者调整SELinux策略
chcon -R -t httpd_sys_content_t /data/static/

# 4. 检查location配置
nginx -T | grep -A 20 'location.*static'

# 5. 查看错误日志
tail -f /var/log/nginx/error.log

问题二:静态资源被转发到后端
症状:静态资源响应时间长,响应头中出现X-Powered-By等后端框架特有的标记。
原因:通常是location匹配顺序或规则问题,导致静态请求命中了代理动态请求的规则。
排查:使用curl -I查看响应头,判断实际走哪个处理逻辑。
解决:调整location顺序,或对静态路径使用^~前缀匹配以阻止后续的正则匹配。

问题三:缓存不生效

# 检查响应头
curl -I http://localhost/static/test.css

# 应该看到类似:
# Cache-Control: public, max-age=2592000
# Expires: Wed, 06 Feb 2025 00:00:00 GMT
# ETag: "65a1b2c3-1234"
# Last-Modified: Wed, 07 Jan 2025 00:00:00 GMT

# 如果没有这些头,检查:
# 1. expires指令位置是否正确
# 2. 是否有其他location覆盖了配置
# 3. 是否开启了proxy_ignore_headers

问题四:权限拒绝(403)

# 1. 检查目录可执行权限
namei -l /data/static/css/test.css
# 所有父目录都需要x权限

# 2. 检查Nginx运行用户
ps aux | grep nginx
# 确认worker进程用户

# 3. 检查文件所有者
ls -la /data/static/css/
# 文件应该对Nginx用户可读

5.2 性能监控

Nginx状态监控

# 启用stub_status模块
location /nginx_status {
    stub_status on;
    access_log off;
    allow 127.0.0.1;
    allow 10.0.0.0/8;
    deny all;
}
# 查看状态
curl http://localhost/nginx_status

# 输出示例:
# Active connections: 256
# server accepts handled requests
#  12345678 12345678 98765432
# Reading: 5 Writing: 128 Waiting: 123

# 指标解释:
# Active connections: 当前活动连接数(包括等待的)
# accepts: 已接受的连接总数
# handled: 已处理的连接总数(正常情况等于accepts)
# requests: 已处理的请求总数
# Reading: 正在读取请求头的连接数
# Writing: 正在发送响应的连接数
# Waiting: 保持连接等待下次请求的空闲连接数

Prometheus监控集成

# nginx-prometheus-exporter配置
# docker-compose.yml
version: '3'
services:
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:1.1
    ports:
      - "9113:9113"
    command:
      - '-nginx.scrape-uri=http://nginx:80/nginx_status'

日志分析脚本

#!/bin/bash
# nginx_stats.sh - 快速分析Nginx访问日志

LOG_FILE=${1:-/var/log/nginx/access.log}

echo "=== Nginx访问统计 ==="
echo ""

echo "--- 请求总数 ---"
wc -l $LOG_FILE

echo ""
echo "--- 静态vs动态请求占比 ---"
awk '{
    if ($7 ~ /\.(css|js|jpg|png|gif|ico|woff)/) {
        static++
    } else {
        dynamic++
    }
}
END {
    total = static + dynamic
    printf "静态请求: %d (%.1f%%)\n", static, static/total*100
    printf "动态请求: %d (%.1f%%)\n", dynamic, dynamic/total*100
}' $LOG_FILE

echo ""
echo "--- 响应状态码分布 ---"
awk '{print $9}' $LOG_FILE | sort | uniq -c | sort -rn | head -10

echo ""
echo "--- Top 10 慢请求(静态) ---"
awk '$7 ~ /\.(css|js|jpg|png|gif)/ {print $0}' $LOG_FILE | \
    awk -F'rt=' '{print $2}' | \
    sort -rn | head -10

echo ""
echo "--- 静态资源带宽统计 ---"
awk '$7 ~ /\.(css|js|jpg|png|gif)/ {sum+=$10} END {printf "总计: %.2f MB\n", sum/1024/1024}' $LOG_FILE

5.3 备份与恢复

配置备份脚本

#!/bin/bash
# backup_nginx_config.sh

BACKUP_DIR="/backup/nginx"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/nginx_config_$DATE.tar.gz"

# 创建备份目录
mkdir -p $BACKUP_DIR

# 备份配置文件
tar -czf $BACKUP_FILE \
    /etc/nginx/ \
    /data/static/ \
    --exclude='*.log'

# 保留最近30天的备份
find $BACKUP_DIR -name "nginx_config_*.tar.gz" -mtime +30 -delete

echo "备份完成: $BACKUP_FILE"

快速恢复流程

# 1. 停止Nginx
systemctl stop nginx

# 2. 恢复配置
cd /
tar -xzf /backup/nginx/nginx_config_20250107_120000.tar.gz

# 3. 检查配置
nginx -t

# 4. 启动服务
systemctl start nginx

# 5. 验证
curl -I http://localhost/static/test.css

总结

6.1 技术要点回顾

动静分离架构的核心可以归纳为三点:

  1. 精准分流:通过精心设计的Nginx location规则,将静态请求与动态请求准确分开处理。
  2. 高效传输:充分利用sentfiletcp_nopushgzip等特性,最大化静态资源的网络传输效率。
  3. 智能缓存:构建从浏览器、CDN到Nginx本身的多级缓存体系,并合理设置缓存策略。

关键配置参数总结:

# 静态资源必备配置
sendfile on;
tcp_nopush on;
expires 30d;
open_file_cache max=10000 inactive=60s;

# 动态代理必备配置
proxy_http_version 1.1;
proxy_set_header Connection "";
keepalive 32;

6.2 进阶学习方向

  • HTTP/2和HTTP/3:利用多路复用等特性进一步提升静态资源加载效率。
  • 边缘计算:将动静分离的思想延伸到CDN边缘节点,实现更快的用户响应。
  • Service Mesh:在云原生架构下探索动静分离的新实践。
  • WebP/AVIF自适应:根据客户端能力,动态返回最优的图片格式以节省带宽。

6.3 参考资料

附录

A. 命令速查表

命令 说明
nginx -t 检查配置语法
nginx -T 显示完整配置
nginx -s reload 平滑重载配置
nginx -s stop 快速停止
nginx -s quit 优雅停止
nginx -V 显示版本和编译参数

B. 配置参数详解

参数 默认值 推荐值 说明
worker_processes 1 auto 工作进程数
worker_connections 512 65535 单进程最大连接数
sendfile off on 高效文件传输
tcp_nopush off on 减少网络包
keepalive_timeout 75s 65s 长连接超时
gzip_comp_level 1 5 压缩级别
open_file_cache off max=10000 文件描述符缓存

C. 术语表

术语 解释
动静分离 将动态请求和静态资源请求分开处理的架构模式
sendfile Linux内核系统调用,实现零拷贝文件传输
upstream Nginx反向代理的后端服务器组
location Nginx配置中的请求匹配规则块
try_files 按顺序检查文件存在性的指令
proxy_pass 将请求转发到指定后端服务器的指令
expires 设置HTTP缓存过期时间的指令
ETag HTTP协商缓存的实体标签

本文介绍的技术实践和配置经验,均可在 云栈社区 找到更多相关的深度讨论和实战案例,欢迎开发者们一起交流学习。




上一篇:Java对象比较指南:从PriorityQueue报错到Top K问题解决,详解Comparable与Comparator接口
下一篇:PolarDB 共享存储IO限流监控与性能优化实践
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 01:42 , Processed in 1.181493 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表