OpenResty中实现灰度发布需在access_by_lua*阶段基于请求特征动态设置backend变量,结合upstream定义、健康检查、熔断降级及可观测机制,全程无需修改业务代码。

在 OpenResty 中实现部署期间的流量灰度分发,核心是利用 Lua 脚本在 access_by_lua* 或 rewrite_by_lua* 阶段动态修改 upstream 选择逻辑,结合请求特征(如 Header、Cookie、IP、Query 参数)将部分流量导向新版本服务,其余仍走旧版。
基于请求特征识别灰度用户
灰度控制的前提是能稳定识别目标用户。常用方式有:
- 从 Cookie 中提取灰度标识(如
uid=abc123; gray=on),用ngx.var.cookie_gray读取 - 检查自定义 Header(如
X-Gray-Version: v2),用ngx.req.get_headers()["x-gray-version"] - 按用户 ID 哈希取模(如
uid参数),用ngx.crc32_short(uid) % 100 实现 10% 流量灰度 - 对来源 IP 做一致性哈希或范围判断(适合内部测试流量)
动态路由到不同 upstream
OpenResty 不支持运行时直接切换 upstream 块,但可通过 balancer_by_lua* 或更简单的方式:用 proxy_pass 拼接变量地址。
示例配置片段(放在 location 内):
set $backend "http://old_backend";
access_by_lua_block {
local version = ngx.var.arg_v or ngx.var.cookie_version or ""
local uid = ngx.var.arg_uid or ngx.var.cookie_uid or ""
if version == "v2" or (uid and ngx.crc32_short(uid) % 100 <p>注意:<code>proxy_pass</code> 后必须带协议(<code>http://</code>),且 <code>new_backend</code> 和 <code>old_backend</code> 需提前在 <code>upstream</code> 块中定义好。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)"><img
src="https://img.php.cn/upload/skill/000/000/081/179228531040284.jpg" alt="Obsidian (Linux)" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)" class="overflowclass">Obsidian (Linux)</a>
<p class="overflowclass">操作 Obsidian 库(纯 Markdown 笔记),并通过 notesmd-cli 实现自动化。</p>
</div>
<a rel="nofollow" href="/xiazai/skill8828" title="Obsidian (Linux)" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div><h3>配合健康检查与平滑降级</h3><p>灰度期间新服务可能不稳定,建议增强容错:</p>
- 为新 upstream 开启
health_check,自动剔除异常节点 - 在 Lua 中捕获
proxy_next_upstream失败,回退到旧版(需启用proxy_next_upstream error timeout) - 添加简易熔断逻辑:若新服务连续失败 N 次,临时关闭灰度路由 5 分钟(用
shared_dict记录状态)
可观测与快速开关
上线后需实时确认灰度效果,并支持秒级关闭:
- 用
log_by_lua记录每条请求的路由决策(如"uid=xxx → new"),便于日志分析 - 将灰度开关抽离为全局变量(如
lua_shared_dict gray_switch 1m;),通过lua_code_cache off+ 热重载或独立管理接口(如/api/gray/enable)控制 - 配合 Prometheus + OpenResty 的
lua-resty-prometheus暴露灰度命中率、错误率等指标
不复杂但容易忽略的是缓存影响——确保灰度逻辑不在 proxy_cache 范围内执行,或对灰度请求显式禁用缓存(proxy_cache_bypass $arg_v)。整个流程无需改业务代码,靠 OpenResty 层统一拦截和调度即可完成可控灰度发布。










