Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
    • CSS
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
    • webpack优化实践
    • webpack基础应用知识总结
    • vue常见原理总结
    • vue基础知识总结
    • react高级特性
    • react基础知识总结
    • 微前端总结
    • 地图标绘--射线法来计算点在多边形内
    • web异常监控和分析
    • axios 与 promise
    • 前端优化指南
    • http缓存机制
    • 静态资源灰度发布
      • 🔵 静态资源灰度发布(Canary Release for Static Assets)
      • ⭐ 方案 1:Nginx / CDN 分流(最常用、最稳定)
        • ⛳ 思路
        • 🔧 Nginx 配置示例(按 Cookie 分流)
        • 用法
        • 优点
      • ⭐ 方案 2:CDN 边缘规则灰度(企业常用,如阿里云、Cloudflare)
        • 示例(Cloudflare Worker/PHP Rule)
      • ⭐ 方案 3:HTML 层 AB Test(适用于 SPA)
        • ⛳ 思路
      • ⭐ 方案 4:单页应用内部版本切换(有限场景)
      • ⭐ 方案 5:CI/CD 流程灰度
      • 🔥 关键注意点
        • 1. 必须使用带 Hash 的构建产物
        • 2. 旧版本资源必须继续保留
        • 3. `index.html` 才是灰度的关键
        • 🟢 给你一个非常简短易记的总结
      • 🔵 Vue 项目静态资源灰度发布(完整可落地案例)
        • 📌 场景
        • 第一步:构建产物做版本隔离
        • 🔧 build 脚本(package.json)加 version 参数
        • 第二步:服务器结构
        • 第三步:Nginx 灰度配置(核心)
        • 🔧 Nginx 配置(可直接复制)
        • ✔ 说明
        • 第四步:给灰度用户注入 Cookie
        • 第五步:回滚方案(非常重要)
        • ❌ 发现问题?一键回滚:
        • 第六步:CI/CD 自动化(可选)
        • 🟢 最终效果
      • 🔵 灰度自动化脚本
        • 🟦 1. Nginx 分流方案(基于随机数灰度)
        • 🟦 2. 灰度脚本 gray.sh(支持 0–100% 任意比例)
        • 🟦 3. 使用方式(非常简单)
        • ✔ 灰度 10%
        • ✔ 灰度提升到 30%
        • ✔ 灰度 90%
        • ✔ 全量 100%
        • ✔ 关闭灰度(回滚 v1)
        • 🟦 4. 日志查看(确认灰度比例)
        • 🟦 5. 在 CI/CD中使用
        • 🟢 最终效果(你已经具备)
      • 🔵 灰度失败 → 自动降级到稳定版本
        • 🟦 一、核心思路
        • 🟦 二、Nginx 分流(前提)
        • 🟦 三、前端自动降级脚本(放在 index.html 顶部)
        • 🟦 四、降级的工作原理
        • 1. 设置降级标记,避免无限 reload
        • 2. 切换 Cookie
        • 3. 立即刷新页面 → 加载稳定版本 v1
        • 🟦 五、确保不会无限循环(防止死循环)
        • 🟦 六、额外增强:捕获 JS Runtime 白屏错误
        • 🟦 七、额外增强:捕获未捕获 Promise 错误
        • 🟢 最终你拥有的能力
    • 小程序笔记
    • JS随机打乱数组
    • 非常实用的JavaScript一行代码
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2024-05-01
目录

静态资源灰度发布

# 🔵 静态资源灰度发布(Canary Release for Static Assets)

静态资源(HTML/CSS/JS/Images 等)没有服务端逻辑,因此灰度发布的核心是:

让部分用户访问新版本静态资源,而其他用户仍使用旧版本。 实现关键:分流 + 版本并存 + 可回滚

下面给你常见可行方案:


# ⭐ 方案 1:Nginx / CDN 分流(最常用、最稳定)

# ⛳ 思路

  1. 构建两份静态资源:old 与 new
  2. 按一定规则将部分用户路由到 new 资源目录
  3. 规则可以是:IP、Cookie、Header、AB Test ID、账户等

# 🔧 Nginx 配置示例(按 Cookie 分流)

# 默认走旧版本
set $version "v1";

# 如果 Cookie 中带 version=v2 则走灰度版本
if ($http_cookie ~* "version=v2") {
    set $version "v2";
}

location / {
    alias /var/www/$version/;  # 指向对应版本资源目录
    try_files $uri $uri/ /index.html;
}
1
2
3
4
5
6
7
8
9
10
11
12

