当前位置: 首页 > news >正文

极简灰度发布实现新老风控系统切流

一、背景描述

公司风控服务即将上线全新版本(新风控),为降低全量切流带来的未知风险,决定采用灰度发布策略:

  1. 先让少量用户流量切入新风控系统
  2. 验证无误后逐步放大比例,直至全量
  3. 一旦异常可秒级回滚到老风控系统

约束条件

  • 入口统一由 Nginx 集群承载
  • 老/新风控均为 HTTP 服务,仅 IP 端口不同
  • 必须保证同一用户始终落入同一版本(用户维度一致性)
  • 低延迟、无外部依赖、运维简单

二、总体思路与原理

  1. 在 Nginx 层通过 Lua 模块维护一张共享内存字典
    key = user_name,value = old | new
  2. 用户首次请求按灰度比例随机计算版本并写回字典,后续请求直接读取字典,实现永久绑定
  3. 通过调整灰度比例变量即可完成放量/回滚,仅 reload 即可生效
  4. 共享字典重启会丢失,但允许少量"重新选路";如零容忍可对接 Redis 做二级存储
监控/运维
后端服务
Nginx 层(OpenResty)
外部
user_name
读取/更新
target_ver=old
target_ver=new
access_log 统计
new 占比
错误率/耗时告警
一键 reload 脚本
回滚方案
A. 写死 old
B. gray_rate=0
老风控
192.168.0.10:8080
新风控
192.168.0.11:8080
Nginx 集群
共享内存字典
risk_gray
(key=user_name
value=old|new)
Lua 脚本
access_by_lua_block
灰度比例变量
gray_rate=5%
版本绑定逻辑
首次随机→写字典
后续读字典
用户请求

三、方案优点

  • 0 外部依赖,<1 ms 延迟
  • 单核可扛 5w+ QPS
  • 回滚只需改一行配置 + reload(秒级)
  • 不改业务代码,不分库分表

四、网络拓扑与链路

User → Nginx(OpenResty) → 根据user_name查字典→
├─ old  → 192.168.0.10:8080  老风控
└─ new  → 192.168.0.11:8080  新风控

五、实操步骤(Step by Step)

0. 环境准备

  • 安装 OpenResty(已集成 Lua 模块)
  • 确认 nginx -V--with-http_lua_module

1. 编辑 nginx.conf(http 段顶层)

# 1. 共享内存,10 MB 约可存 80 万用户
lua_shared_dict risk_gray 10m;# 2. 上游集群
upstream risk_old { server 192.168.0.10:8080 max_fails=2 fail_timeout=10s; }
upstream risk_new { server 192.168.0.11:8080 max_fails=2 fail_timeout=10s; }

2. 在业务 location 中嵌入 Lua 逻辑

location ^~ /api/risk {access_by_lua_block {-- 取用户名(优先 header,其次参数,其次 cookie)local user = ngx.req.get_headers()["X-User-Name"]or ngx.var.arg_user_nameor ngx.var.cookie_user_nameif not user or user == "" thenngx.exit(403)   -- 无用户标识直接拒绝endlocal dict = ngx.shared.risk_graylocal ver  = dict:get(user)-- 首次访问:按灰度比例计算if not ver thenlocal gray_rate = 5          -- 初始 5% 可逐步放大local h = tonumber(ngx.crc32_long(user)) % 100 + 1ver = (h <= gray_rate) and "new" or "old"dict:set(user, ver, 2592000) -- 30 天过期endngx.var.target_ver = ver        -- 传给 content 阶段}# 占位变量,content 阶段使用set $target_ver "";proxy_pass http://$target_ver;proxy_set_header X-Risk-Version $target_ver;  -- 方便后台区分版本proxy_connect_timeout 1s;proxy_read_timeout    3s;
}

3. 灰度放量

  1. 观察新风控错误率、耗时、日志
  2. 修改 gray_rate = 10 → 20 → 50 → 100
  3. 每次执行:
    nginx -t  &&  nginx -s reload
    

4. 紧急回滚

方案 A(瞬间生效)
把 Lua 里 ver = "old" 写死,reload → 所有用户立即回老风控
方案 B(温和)
gray_rate 改 0,新用户全部落老版本,旧映射 30 天后自然淘汰

5. 全量切流(灰度完成)

确认 100% 无异常后,下线老风控节点,将 upstream risk_old 的 IP 改为新风控,注释掉 Lua 段,恢复普通 proxy_pass,再次 reload,即完成全量切换。

六、监控与核对

项目方法
比例核对打印 $target_ver 到 access_log,用 ELK/Grafana 统计 new 占比
性能对比给新风控返回头加 X-Risk-Cost: ms,与老版本对比 P99
异常告警新风控错误率 >1% 立即触发告警,执行回滚脚本
共享内存监控通过 ngx.shared.risk_gray:free_space() 定期上报,低于 20% 扩容

七、常见问题 FAQ

Q1 共享内存重启会丢吗?
会丢,但灰度数据允许“重新选路”;可把过期时间调到 30 天降低概率。若零容忍,把 dict:get/ set 换成 redis:get/ set 即可。

