场景
灰度用户uid存储在redis中,灰度用户访问新版本,其他用户访问旧版本。
发布前:需要准备两套环境:灰度环境与生产环境,其中灰度环境集群资源占比为生产环境占比的10%。
发布后:灰度环境保留并接受生产流量,用于下一次灰度环境发布。
核心逻辑:通过用户UID动态路由请求,灰度用户访问新版本服务,其他用户访问旧版本服务。
一、整体业务架构图
二、请求切换流程图
三、OpenResty完整配置
1. Nginx主配置 (nginx.conf
)
http {
# 共享Redis连接配置
lua_shared_dict redis_conn 10m;
# 灰度服务配置
upstream old_backend {
server 10.0.0.1:8080; # 旧版服务器
server 10.0.0.2:8080 backup;
}
upstream new_backend {
server 10.1.0.1:8080; # 新版服务器
server 10.1.0.2:8080 backup;
}
# Redis配置
resolver 8.8.8.8; # DNS解析器
init_by_lua_block {
redis_host = "10.2.0.100" -- Redis IP
redis_port = 6379
redis_key = "gray_uids" -- 存储UID的集合Key
gray_ratio = 0.1 -- 灰度比例(可选)
}
server {
listen 80;
server_name yourdomain.com;
location / {
# 执行灰度路由逻辑
access_by_lua_file /etc/nginx/lua/gray_router.lua;
proxy_pass http://$backend; # 动态设置的后端
}
}
}
2. Lua路由脚本 (/etc/nginx/lua/gray_router.lua
)
-- 获取用户UID (根据实际场景调整)
local function get_uid()
-- 方案1: 从Header获取
local uid = ngx.req.get_headers()["X-User-ID"]
-- 方案2: 从Cookie获取
if not uid or uid == "" then
local cookie = ngx.var.cookie_userid
if cookie then uid = cookie end
end
-- 方案3: 从URL参数获取
if not uid or uid == "" then
local args = ngx.req.get_uri_args()
uid = args["user_id"]
end
return uid
end
-- Redis查询灰度UID
local function check_gray_uid(uid)
local redis = require "resty.redis"
local red = redis:new()
red:set_timeout(1000) -- 1秒超时
local ok, err = red:connect(redis_host, redis_port)
if not ok then
ngx.log(ngx.ERR, "Redis连接失败: ", err)
return false
end
-- 检查UID是否在集合中
local is_gray, err = red:sismember(redis_key, uid)
if not is_gray then
ngx.log(ngx.ERR, "Redis查询失败: ", err)
return false
end
-- 放回连接池
red:set_keepalive(10000, 100)
return is_gray == 1
end
-- 主路由逻辑
local uid = get_uid()
if not uid then
ngx.var.backend = "old_backend" -- 默认旧版
return
end
-- 检查灰度状态 (带降级保护)
local is_gray = false
local status, ret = pcall(check_gray_uid, uid)
if status and ret then
is_gray = ret
else
ngx.log(ngx.ERR, "灰度检查异常: ", ret)
end
-- 设置代理后端
if is_gray then
ngx.var.backend = "new_backend"
ngx.log(ngx.INFO, "灰度路由 UID=", uid, " -> 新版本")
else
ngx.var.backend = "old_backend"
ngx.log(ngx.INFO, "常规路由 UID=", uid, " -> 旧版本")
end
四、关键组件说明
组件 | 作用 | 示例 |
---|---|---|
Redis | 存储灰度用户UID集合 | SADD gray_uids "user1001" |
管理后台 | 动态管理灰度UID | 添加/删除灰度用户 |
OpenResty | 请求路由网关 | 执行Lua路由逻辑 |
旧版本后端 | 稳定版服务 | v1.0服务集群 |
新版本后端 | 待发布的新版服务 | v2.0服务集群 |
五、灰度发布流程
1、准备阶段
-
部署新旧版本服务集群
-
Redis创建灰度UID集合(
gray_uids
) -
管理后台录入首批灰度用户UID
2、请求处理阶段
3、监控与调整
-
监控新版本错误率/性能指标
-
通过管理后台动态调整灰度用户:
# 添加灰度用户 redis-cli SADD gray_uids "user2024" # 移除灰度用户 redis-cli SREM gray_uids "user1012"
4、全量发布
-
当新版本稳定后:
-
修改OpenResty默认路由到新版本
-
下线旧版本服务
-
清理Redis灰度配置
-
六、注意事项
1、UID获取方式
-
根据业务场景选择:Header > Cookie > URL参数
-
重要:需确保前端传递UID(移动端/Web端需集成)
2、降级策略
-
Redis故障时自动降级到旧版本
-
UID解析失败时默认走旧版本
3、性能优化
-- 在http块添加
lua_shared_dict gray_cache 50m; -- 灰度UID本地缓存
-- 在check_gray_uid函数中添加缓存逻辑
local cache = ngx.shared.gray_cache
local cache_key = "gray_" .. uid
local cached = cache:get(cache_key)
if cached ~= nil then
return cached == 1
end
4、灰度扩展
-
比例灰度:在Lua脚本中添加随机比例逻辑
-
多维灰度:结合设备类型/地域等更多维度
实际部署时需:
调整Redis连接参数(密码/TLS)
配置Nginx日志记录路由轨迹
添加Prometheus监控指标