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

加群联系作者vx:xiaoda0423

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

https://1024bat.cn/

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

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

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

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

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

    • 服务端响应:

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

  • 协商缓存(304)

    • 服务端响应:

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

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

  • 强制刷新(Ctrl+F5)

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

      go 复制代码
      Cache-Control: no-cache
      Pragma: no-cache

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

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

✅ 示例策略(Nginx):
go 复制代码
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):
go 复制代码
@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 常用方式:
go 复制代码
output: {
  filename: '[name].[contenthash].js'
}
  • HTML 引用时自动更新引用路径,如:
go 复制代码
<script src="/static/js/app.7ad32f.js"></script>

🛠 四、注意点

  1. ETag 在集群部署时建议关闭
  • 每个节点生成的 ETag 可能不同,导致命中失败 → nginx.conf: etag off;
  • 尽量不要前端禁用缓存(Disable Cache)

    • 除了调试开发,正式环境打开会极大增加流量
  • 图片字体类资源 CDN 分发 + 永久缓存

    • 甚至可以设置:

      go 复制代码
      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_passrewrite 效果。

    • 添加资源带上 ETag, Last-Modified, Cache-Control 头部测试缓存策略。

  • Curl 测试命令模板

    go 复制代码
    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 + breakproxy_pass 行为影响

    • proxy_set_header 的 header 继承与覆盖机制

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

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


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

    🔍 核心思路:

    同时部署两个版本:

    • blue:当前稳定线上版本

    • green:即将上线的新版本

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


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

    go 复制代码
    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 灰度发布

    go 复制代码
    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 灰度发布

    go 复制代码
    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;
    }

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

    go 复制代码
    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 示例)

    go 复制代码
    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

    go 复制代码
    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

    go 复制代码
    # 蓝绿灰度配置
    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

    go 复制代码
    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

    go 复制代码
    <!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

    go 复制代码
    <!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

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

    🚀 使用方法

    1. 启动项目:
    go 复制代码
    docker-compose up --build
    1. 访问测试:
    • 普通访问: http://localhost:8080

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

  • 运行测试脚本:

go 复制代码
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 实现:

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

例如:

conf.d/upstream-blue.conf

go 复制代码
upstream backend {
    server app-v1:80;
}

conf.d/upstream-green.conf

go 复制代码
upstream backend {
    server app-v2:80;
}

conf.d/location.conf

go 复制代码
server {
    listen 80;
    location / {
        proxy_pass http://backend;
    }
}

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

go 复制代码
#!/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

go 复制代码
#!/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(值为 bluegreen

  • 构建步骤:

go 复制代码
cd /your/nginx-deploy-dir
git pull
chmod +x switch_traffic.sh
./switch_traffic.sh $TARGET_ENV

方式二:Pipeline 示例

go 复制代码
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(如 .htmltext/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 {} 块通常写在这里

例子:

go 复制代码
/etc/nginx/
├── nginx.conf          # 主配置
└── conf.d/
    ├── app1.conf       # 站点1
    ├── app2.conf       # 站点2

📁 项目结构建议

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

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

go 复制代码
# 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

go 复制代码
# blue.conf
server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;

🟩 green.conf

go 复制代码
# green.conf
server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;

🔗 active.conf(软链接)

go 复制代码
# 初始环境为 blue
ln -s /etc/nginx/upstream/blue.conf /etc/nginx/upstream/active.conf

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

go 复制代码
#!/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

✅ 使用方式

go 复制代码
# Jenkins中执行
bash /path/to/scripts/switch_env.sh

💡 可选增强点

功能 建议方式
自动灰度比例切换 Nginx weight 参数 + hash
多版本健康检查 /healthz 带版本参数
压力测试 wrk / ab / hey 工具
可视化状态切换 搭配 Nginx status + Lua
相关推荐
Hello.Reader34 分钟前
ngx_http_limit_conn_module精准连接控制
网络·网络协议·http
张哈大36 分钟前
【 Redis | 实战篇 秒杀实现 】
数据库·redis·缓存
山猪打不过家猪3 小时前
(五)毛子整洁架构(分布式日志/Redis缓存/OutBox Pattern)
分布式·缓存
巴巴_羊5 小时前
前端面经 计网 http和https区别
网络协议·http·https
Chase_Mos5 小时前
Spring 必会之微服务篇(1)
java·spring·微服务
LyaJpunov7 小时前
HTTPS全解析:从证书签发到TLS握手优化
网络协议·http·https
你曾经是少年7 小时前
HTTPS
网络协议·http·https
2501_915918417 小时前
多账号管理与自动化中的浏览器指纹对抗方案
websocket·网络协议·tcp/ip·http·网络安全·https·udp
jstart千语7 小时前
【Redis】分布式锁的实现
数据库·redis·分布式
?abc!9 小时前
缓存(5):常见 缓存数据淘汰算法/缓存清空策略
java·算法·缓存