分布式微服务系统架构第109集:HTTP缓存优化,Nginx 代理配置,蓝绿部署, Jenkins一键切流脚本

发布于:2025-04-18 ⋅ 阅读:(26) ⋅ 点赞:(0)

加群联系作者vx:xiaoda0423

仓库地址:https://webvueblog.github.io/JavaPlusDoc/

https://1024bat.cn/

🚀 一、前后端缓存策略协同

🔧 1. 前端:合理利用浏览器缓存

  • 强缓存(from disk cache / memory cache)

    • 浏览器命中强缓存,不发请求,直接用本地缓存

    • 适合不常改动的静态资源:如图片、字体、JS/CSS 等

    • 服务端响应:

      Cache-Control: public, max-age=86400
      Expires: Wed, 16 Apr 2025 12:00:00 GMT
    • 前端表现:

  • 协商缓存(304)

    • 服务端响应:

      Last-Modified: Wed, 10 Apr 2025 06:00:00 GMT
      ETag: "abc123xyz"
    • 客户端下一次发起请求带上:

      If-Modified-Since: ...
      If-None-Match: ...
    • 如果资源没变,返回 304,节省带宽;否则返回新的 200

  • 强制刷新(Ctrl+F5)

    • 浏览器自动加上请求头:

      Cache-Control: no-cache
      Pragma: no-cache

🧠 2. 后端:根据资源类型设置缓存响应头

建议结合 Nginx、Spring Boot 或其他后端框架统一配置静态资源策略。

✅ 示例策略(Nginx):
location ~* .(js|css|png|jpg|jpeg|gif|woff|woff2|svg|ico)$ {
    expires 30d;
    add_header Cache-Control "public, max-age=2592000, immutable";
}
location /api/ {
    expires off;
    add_header Cache-Control "no-store";
}
✅ 示例策略(Spring Boot):
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/static/")
            .setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS).cachePublic());
    }
}

🎯 二、配合思路

资源类型

建议缓存策略

原因/说明

JS/CSS

Cache-Control: max-age=31536000, immutable

不常更新、版本号改名

图片/字体

Cache-Control: max-age=31536000, immutable

不变就一直缓存

HTML

Cache-Control: no-cache

每次都需要检查是否有更新

API 请求

Cache-Control: no-store

避免缓存敏感数据,实时性强

JSON 配置文件

可选协商缓存 (ETag + Last-Modified)

若更新频率低可协商缓存

上传文件

通常不缓存

私密/敏感性资源


🎨 三、结合构建工具版本号策略(前端重点)

💡 原理:

每次构建时,资源文件自动添加 hash 值(如 app.7ad32f.js),强缓存时间可以设置很长(如一年),资源更新后文件名变了,用户自然请求最新资源

  • Webpack/Vite/Rollup 常用方式:

output: {
  filename: '[name].[contenthash].js'
}
  • HTML 引用时自动更新引用路径,如:

<script src="/static/js/app.7ad32f.js"></script>

