Nginx怎么实现简单的前端灰度系统

发布时间 - 2023-05-14 00:00:00    点击率:

写在前面的话

灰度这个概念,来自数字图像领域,最初是描述黑白数字图像的灰度值,范围从 02550 表示黑色,255 表示白色,中间的数值表示不同程度的灰色。

灰度系统的诞生源于交叉学科的建设,在互联网上也不例外。对于一个软件产品,在开发和发布的时候肯定希望用户能够顺利的看到想让其看到的内容。但是,发布没有一帆风顺的,如果在发布的某个环节出了问题,比如打错了镜像或者由于部署环境不同触发了隐藏的bug,导致用户看到了错误的页面或者旧的页面,这就出现了生产事故。为了避免这种情况出现,借鉴数字图像处理的理念,设计师们设计出了一种介于 01 之间的过渡系统的概念:让系统可以预先发布,并设置可见范围,就像朋友圈一样,等到风险可控后,再对公众可见。这就是灰度系统。

灰度系统版本的发布动作称作 灰度发布,又名金丝雀发布,或者灰度测试,他是指在黑与白之间能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。(概念来自知乎)

对于前端领域,演进到现在,灰度系统主要有如下几点功能:

  1. 增量灰度:小的patch可以增量的添加在发布版本上,也可以通过开关一键关闭

  2. 用户灰度:增量和全量版本都可对不同群体或者某几个特定的用户进行灰度可见

  3. 版本回退:每一个版本都在灰度系统里可见,可以一键回退

前端灰度系统工作流程图如下:

sequenceDiagram
前端项目-->灰度系统: 部署阶段
前端项目->>灰度系统: 1.CI 写入打包资源
前端项目->>灰度系统: 2.CI 打包完成后更新资源状态
前端项目-->灰度系统: 访问阶段
前端项目->>灰度系统: 1.页面访问,请求当前登录用户对应的资源版本
灰度系统-->>前端项目: 2.从对应版本的资源目录返回前端资源

灰度规则

关于灰度资源优先级的说明如下:

灰度策略 优先级
未生效
生效
全量 一般

如此就起到了灰度的作用:全量表示所有人都可以看;生效表示只有在规则中的用户才可以看到这部分增量更新,优先级最高;未生效表示不灰度,优先级最低。

灰度系统数据库设计

为什么灰度系统有后端:前端项目 CI 部署后,会产生一个 commit 号和一个镜像记录,并且打包后的文件存放在服务器中某一个深层的文件夹目录中,灰度系统需要存入该部署的目录地址,便于在切换灰度时查找不同版本的文件。

先介绍一个要部署的前端项目(你可以根据自己的前端项目动态调整)。

本项目针对的前端项目是一个基于微服务架构的项目,

下面是设计ER图:

我们依此来分析:

子项目表

该表用于存放所有子项目的信息,新建一个微服务子项目时,会在这个表里新建一个条目,数据示意如下:

灰度用户表

用于灰度系统登录的用户,拥有灰度权限的人才可以加入。

资源表

资源表存放项目在 CI 中写入的 commit 信息和 build 完以后在服务器的存放位置,数据示意如下:

其中 branch 是跑CI的分支,data 存放打包资源目录信息,一般结构如下:

gitProjectId 存放该产品在 gitlab 中的项目号, status 表示构建状态:0:构建完成 1:部署完成 2:构建失败,3:部署失败。

这里简单提一下 CI 是如何写入灰度系统数据库的,过多详情不做解释,写入数据库方式很多,这只是其中一种实现方式。

  1. 首先在 CI build 环节往服务器写入打包信息的 JSON:

其中 build.sh 负责把传入的参数写到一个 json 中。

  1. 在 CI 部署环节,通过调用脚本创建资源:

其中 run_gray.js:

const { ENV, file, branch, projectId, gitProjectId, user, commitMsg } = require('yargs').argv;