Q2 想按"订单号"而不是用户名?
把 Lua 里 user = ... 改成 order_id = ... 即可,其余逻辑不变。

Q3 灰度过程中想加白名单?
在计算 ver 前加一行

if user == "zhangsan" or user == "lisi" then ver = "new" end

Q4 后端想获取版本标识?
已通过 proxy_set_header X-Risk-Version $target_ver 透传,后台直接取 header。

八、附录:一键 reload 脚本(可选)

#!/bin/bash
set -e
nginx -t
nginx -s reload
echo "Nginx reload OK, gray_rate=$(grep gray_rate /usr/local/openresty/nginx/conf/nginx.conf | awk '{print $3}')"

九、结论

采用 “Nginx + Lua + 共享字典” 方案,可在不引入任何外部组件的前提下,实现:

  • 用户维度永久绑定
  • 毫秒级延迟
  • 秒级回滚/放量
  • 单机 5w+ QPS

整个灰度周期只需改动一个变量并 reload,运维成本低、风险可控,适合快速落地新风控系统的灰度发布需求。


文章转载自:

http://oLBB6aSA.nqyzg.cn
http://UDOIGhkC.nqyzg.cn
http://kz7MJOut.nqyzg.cn
http://Xt1GN8IG.nqyzg.cn
http://2pP4tvUG.nqyzg.cn
http://XuBXPKul.nqyzg.cn
http://BH1QO1jm.nqyzg.cn
http://3Fal1f4s.nqyzg.cn
http://HUKI80Ag.nqyzg.cn
http://90PzgwGg.nqyzg.cn
http://iblWwsvD.nqyzg.cn
http://tc3T5XzV.nqyzg.cn
http://7u54Zh8R.nqyzg.cn
http://wc1MxJSL.nqyzg.cn
http://C5evfbdL.nqyzg.cn
http://nJECipry.nqyzg.cn
http://nEjbKOQU.nqyzg.cn
http://Rjfn1gHX.nqyzg.cn
http://HFEinhZY.nqyzg.cn
http://L41zT0RH.nqyzg.cn
http://3EEze9tu.nqyzg.cn
http://wakNc6VQ.nqyzg.cn
http://iZ82aDYL.nqyzg.cn
http://vere47L6.nqyzg.cn
http://nWiDQiSs.nqyzg.cn
http://R2q0M8dk.nqyzg.cn
http://rZjtlmbN.nqyzg.cn
http://nZQfmjCo.nqyzg.cn
http://mKo1jbAo.nqyzg.cn
http://Vj9mkQQH.nqyzg.cn
http://www.dtcms.com/a/380250.html

相关文章:

  • 基于跳跃表的zset实现解析(lua版)
  • 【学习K230-例程18】GT6700-HTTP-Server
  • Redis列表(List):实现队列/栈的利器,底层原理与实战
  • 超级流水线和标量流水线的原理
  • 漫谈《数字图像处理》之边缘检测与边界预处理的辨析
  • (二)文件管理-文件查看-less命令的使用
  • 深入理解节流(Throttle):原理、实现与应用场景
  • 汽车电子电气架构中的电源架构(下)
  • GISBox与GeoServer使用体验全对比:轻量化工具如何重新定义GIS价值?
  • 02.【Linux系统编程】Linux权限(root超级用户和普通用户、创建普通用户、sudo短暂提权、权限概念、权限修改、粘滞位)
  • JavaEE 初阶第二十二期:网络原理,底层框架的“通关密码”(二)
  • Netty 实战应用:从 RPC 到即时通讯,再到 WebSocket
  • 南京方言数据集|300小时高质量自然对话音频|专业录音棚采集|方言语音识别模型训练|情感计算研究|方言保护文化遗产数字化|语音情感识别|方言对话系统开发
  • Django全栈班v1.04 Python基础语法 20250912 下午
  • uniapp多端打包样式处理
  • Unity学习----【进阶】TextMeshPro学习(一)--基础知识点
  • Echarts雷达图根据数值确定颜色
  • etcd备份脚本
  • 基于 OpenCV 与 SIFT 算法的指纹识别系统实现:从匹配到可视化
  • uniapp微信小程序保存海报到手机相册canvas
  • 3227. 字符串元音游戏
  • 【python实用小脚本-215】[硬件互联] 按钮×Python梦幻联动|用20行代码实现“一键录音”自动化改造实录(建议收藏)
  • 分布式专题——10.2 ShardingSphere-JDBC分库分表实战与讲解
  • 机器学习-数据标注
  • Leetcode:动态规划算法
  • 鸿蒙项目篇-22-项目功能结构说明-写子页面和导航页面
  • 深入解析 Kubernetes 中的 Service 资源:为应用提供稳定的网络访问
  • JAiRouter 0.8.0 发布:Docker 全自动化交付 + 多架构镜像,一键上线不是梦
  • 自如入局二手房,对居住服务行业的一次范式重构
  • BLE6.0信道探测,如何重构物联网设备的距离感知逻辑?