🛠 四、注意点

  1. ETag 在集群部署时建议关闭

  • 每个节点生成的 ETag 可能不同,导致命中失败 → nginx.confetag off;

  • 尽量不要前端禁用缓存(Disable Cache)

    • 除了调试开发,正式环境打开会极大增加流量

  • 图片字体类资源 CDN 分发 + 永久缓存

    • 甚至可以设置:

      Cache-Control: max-age=315360000, immutable
  • 动态数据慎用缓存头

    • 比如用户信息、购物车、支付状态,不建议缓存


    前端合理配置构建和版本策略,后端精细控制缓存响应头。

    层级

    优化手段

    前端

    hash 文件名、immutable 标记、避免 no-cache 滥用

    后端

    静态资源加长缓存、动态资源配合校验、关闭 ETag(如必要)

    网络

    启用 CDN 缓存、压缩 Gzip/Brotli、减少 RTT

    业务

    尽量数据解耦(静态 vs 动态),结构分离、更新原子性

    1. 准备 3 个 Nginx 实例:模拟多层代理(Client → Proxy1 → Proxy2 → Backend)

    2. 配置测试页面

    • 后端 Nginx 返回 $remote_addr$http_x_forwarded_for$http_x_real_ip 来验证 IP 透传。

    • 同时返回 $request_method$request_uri 来验证 proxy_pass 和 rewrite 效果。

    • 添加资源带上 ETagLast-ModifiedCache-Control 头部测试缓存策略。

  • Curl 测试命令模板

    curl -I -H "Cache-Control: no-cache" http://localhost/static.js
    curl -I -H "If-None-Match: "etag123"" http://localhost/static.js
    curl -I -H "a_b: 123" http://localhost:81
  • 关键配置测试点

    • proxy_pass + URI 规则的差异行为

    • rewrite + break 对 proxy_pass 行为影响

    • proxy_set_header 的 header 继承与覆盖机制

    • underscores_in_headers 开关对 header 变量命名的影响

    • 缓存命中(304 vs 200 vs from disk cache)


    ✅ 一、蓝绿部署(Blue-Green Deployment)

    🔍 核心思路:

    同时部署两个版本:

    • blue:当前稳定线上版本

    • green:即将上线的新版本

    通过 Nginx 实现快速流量切换,避免停机。


    🔧 Nginx 配置方法(蓝绿环境切换):

    upstream app_backend {
        server 10.0.0.1:8080;  # blue
        # server 10.0.0.2:8080;  # green(上线时把这行打开,把上面注释掉)
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://app_backend;
        }
    }

    🔁 切换版本时:只需修改 upstream 的目标 IP 并 reload Nginx,即可完成无缝蓝绿切换。


    ✨ 实操建议:

    • 配合 Jenkins/脚本做一键切流

    • 结合 health check,避免新版本服务不可用


    ✅ 二、灰度发布(Canary Release)

    🔍 核心思路:

    只让部分用户使用新版本,根据规则(如 IP、cookie、header、比例等)灰度流量。


    🎯 场景一:按 IP 灰度发布

    map $remote_addr $gray_user {
        default         0;
        192.168.1.100   1;  # 指定用户 IP 灰度
    }
    
    upstream app_v1 {
        server 10.0.0.1:8080; # 旧版本
    }
    
    upstream app_v2 {
        server 10.0.0.2:8080; # 新版本
    }
    
    server {
        listen 80;
        location / {
            if ($gray_user = 1) {
                proxy_pass http://app_v2;
            }
            proxy_pass http://app_v1;
        }
    }

    🎯 场景二:按 Cookie 灰度发布

    map $http_cookie $gray_cookie {
        default     0;
        "~*gray_user=true"  1;
    }
    
    location / {
        if ($gray_cookie = 1) {
            proxy_pass http://app_v2;
        }
        proxy_pass http://app_v1;
    }

    🎯 场景三:按比例灰度发布(权重轮询)

    upstream canary {
        server 10.0.0.1:8080 weight=9;  # v1 - 90%
        server 10.0.0.2:8080 weight=1;  # v2 - 10%
    }
    
    server {
        listen 80;
        location / {
            proxy_pass http://canary;
        }
    }

    ⚠️ 注意:这种方式对“同一个用户固定命中新旧版本”没有保证。


    🔧 Nginx 配合蓝绿/灰度部署的实操小技巧:

    功能点

    技术方案

    精准用户灰度

    $remote_addr

     / $http_cookie / $http_user_agent

    动态权重切流

    使用 nginx + consul/etcd + lua/openresty

    实时切换 upstream

    配合 nginx reload or dynamic upstream 模块

    监控切流效果

    配合 access_log + log_format 打日志分析

    健康检查

    proxy_next_upstream

     + max_fails + fail_timeout

    ✅ 项目结构(蓝绿灰度 Nginx 示例)

    nginx-canary-demo/
    ├── docker-compose.yml
    ├── nginx/
    │   ├── nginx.conf
    │   └── conf.d/
    │       └── default.conf
    ├── app-v1/  # 模拟蓝色版本
    │   └── index.html
    ├── app-v2/  # 模拟绿色版本
    │   └── index.html
    └── test/
        └── curl_test.sh

    📦 docker-compose.yml

    version: '3.8'
    
    services:
      nginx:
        image: nginx:latest
        volumes:
          - ./nginx/nginx.conf:/etc/nginx/nginx.conf
          - ./nginx/conf.d:/etc/nginx/conf.d
          - ./app-v1:/usr/share/nginx/html/v1
          - ./app-v2:/usr/share/nginx/html/v2
        ports:
          - "8080:80"
    
      app-v1:
        image: httpd:alpine
        volumes:
          - ./app-v1:/usr/local/apache2/htdocs/
        ports:
          - "8081:80"
    
      app-v2:
        image: httpd:alpine
        volumes:
          - ./app-v2:/usr/local/apache2/htdocs/
        ports:
          - "8082:80"

    🧠 nginx/conf.d/default.conf

    # 蓝绿灰度配置
    map $http_cookie$is_gray_user {
        default                     0;
        "~*gray_user=true"         1;
    }
    
    upstream blue {
        server app-v1:80;
    }
    
    upstream green {
        server app-v2:80;
    }
    
    server {
        listen 80;
        server_name localhost;
    
        location / {
            if ($is_gray_user = 1) {
                proxy_pass http://green;
            }
            proxy_pass http://blue;
        }
    }

    🔧 nginx/nginx.conf

    worker_processes 1;
    events { worker_connections 1024; }
    
    http {
        include       mime.types;
        default_type  text/html;
        sendfile        on;
        keepalive_timeout  65;
    
        include /etc/nginx/conf.d/*.conf;
    }

    📄 app-v1/index.html

    <!DOCTYPE html>
    <html>
    <head><title>Blue Version</title></head>
    <body style="background-color:lightblue;">
    <h1>Blue Version: v1</h1>
    </body>
    </html>

    📄 app-v2/index.html

    <!DOCTYPE html>
    <html>
    <head><title>Green Version</title></head>
    <body style="background-color:lightgreen;">
    <h1>Green Version: v2 (灰度用户专属)</h1>
    </body>
    </html>

    🧪 test/curl_test.sh

    #!/bin/bash
    
    echo "== 普通用户访问 =="
    curl -s http://localhost:8080 | grep h1
    
    echo "== 灰度用户访问 =="
    curl -s -H "Cookie: gray_user=true" http://localhost:8080 | grep h1

    🚀 使用方法

    1. 启动项目:

    docker-compose up --build
    1. 访问测试:

    • 普通访问: http://localhost:8080

    • 灰度访问: curl -H "Cookie: gray_user=true" http://localhost:8080

  • 运行测试脚本:

  • chmod +x test/curl_test.sh
    ./test/curl_test.sh

    🧩 后续进阶建议

    功能

    方法

    动态流量比例调整

    Nginx + Lua + Redis 控制比例

    权重灰度(10% 进入 green)

    upstream weight

     + sticky session

    请求路径/UA/Referer 灰度

    用 $http_user_agent 或 $request_uri 做 map

    🌈 场景回顾:蓝绿环境 + 灰度切流

    • Blue(当前稳定)

    • Green(新版本)

    • 默认流量都走 Blue

    • Jenkins 按钮发布时,先检测 Green 健康,再流量切换

    • 切流可灰度逐步调整、也可一键全量

    🛠️ 1. Nginx 目标配置(动态切换 upstream)

    我们通过软链接 + reload 实现:

    bash
    复制编辑
    /etc/nginx/conf.d/default.conf  -> points to: conf.d/blue.conf or conf.d/green.conf

    例如:

    conf.d/upstream-blue.conf

    upstream backend {
        server app-v1:80;
    }

    conf.d/upstream-green.conf

    upstream backend {
        server app-v2:80;
    }

    conf.d/location.conf

    server {
        listen 80;
        location / {
            proxy_pass http://backend;
        }
    }

    🧪 2. 健康检查脚本(health_check.sh

    #!/bin/bash
    
    TARGET_URL=$1
    RETRY=5
    
    for i in $(seq 1 $RETRY); do
    echo"Health check [$i/$RETRY]: $TARGET_URL"
      code=$(curl -s -o /dev/null -w "%{http_code}""$TARGET_URL")
    if [ "$code" == "200" ]; then
        echo"✅ Health check success."
        exit 0
    fi
      sleep 2
    done
    
    echo"❌ Health check failed."
    exit 1

    🚀 3. 一键切流脚本(switch_traffic.sh

    #!/bin/bash
    
    TARGET=$1# blue or green
    NGINX_DIR="/etc/nginx/conf.d"
    LINK="$NGINX_DIR/default.conf"
    UPSTREAM="$NGINX_DIR/upstream-${TARGET}.conf"
    
    if [ "$TARGET" != "blue" ] && [ "$TARGET" != "green" ]; then
    echo"Usage: $0 [blue|green]"
    exit 1
    fi
    
    # 健康检查
    bash ./health_check.sh "http://127.0.0.1:808${TARGET/blue/1}${TARGET/green/2}" || exit 1
    
    # 切换 upstream
    rm -f $LINK
    ln -s $UPSTREAM$LINK
    
    # Reload Nginx
    nginx -s reload
    echo"✅ 切流到 $TARGET 成功"

    🧱 4. Jenkins 流程(自由风格任务 / Pipeline)

    方式一:自由风格任务 + 参数

    • 添加构建参数:TARGET_ENV(值为 blue 或 green

    • 构建步骤:

    cd /your/nginx-deploy-dir
    git pull
    chmod +x switch_traffic.sh
    ./switch_traffic.sh $TARGET_ENV

    方式二:Pipeline 示例

    pipeline {
        agent any
        parameters {
            choice(name: 'TARGET', choices: ['blue', 'green'], description: '选择要切流的版本')
        }
        stages {
            stage('切流') {
                steps {
                    sh '''
                    cd /your/nginx-deploy-dir
                    git pull
                    ./switch_traffic.sh $TARGET
                    '''
                }
            }
        }
    }

    🔒 稳定性保证(建议搭配)

    功能

    技术点

    健康检查

    curl + retry

    灰度切换(10%)

    Nginx map + $request_id hash 灰度

    回滚

    切换软链接回 blue 即可

    实时监控

    Prometheus + Grafana

    异常自动报警

    Jenkins 配合 DingTalk/飞书

    Nginx 的主配置文件(nginx.conf)

    🔧 worker_processes 1;

    • 启动 1 个 worker 进程(处理请求的核心进程)

    • 通常设为 auto(自动根据 CPU 核心数来分配)


    ⚙️ events { worker_connections 1024; }

    这是 事件模块,控制每个 worker 能同时处理的连接数:

    • worker_connections 1024:一个 worker 最多能处理 1024 个并发连接

    • 理论最大并发:worker_processes × worker_connections(但受限于系统 ulimit)


    🌐 http { ... }

    Nginx 的 HTTP 服务核心配置块,包含所有 web server 配置。

    逐条解释:


    📎 include mime.types;

    • 引入 mime.types 文件,告诉 Nginx 各种文件扩展名对应的 Content-Type(如 .html 是 text/html


    📎 default_type text/html;

    • 如果找不到合适的 mime type,就默认使用 text/html


    🚀 sendfile on;

    • 开启零拷贝(zero copy),优化文件传输效率

    • 用于静态资源场景(图片、视频、压缩包等)


    🔁 keepalive_timeout 65;

    • TCP keep-alive 时间,单位:秒

    • 如果 65 秒内没有请求,则断开连接,节省资源

    • 默认值是 75,这里调成 65 说明略早释放


    📁 include /etc/nginx/conf.d/*.conf;

    • 引入所有 /etc/nginx/conf.d/ 目录下的 .conf 文件

    • 所有的 server {} 或 location {} 块通常写在这里

    例子:

    /etc/nginx/
    ├── nginx.conf          # 主配置
    └── conf.d/
        ├── app1.conf       # 站点1
        ├── app2.conf       # 站点2

    📁 项目结构建议

    deploy/
    ├── nginx/
    │   ├── blue.conf          # 蓝环境配置
    │   ├── green.conf         # 绿环境配置
    │   └── main.conf          # 入口 nginx 配置 (引用 upstream)
    ├── scripts/
    │   └── switch_env.sh      # 切流脚本(蓝 -> 绿 / 绿 -> 蓝)

    🧩 main.conf(Nginx 主配置引用 upstream)

    # main.conf (加载到 nginx.conf 或 conf.d/default.conf)
    
    upstream backend {
        include /etc/nginx/upstream/active.conf;
    }
    
    server {
        listen 80;
    
        location / {
            proxy_pass http://backend;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    
        location = /healthz {
            return 200 'ok';
        }
    }

    🟦 blue.conf

    # blue.conf
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;

    🟩 green.conf

    # green.conf
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;

    🔗 active.conf(软链接)

    # 初始环境为 blue
    ln -s /etc/nginx/upstream/blue.conf /etc/nginx/upstream/active.conf

    🛠️ switch_env.sh(脚本实现一键切流)

    #!/bin/bash
    
    UPSTREAM_DIR="/etc/nginx/upstream"
    CURRENT=$(readlink "$UPSTREAM_DIR/active.conf")
    
    if [[ $CURRENT == *"blue.conf" ]]; then
        NEW_TARGET="green.conf"
    else
        NEW_TARGET="blue.conf"
    fi
    
    echo"[INFO] 切换到环境: $NEW_TARGET"
    
    # 更新软链接
    ln -sf "$UPSTREAM_DIR/$NEW_TARGET""$UPSTREAM_DIR/active.conf"
    
    # 检查健康
    echo"[INFO] 进行健康检查..."
    
    CHECK_URL="http://127.0.0.1/healthz"
    sleep 1
    HEALTH=$(curl -s --max-time 3 "$CHECK_URL")
    
    if [[ "$HEALTH" == "ok" ]]; then
        echo"[INFO] 健康检查通过,重载 Nginx..."
        nginx -s reload
        echo"[OK] 切流成功!当前环境:$NEW_TARGET"
    else
        echo"[ERROR] 健康检查失败,回滚..."
        # 回滚
        ln -sf "$CURRENT""$UPSTREAM_DIR/active.conf"
        nginx -s reload
        echo"[ROLLBACK] 已回滚至 $CURRENT"
        exit 1
    fi

    ✅ 使用方式

    # Jenkins中执行
    bash /path/to/scripts/switch_env.sh

    💡 可选增强点

    功能

    建议方式

    自动灰度比例切换

    Nginx weight 参数 + hash

    多版本健康检查

    /healthz

     带版本参数

    压力测试

    wrk

     / ab / hey 工具

    可视化状态切换

    搭配 Nginx status + Lua


网站公告

今日签到

点亮在社区的每一天
去签到