axios({
    url: URL,
    method: "POST",
    headers: {
        remoteUser: user
    },
    data: {
        Action: "CreateResource",
        projectId,
        branch,
        commitMsg,
        gitProjectId,
        channel: Channel,
        data: fs.readFileSync(file, 'utf8'),
        status: "0"
    }
}).then(...)

其中 status 的变化,在 CI 部署服务器完成后,追加一个 UpdateResource 动作即可:

if [[ $RetCode != 0 ]]; then curl "$STARK_URL" -X 'POST' -H 'remoteUser: '"$GITLAB_USER_NAME"'' -H 'Content-Type: application/json' -d '{"Action": "UpdateResource", "id": "'"$ResourceId"'", "status": "2"}' > test.log && echo `cat test.log`; fi

灰度策略表

灰度策略是对灰度资源的调动配置。其设计如下:

其中,prijectId 表示灰度的项目,resourceId 表示使用的资源,rules 配置了对应的用户或用户组(看你怎么配置了,我这里只配置了单独的 userId),status 是灰度的状态,我设置了三种:

  • default: 未生效

  • failure: 生效

  • success: 全量

状态生效表示是增量发布的意思。

到这里,数据库设计就完毕了。

灰度系统接口API开发

有了数据库,还需要提供能够操作数据库的服务,上边创建资源的接口就是调用的灰度自己的API实现的。主要的API列表如下:

名称 描述
getResourcesByProjectId 获取单个产品下所有资源
getResourcesById 通过主键获取资源
createResource 创建一个资源
updateResource 更新一个资源
getIngressesByProjectId 获取单个产品下灰度策略任务列表
getIngressById 通过主键获取单个灰度策略任务详情
createIngress 创建一个策略
updateIngress 更新一个策略

剩余的接口有用户处理的,有子项目管理的,这里不做详述。除了上边的必须的接口外,还有一个最重要的接口,那就是获取当前登录用户需要的资源版本的接口。在用户访问时,需要首先调用灰度系统的这个接口来获取资源地址,然后才能重定向到给该用户看的页面中去:

名称 描述 接收参数 输出
getConsoleVersion 获取当前用的产品版本 userId,products resource键值对列表

getConsoleVersion 接受两个参数,一个是当前登录的用户 ID, 一个是当前用户访问的微服务系统中所包含的产品列表。该接口做了如下几步操作:

  1. 遍历 products,获取每一个产品的 projectId

  2. 对于每一个 projectId,联查资源表,分别获取对应的 resourceId

  3. 对于每一个resourceId,结合 userId,并联查灰度策略表,筛选出起作用的灰度策略中可用的资源

  4. 返回每一个资源的 data 信息。

其中第三步处理相对繁琐一些,比如说,一个资源有两个起作用的灰度资源,一个是增量的,一个是全量的,这里应该拿增量的版本,因为他优先级更高。

获取用户版本的流程图如下:

graph TD
用户登录页面 --> 获取所有产品下的资源列表
获取所有产品下的资源列表 --> 根据灰度策略筛选资源中该用户可用的部分 --> 返回产品维度的资源对象

最后返回的资源大概长这个样子:

interface VersionResponse {
    [productId: number]: ResourceVersion;
}

interface ResourceVersion {
    files: string[];
    config: ResourceConfig;
    dependencies: string[];
}

其中 files 就是 JSON 解析后的上述 data 信息的文件列表,因为打包后的文件往往有 css和多个js。

至于这个后端使用什么语言,什么框架来写,并不重要,重要的是一定要稳定,他要挂掉了,用户就进不去系统了,容灾和容错要做好;如果是个客户比较多的网站,并发分流也要考虑进去。

前端页面展示

前端页面就随便使用了一个前端框架搭了一下,选型不是重点,组件库能够满足要求就行:

  • 登录

  • 查看资源

  • 配置策略


部署以后,实际运行项目看看效果:

