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

加群联系作者vx:xiaoda0423

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

1024bat.cn/

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

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

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

    • 服务端响应:

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

      • 浏览器命中强缓存,不发请求,直接用本地缓存
      • 适合不常改动的静态资源:如图片、字体、JS/CSS 等
  • 协商缓存(304)

    • 服务端响应:

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

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

  • 强制刷新(Ctrl+F5)

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

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

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

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

✅ 示例策略(Nginx):

ini 复制代码
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):

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

🛠 四、注意点

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

    • 每个节点生成的 ETag 可能不同,导致命中失败 → nginx.conf: etag off;
  2. 尽量不要前端禁用缓存(Disable Cache)

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

    • 甚至可以设置:

      ini 复制代码
      Cache-Control: max-age=315360000, immutable
  4. 动态数据慎用缓存头

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

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

层级 优化手段
前端 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 头部测试缓存策略。
  3. Curl 测试命令模板

    arduino 复制代码
    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
  4. 关键配置测试点

    • 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 配置方法(蓝绿环境切换):

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

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

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

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

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

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

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

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

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

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

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

bash 复制代码
#!/bin/bash

echo "== 普通用户访问 =="
curl -s http://localhost:8080 | grep h1

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

🚀 使用方法

  1. 启动项目:
css 复制代码
docker-compose up --build
  1. 访问测试:

    • 普通访问: http://localhost:8080
    • 灰度访问: curl -H "Cookie: gray_user=true" http://localhost:8080
  2. 运行测试脚本:

bash 复制代码
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 复制代码
bash
复制编辑
/etc/nginx/conf.d/default.conf  -> points to: conf.d/blue.conf or conf.d/green.conf

例如:

conf.d/upstream-blue.conf

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

conf.d/upstream-green.conf

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

conf.d/location.conf

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

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

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

bash 复制代码
#!/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
  • 构建步骤:
bash 复制代码
cd /your/nginx-deploy-dir
git pull
chmod +x switch_traffic.sh
./switch_traffic.sh $TARGET_ENV

方式二:Pipeline 示例

python 复制代码
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 {} 块通常写在这里

例子:

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

📁 项目结构建议

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

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

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

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

🟩 green.conf

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

🔗 active.conf(软链接)

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

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

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

✅ 使用方式

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

💡 可选增强点

功能 建议方式
自动灰度比例切换 Nginx weight 参数 + hash
多版本健康检查 /healthz 带版本参数
压力测试 wrk / ab / hey 工具
可视化状态切换 搭配 Nginx status + Lua
相关推荐
独立开阀者_FwtCoder1 分钟前
# 白嫖千刀亲测可行——200刀拿下 Cursor、V0、Bolt和Perplexity 等等 1 年会员
前端·javascript·面试
Aska_Lv12 分钟前
RocketMQ---core原理
后端
小七_雪球13 分钟前
Permission denied"如何解决?详解GitHub SSH密钥认证流程
前端·github
AronTing17 分钟前
10-Spring Cloud Alibaba 之 Dubbo 深度剖析与实战
后端·面试·架构
没逻辑21 分钟前
⏰ Redis 在支付系统中作为延迟任务队列的实践
redis·后端
雷渊23 分钟前
如何保证数据库和Es的数据一致性?
java·后端·面试
fjkxyl24 分钟前
Spring的启动流程
java·后端·spring
掘金酱25 分钟前
😊 酱酱宝的推荐:做任务赢积分“拿”华为MatePad Air、雷蛇机械键盘、 热门APP会员卡...
前端·后端·trae
总之就是非常可爱1 小时前
🚀 使用 ReadableStream 优雅地处理 SSE(Server-Sent Events)
前端·javascript·后端
夜寒花碎1 小时前
GO入门——Hello, World
后端·go