分布式微服务系统架构第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
相关推荐
AI人工智能+电脑小能手4 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
红尘散仙4 小时前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记5 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
会编程的土豆6 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
Cosolar6 小时前
从零写一个 Attention Is All You Need
人工智能·面试·架构
喵个咪6 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
basketball6166 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
qq_2518364576 小时前
SpringBoot+Vue 共享电池柜管理系统 完整实现 前后端分离项目实战 完整代码
vue.js·spring boot·后端
zhangxingchao7 小时前
AI 大模型核心六:量化、Workflow 与 Agent、多轮 RAG
前端·人工智能·后端
洛宇8 小时前
一个口语 skill,灵感居然来自2021年的那个夏天
人工智能·程序员·github