# 用法

给灰度用户设置 Cookie:

document.cookie = "version=v2; path=/"
1

# 优点

✔ 精准控制用户比例 ✔ 版本共存,可随时回滚 ✔ 对前端透明,兼容 SPA


# ⭐ 方案 2:CDN 边缘规则灰度(企业常用,如阿里云、Cloudflare)

CDN 支持:

  • 按 User-Agent 分流
  • 按地域分流
  • 按 Cookie 分流
  • 按百分比分流(权重)

# 示例(Cloudflare Worker/PHP Rule)

if (Math.random() < 0.1) {
  // 10% 用户访问新版本
  return fetch("https://cdn.xxx.com/app-v2" + request.url);
} else {
  return fetch("https://cdn.xxx.com/app-v1" + request.url);
}
1
2
3
4
5
6

# ⭐ 方案 3:HTML 层 AB Test(适用于 SPA)

# ⛳ 思路

给不同用户返回不同版本的 index.html,其里面的 JS/CSS 路径不同。

示例:

  • index.v1.html -> 引用 /static/v1/*
  • index.v2.html -> 引用 /static/v2/*

Nginx/Server 决定给谁返回哪份 HTML。


# ⭐ 方案 4:单页应用内部版本切换(有限场景)

在 JS 中决定加载哪个版本的资源(比较少用)

if (isGrayUser()) {
  loadScript("/static/v2/main.js");
} else {
  loadScript("/static/v1/main.js");
}
1
2
3
4
5

# ⭐ 方案 5:CI/CD 流程灰度

结合脚本自动执行:

  1. 上传 v2 静态资源
  2. CDN / Nginx 设置 x% 灰度分流
  3. 监控指标 OK → 提升百分比 → 全量
  4. 若异常 → 切换回 v1

# 🔥 关键注意点

# 1. 必须使用带 Hash 的构建产物

比如:

  • main.55c33.js
  • vendor.9ad01.css

否则缓存会导致用户版本不一致。


# 2. 旧版本资源必须继续保留

否则灰度用户刷新页面会 404。


# 3. index.html 才是灰度的关键

CDN 缓存策略要保证: 页面(HTML)可灰度,静态资源(JS/CSS)长期缓存。

最常见配置:

  • HTML:no-cache or max-age=0
  • JS/CSS:cache-control: max-age=31536000, immutable

# 🟢 给你一个非常简短易记的总结

静态资源灰度 = 两个版本并存 + 控制不同用户返回不同 index.html。

核心:HTML 分流,资源多版本共存,缓存版本锁定。


下面给你一个完整、可真实上线的 Vue 项目静态资源灰度发布案例。 包含:构建、多版本部署、Nginx 配置、Cookie 灰度分流、回滚方案。

内容非常实用,面试也能直接讲。


# 🔵 Vue 项目静态资源灰度发布(完整可落地案例)

# 📌 场景

你的项目是:

my-app/
  dist/
    v1/     ← 当前线上版本
    v2/     ← 新灰度版本(准备发布)
1
2
3
4

你想让:

  • 10% 用户访问 v2
  • 90% 用户继续使用 v1
  • 可以随时回滚
  • 旧版本资源继续保留,避免 404

# 第一步:构建产物做版本隔离

# 🔧 build 脚本(package.json)加 version 参数

{
  "scripts": {
    "build:v1": "vue-cli-service build --dest dist/v1",
    "build:v2": "vue-cli-service build --dest dist/v2"
  }
}
1
2
3
4
5
6

执行:

npm run build:v1   # 线上的旧版本
npm run build:v2   # 即将灰度的新版本
1
2

这样 Vue 会分别打包到:

  • /dist/v1/*
  • /dist/v2/*

并且每个 JS/CSS 都带 hash,不冲突。


# 第二步:服务器结构

将两套版本部署到服务器:

/var/www/my-app/
    v1/
    v2/
1
2
3

⚠ index.html 不能缓存,否则灰度分流无效。


# 第三步:Nginx 灰度配置(核心)

下面是可直接用的灰度分流配置,按 Cookie 判断用户落到 v1 还是 v2。


# 🔧 Nginx 配置(可直接复制)

# 给灰度用户设置 Cookie 后即可访问 v2
map $http_cookie $version {
    default "v1";
    "~*version=v2" "v2";
}

server {
    listen 80;
    server_name example.com;

    # 根路径
    location / {
        alias /var/www/my-app/$version/;

        # 关闭 index.html 缓存
        add_header Cache-Control "no-cache";

        try_files $uri $uri/ /index.html;
    }

    # 静态资源开启强缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|svg)$ {
        alias /var/www/my-app/$version/;
        add_header Cache-Control "max-age=31536000, immutable";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# ✔ 说明

📌 Cookie 中 version=v2 → 访问 v2 📌 无 Cookie → 默认访问 v1 📌 JS/CSS 永不过期(带 hash,不会冲突) 📌 HTML 不缓存,以便实时分流


# 第四步:给灰度用户注入 Cookie

方式一:浏览器控制台测试

document.cookie = "version=v2; path=/";
1

刷新 → 强制进入 v2 版本。


方式二:服务端给部分用户下发 Cookie(真实灰度)

例如:

# 随机 10% 灰度用户
set $rand "";
set $rand $request_id;

# 取 request_id 的最后一位数字
if ($rand ~ "([0-9])$") {
    set $last_digit $1;
}

# 0~9 的数字,10% 落入 v2
if ($last_digit = 1) {
    add_header Set-Cookie "version=v2; Path=/;";
}
1
2
3
4
5
6
7
8
9
10
11
12
13

比如末尾是 1 的用户下发灰度 Cookie。


# 第五步:回滚方案(非常重要)

# ❌ 发现问题?一键回滚:

只需改 Nginx 的分流逻辑即可:

  1. 修改 Nginx 分流:所有用户走 v1

    map $http_cookie $version {
        default "v1";
    }
    
    1
    2
    3
  2. reload Nginx

    nginx -s reload
    
    1
  3. 等待所有灰度 Cookie 失效或立即清除 Cookie

  4. 确认没有请求访问 /v2/ 目录(通过 Nginx access log)

  5. 最后删除 /v2/ 目录

回滚非常快,因为是纯静态资源。


# 第六步:CI/CD 自动化(可选)

比如:

  1. Jenkins / GitHub Actions 编译 v2
  2. 上传v2到服务器
  3. 修改Nginx 分流比例从1% → 5% → 10%
  4. 观察日志和告警
  5. 全量后删除v1(或保留 1 个历史版本)

# 🟢 最终效果

  • 访问同一个域名
  • 不同用户访问到不同版本的 Vue应用
  • 旧资源保留可随时回滚
  • index.html 动态灰度,JS/CSS永久缓存
  • Vue 项目完全无侵入

# 🔵 灰度自动化脚本

脚本以 Nginx + Cookie 分流 为例,可用于 Jenkins / GitHub Actions / 手工执行。

重点特性:

✔ 支持设置灰度比例(如 5%、10%、30%、50%、100%) ✔ 自动写入 Nginx 的 map 配置 ✔ 自动 reload Nginx ✔ 自动备份配置(防误操作) ✔ 全量后自动关闭灰度逻辑

这是行业常用的一套静态资源灰度控制流程。


# 🟦 1. Nginx 分流方案(基于随机数灰度)

你需要在 Nginx 中预留一个字段,用于不同脚本来修改:

# /etc/nginx/conf.d/gray.conf
map $cookie_gray $gray_flag {
    default "off"; # 可由脚本修改为 on/off
}

map $request_id $gray_bucket {
    "~(?<num>[0-9])$" $num;  # 取 request_id 的最后一位数字 (0-9)
}

map "$gray_flag:$gray_bucket" $version {
    # 灰度开关关闭 → 全量旧版本
    "off:0"  "v1";
    "off:1"  "v1";
    "off:2"  "v1";
    "off:3"  "v1";
    "off:4"  "v1";
    "off:5"  "v1";
    "off:6"  "v1";
    "off:7"  "v1";
    "off:8"  "v1";
    "off:9"  "v1";

    # 灰度开关开启 → 实际灰度逻辑由脚本注入
    # (脚本会把 gray.conf 的内容替换成对应灰度比例)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

脚本只需要修改 灰度比例对应的映射表。


# 🟦 2. 灰度脚本 gray.sh(支持 0–100% 任意比例)

创建文件:

/opt/scripts/gray.sh
1

内容如下(可直接用):

#!/bin/bash

# -----------------------------
# 参数检查
# -----------------------------
if [ -z "$1" ]; then
    echo "❌ 使用方法: ./gray.sh <比例>"
    echo "   示例: ./gray.sh 10   # 灰度 10%"
    echo "   示例: ./gray.sh 100  # 全量"
    exit 1
fi

PERCENT=$1

if [ "$PERCENT" -lt 0 ] || [ "$PERCENT" -gt 100 ]; then
    echo "❌ 灰度比例必须是 0~100"
    exit 1
fi

# -----------------------------
# 配置文件位置
# -----------------------------
NGINX_GRAY_CONF="/etc/nginx/conf.d/gray.conf"
BACKUP="/etc/nginx/conf.d/gray.conf.bak"

# 备份配置
cp $NGINX_GRAY_CONF $BACKUP

# -----------------------------
# 生成新的灰度映射规则
# -----------------------------
echo "⚙ 正在更新灰度比例为:${PERCENT}% ..."

# 计算有多少 bucket 命中灰度
# bucket 0~9,一共 10
GRAY_BUCKET_COUNT=$((PERCENT / 10))

# 处理小数,如 15% → 灰度 1 个 bucket(10%)
if [ "$PERCENT" -gt 0 ] && [ "$GRAY_BUCKET_COUNT" -eq 0 ]; then
    GRAY_BUCKET_COUNT=1
fi

# -----------------------------
# 写入新版 gray.conf
# -----------------------------
cat > $NGINX_GRAY_CONF <<EOF
# 本文件由 gray.sh 自动生成
map \$request_id \$gray_bucket {
    "~(?<num>[0-9])$" \$num;
}

map \$gray_bucket \$version {
EOF

for i in $(seq 0 9); do
    if [ "$i" -lt "$GRAY_BUCKET_COUNT" ]; then
        echo "    $i  v2;" >> $NGINX_GRAY_CONF
    else
        echo "    $i  v1;" >> $NGINX_GRAY_CONF
    fi
done

echo "}" >> $NGINX_GRAY_CONF

# -----------------------------
# 重载 Nginx
# -----------------------------
nginx -t
if [ $? -ne 0 ]; then
    echo "❌ Nginx 配置错误,正在回滚..."
    cp $BACKUP $NGINX_GRAY_CONF
    exit 1
fi

nginx -s reload
echo "✅ 灰度比例已更新为 ${PERCENT}%"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76

# 🟦 3. 使用方式(非常简单)

# ✔ 灰度 10%
./gray.sh 10
1

10% 用户访问 v2,90% 进 v1。


# ✔ 灰度提升到 30%
./gray.sh 30
1

无需重启,只会让更多用户落到 v2。


# ✔ 灰度 90%
./gray.sh 90
1

# ✔ 全量 100%
./gray.sh 100
1

系统自动把所有 bucket 指向 v2,相当于完全发布。


# ✔ 关闭灰度(回滚 v1)
./gray.sh 0
1

所有用户全部回到 v1。


# 🟦 4. 日志查看(确认灰度比例)

Nginx access log 中增加:

$version
1

示例:

log_format main '$remote_addr - $version - [$time_local] "$request" $status';
1

这样你能看到当前用户落到哪个版本。


# 🟦 5. 在 CI/CD中使用

在 Jenkins pipeline 中:

stage('灰度 10%') {
    sh "/opt/scripts/gray.sh 10"
}

stage('灰度提升 50%') {
    sh "/opt/scripts/gray.sh 50"
}

stage('全量') {
    sh "/opt/scripts/gray.sh 100"
}
1
2
3
4
5
6
7
8
9
10
11

# 🟢 最终效果(你已经具备)

  • ✔ 支持任意灰度比例 0–100%
  • ✔ 自动生成 Nginx 配置
  • ✔ 自动 reload
  • ✔ 支持全量发布
  • ✔ 支持一键回滚
  • ✔ 可以集成 CI/CD
  • ✔ 不需要改前端代码
  • ✔ 不需要改 Nginx 主配置结构

# 🔵 灰度失败 → 自动降级到稳定版本

真正可在生产使用的「灰度失败 → 自动降级到稳定版本」机制,包括:

  • ✔ 前端自动检测灰度版本加载失败
  • ✔ 自动重新加载稳定版本(v1)
  • ✔ 避免无限循环 + 保证用户最终可正常访问
  • ✔ 适用于 Vue CLI / Vite / SPA
  • ✔ 可同时配合 Nginx 灰度发布使用

# 🟦 一、核心思路

灰度(v2)用户可能遇到的问题:

  • 新版本 JS 加载失败(网络 / CDN / 打包错误)
  • 白屏(入口文件 load 失败)
  • 静态资源 404
  • 部分文件缓存冲突

当 v2 失败时,应自动降级到 v1,以保证用户不受影响。

机制如下:

  1. 浏览器加载灰度的 index.html(指向 v2/main.js)
  2. 如果任何入口文件(main.js / chunk)加载失败
  3. 自动切换 cookie:version=v1
  4. 强制刷新加载旧版本 v1 的页面
  5. 永不陷入循环:最多尝试 1 次

# 🟦 二、Nginx 分流(前提)

Nginx 根据 Cookie 分流:

map $http_cookie $version {
    "~*version=v2"  v2;
    default         v1;
}
1
2
3
4

灰度用户有 version=v2 → 加载 v2 降级用户 version=v1 → 稳定版 v1


# 🟦 三、前端自动降级脚本(放在 index.html 顶部)

这是关键! 要放在 index.html 的 <head> 中,优先于所有 JS 加载。

<script>
(function () {
  // 降级标记(防无限循环)
  const retryKey = 'gray_retry';

  // 若已经降级过一次,不再重复
  if (localStorage.getItem(retryKey) === 'done') return;

  // 全局资源加载失败监听
  window.addEventListener('error', function (e) {
    const target = e.target;

    // 仅处理 JS/CSS 资源错误
    if (target.tagName === 'SCRIPT' || target.tagName === 'LINK') {
      // 记录已降级
      localStorage.setItem(retryKey, 'done');

      // 切换到稳定版 v1
      document.cookie = "version=v1; path=/; max-age=3600";

      // 刷新页面重新加载 v1 资源
      window.location.reload(true);
    }
  }, true);
})();
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 🟦 四、降级的工作原理

假设灰度用户加载:

/v2/js/main.abc123.js
1

如果:

  • 文件损坏
  • CDN 部署不完整
  • JS 报错
  • 404
  • chunk 丢失(懒加载失败)

浏览器触发 window.error 事件(资源加载异常)。

然后脚本会自动:

# 1. 设置降级标记,避免无限 reload
localStorage.setItem('gray_retry', 'done');
1
# 2. 切换 Cookie
document.cookie = "version=v1; path=/";
1
# 3. 立即刷新页面 → 加载稳定版本 v1
window.location.reload(true);
1

# 🟦 五、确保不会无限循环(防止死循环)

只允许触发一次降级:

if (localStorage.getItem(retryKey) === 'done') return;
1

所以逻辑是:

  • 第一次灰度加载失败 → 自动降级
  • 第二次(稳定版 v1)加载一定不会失败 → 不会重复触发

# 🟦 六、额外增强:捕获 JS Runtime 白屏错误

有时不是资源失败,而是代码 runtime 崩溃(Vue 初始化失败)。

加上:

<script>
window.addEventListener("error", function(e) {
  // runtime 错误白屏(灰度版本常见)
  if (e.message && localStorage.getItem("gray_retry") !== "done") {
    localStorage.setItem("gray_retry", "done");
    document.cookie = "version=v1; path=/;";
    location.reload();
  }
});
</script>
1
2
3
4
5
6
7
8
9
10

# 🟦 七、额外增强:捕获未捕获 Promise 错误

很多 chunk 加载失败会走这里:

<script>
window.addEventListener("unhandledrejection", function(e) {
  if (localStorage.getItem("gray_retry") === "done") return;

  // 如果是动态 chunk 加载失败
  if (e.reason && e.reason.message && e.reason.message.includes('Loading chunk')) {
    localStorage.setItem("gray_retry", "done");
    document.cookie = "version=v1; path=/;";
    location.reload();
  }
});
</script>
1
2
3
4
5
6
7
8
9
10
11
12

# 🟢 最终你拥有的能力

你现在具备了完整的「灰度失败自动降级」机制:

  • ✔ 自动检测入口 JS / chunk 失败
  • ✔ 自动降级到稳定版本
  • ✔ 自动刷新
  • ✔ 不会重复触发(避免无限循环)
  • ✔ 可结合 Nginx 灰度策略
  • ✔ SPA(Vue)天然支持

最终效果:

灰度版本即使完全坏掉,用户也看不到白屏,而是自动切回稳定版本。

这是企业大规模灰度发布必备的机制。


上次更新: 2025/11/29, 05:49:46
http缓存机制
小程序笔记

← http缓存机制 小程序笔记→

Copyright © 2015-2025 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式