可以看到,在调用业务接口之前,优先调用了 getConsoleVersion来获取版本,其返回值是以产品为 key 的键值对:

访问转发

这里拿到部署信息后,服务器要进行下一步处理的。我这里是把它封装到一个对象中,带着参数传给了微服务的 hook 去了,可以期待一下后续的手写一个前端微服务的系列文章;如果你是单页应用,可能需要把工作重心放在 Nginx 的转发上,通过灰度系统告知转发策略后,Nginx负责来切换路由转发,可能只是改变一个路由变量。 (,下面我简单的给个示意图:

graph TD
灰度系统配置灰度策略 --> 告知Nginx资源地址 
告知Nginx资源地址 --> Nginx服务器配置资源转发


# nginx  # 架构  # json  # css  # 前端框架  # 封装  # 接口  # 并发  # JS  # 对象  # default  # gitlab  # 数据库  # bug  # 自己的  # 放在  # 出了  # 才可以  # 镜像  # 不做  # 该用户  # 创建一个  # 一键  # 键值 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 百度输入法ai组件怎么删除 百度输入法ai组件移除工具  Android仿QQ列表左滑删除操作  Laravel如何操作JSON类型的数据库字段?(Eloquent示例)  如何在IIS7上新建站点并设置安全权限?  微信小程序 配置文件详细介绍  javascript中的数组方法有哪些_如何利用数组方法简化数据处理  怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?  javascript中的try catch异常捕获机制用法分析  如何用狗爹虚拟主机快速搭建网站?  高端企业智能建站程序:SEO优化与响应式模板定制开发  php增删改查怎么学_零基础入门php数据库操作必知基础【教程】  php做exe能调用系统命令吗_执行cmd指令实现方式【详解】  Thinkphp 中 distinct 的用法解析  北京的网站制作公司有哪些,哪个视频网站最好?  黑客入侵网站服务器的常见手法有哪些?  Win11怎么查看显卡温度 Win11任务管理器查看GPU温度【技巧】  如何制作公司的网站链接,公司想做一个网站,一般需要花多少钱?  如何在腾讯云服务器快速搭建个人网站?  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  Laravel怎么连接多个数据库_Laravel多数据库连接配置  如何在Windows虚拟主机上快速搭建网站?  Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能  如何挑选优质建站一级代理提升网站排名?  网页制作模板网站推荐,网页设计海报之类的素材哪里好?  Laravel项目如何进行性能优化_Laravel应用性能分析与优化技巧大全  香港服务器WordPress建站指南:SEO优化与高效部署策略  VIVO手机上del键无效OnKeyListener不响应的原因及解决方法  如何获取PHP WAP自助建站系统源码?  如何快速搭建高效WAP手机网站?  Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制  Laravel如何使用Eloquent ORM进行数据库操作?(CRUD示例)  微信小程序 闭包写法详细介绍  如何在自有机房高效搭建专业网站?  黑客如何利用漏洞与弱口令入侵网站服务器?  Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践  详解免费开源的.NET多类型文件解压缩组件SharpZipLib(.NET组件介绍之七)  Laravel如何处理JSON字段_Eloquent原生JSON字段类型操作教程  东莞市网站制作公司有哪些,东莞找工作用什么网站好?  Laravel怎么写单元测试_PHPUnit在Laravel项目中的基础测试入门  如何快速打造个性化非模板自助建站?  长沙企业网站制作哪家好,长沙水业集团官方网站?  HTML5建模怎么导出为FBX格式_FBX格式兼容性及导出步骤【指南】  如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  Laravel怎么实现搜索功能_Laravel使用Eloquent实现模糊查询与多条件搜索【实例】  宙斯浏览器文件分类查看教程 快速筛选视频文档与图片方法  Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程  Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】  Laravel如何使用Livewire构建动态组件?(入门代码)  小米17系列还有一款新机?主打6.9英寸大直屏和旗舰级影像  儿童网站界面设计图片,中国少年儿童教育网站-怎么去注册?