qq临时会话网站,公众号开发者密码多长时间生效,阳城做网站,设计素材网站官网前言今日早读文章由《React状态管理与同构实战》作者LucasHC投稿分享。正文从这开始~~近些年#xff0c;「NodeJS 应该如何在公司业务中真实落地 」这类问题屡见不鲜。自从 2009 年 NodeJS 诞生之后#xff0c;抢尽风头#xff0c;圈粉无数。但一定有工程师不禁要质疑「Node… 前言今日早读文章由《React状态管理与同构实战》作者LucasHC投稿分享。正文从这开始~~近些年「NodeJS 应该如何在公司业务中真实落地 」这类问题屡见不鲜。自从 2009 年 NodeJS 诞生之后抢尽风头圈粉无数。但一定有工程师不禁要质疑「NodeJS 真的已经开辟天地占据架构体系的一席之地了吗」「国外听说 NodeJS 开展如火如荼国内现在到底是个什么状态」「听到过阿里 NodeJS 扛下双十一到底是什么情况」没错风头越大质疑也就越多。当争议和浮华褪去技术的落地就让上帝的归上帝撒旦的归撒旦。本系列文章我将会梳理 NodeJS 在我司以及在国内外其他团队的典型项目案例并深入探讨 NodeJS 的发展前景和最佳实践。开篇小茶歇本篇文章内容较长涉及到了端到端测试NodeJS 服务中台能力(docker 镜像等基础设施)基于图片比对的插件实现等。命中注定的缘分 —— 当 NodeJS 遇见端到端测试困局端到端测试也叫做 UI 测试e2e 测试。用白话说类似常见的自动化测试它站在用户使用的角度并基于协议或者其他技术手段打开真实浏览器与浏览器中页面交互。端到端测试有着肉眼可见的优势比如项目经过不断的开发最终肯定会趋于稳定在适当的时机引入端到端测试能及早发现问题进而保证产品的质量。这种让软件代替人工实施快速、反复的测试收益非常明显。有人总结出端到端收益公式(出处测试人员的 “救命稻草”)端到端收益 迭代次数 全手动执行成本 - 首次自动化成本 - 维护次数 维护成本即便收益明显且相关领域工具层出不穷但端到端测试的落地实施目前并不广泛。有条件接入端到端测试的团队端到端测试似乎也没有扮演到应有的角色一直难以发挥出最大化作用。其原因除了“项目特点是否适合”以外我认为还和端到端测试在开发上线流程中进行接入的阶段有密不可分的关系。具体来说相当多的团队会将端到端测试放到本地执行和项目代码强耦合。比如本地通过 npm script 脚本来实施端到端测试。端到端测试需要保证最新待测试页面的可访问性因此相关脚本需要优先保障本地服务的成功搭建。以 npm run e2e 这样的 npm script 为例相关流程如下图本质上这是一种“按心情”执行。一般需要开发者在本地开发完毕之后自觉执行脚本并观察端到端测试结果。“按心情”的事情自然是无法正规化、流程化、平台化的注定是比较鸡肋的存在。顺着上述思路我们可以想到借助 huskey将上述端到端测试放到 pre-commit 或者 pre-push 阶段强制执行。这样一来我们使得端到端测试流程化将“按心情”执行改为了强制执行。但进一步思考在 pre-commit/pre-push 阶段执行的弊端也很明显增加了额外的 git hooks延长了代码提交流程直接影响到了上线效率。如果是一个 hot-fix 紧急修改 bug这样的时间殇是我们无法接受的。同时本地阶段执行端到端测试的一个前提就是先要保证本地服务的可用性暂且不谈开启本地服务的时间开销一个更尴尬问题就在于本地服务和线上环境是有天然差别的本地执行的端到端测试难以幂等于线上真实效果。基于上述情况端到端测试在团队技术体系中要么渐渐地成为一个“漂亮的玩具”华而不实要么就是一个开发者“眼见心烦的累赘”最终沦为鸡肋。这么看来端到端测试想要破局并突破势必应该在执行流程上进行创新。为此我们认为端到端测试应该搬到“容器”上进行融合到 CI/CD 阶段实施彻底做到自动化、服务化。这里插入一个知识点什么是 CI/CD 阶段呢它们都是现代互联网应用编译和发布流程当中的常用词语分别对应持续集成(Continuous Integration)和持续部署(Continuous Deployment)实际上我们还有一个持续交付(Continuous Delivery)的概念这里不再详细展开我们来聚焦到持续集成和部署。在持续集成环境中开发人员将代码提交到主干 master并触发 Gitlab 的 hook进而自动推进代码的编译。不同的团队对持续集成阶段的定义也许会有略微不同但是并不妨碍我们理解。我司在持续集成阶段主要完成构建项目流程。具体来说在这个阶段中台团队使用基础镜像启动容器拉取最新代码安装必要依赖执行单测脚本并最终 commit 出下一阶段(持续部署阶段)的镜像。在持续部署阶段中台使用构建阶段(持续集成阶段)产出的镜像启动容器按照一定 pipeline 流程串行发布最新版本应用最终启动服务。了解 CI/CD 的概念之后将端到端测试后置到 CD/CD 阶段并在真实容器中执行——这似乎是一个很好的尝试和创新。NodeJS 实现端到端服务 —— 想说爱你不容易截止为此根据我们的“容器中执行端到端测试服务并接入 CI/CD”的设计思想我们可以画出来一个简单的流程分析。由上图衍生出第一个问题我们应该将端到端服务接入到 CI 阶段还是 CD 阶段呢按照常理CI 阶段应该重视测试验证结果以保障所有的提交在部署前的质量对可能出现的一些问题进行预警CD 阶段应该没有人为干预只有当一个修改在工作流 pipeline 中构建失败才能阻止它部署到产品线。但是端到端测试需要保证具有一个可访问的最新版本应用地址而 CI 阶段在我司只是进行代码的编译和容器基础镜像的生成并不会开启应用服务因而不具备端到端测试所需的地址。我们当然可以“改造” CI 阶段新启动一个新的进程进行应用服务的启动但这样的做法显然粗暴而不合理。同时 CD 阶段我司在金丝雀过程之前有一个“办公室阶段”(下文统一使用办公室阶段/办公室环境一词)即办公室内(公司内网环境下)可全量访问新版本应用。也就是说公司内网下访问线上地址www.a.com/b网关会将该流量全部…综合考虑对于我司来讲在这个“办公室”阶段应该是最好执行端到端测试的时间点。一旦端到端测试无法通过将会中断部署交付流程。这样一来就解决了全量可测页面的访问性问题同时端到端测试的环境完全和线上环境保持一致。任何一个创新型项目的开展之路都注定曲折坎坷。设计先行但在实施过程中我们还是遇见了较多的阻力和难点。主要问题集中在端到端框架和中台容器的贴合性、一致性上。下面我举一些典型例子来进行说明。融入社区反推框架的进步和完善我们选用了目前业界最为流行和活跃的 Cypress 作为端到端测试框架关于不同端到端框架的对比和技术实现原理这里不再赘述感兴趣的同学可以关注我们的博文后续将会专门进行解剖。整体端到端服务流程并不复杂如下图这只是一个极简的图示粗略地表现了在相关代码 MR(merge request)成功构建并部署到办公室环境时中台请求我们的端到端测试服务开启接口。这就存在了第一个难题我们发现在办公室部署完成之后端到端服务接收到 post 请求执行 cypress.run()总会得到报错Cypress binary is not installed。为什么本地就能顺利执行到了容器上开启 NodeJS 服务之后就会得到报错呢翻看 Cypress 代码实现究其原因非常有趣Cypress 会在 npm post-install 过程安装 Cypress binary 到容器系统路径下post-install 是 npm 的一个 hook它会在 npm install 成功执行后触发。在执行 cypress.run() 时Cypress 会先执行 cypress.verify() 验证 Cypress 的可用性其中一个验证标准就是检查系统路径下是否存在 Cypress binary。那为什么我们容器上就找不到 Cypress binary 呢我依然用图示还原案发现场在第一次构建时我们的构建脚本执行 npm install并成功触发 npm post-installCypress 将 Cypress binary 安装在容器系统路径~/.cache/Cypress 当中。第二(N)次构建时面对一个“全新”的空容器中台为我们缓存了 node_modules因此 npm install 并不会真正下载依赖post-install 的 hook 也不会触发也就不存在“Cypress 将 Cypress binary 安装在容器系统路径~/.cache/Cypress 当中”这一动作。进而执行时得到了 Cypress binary is not installed 的报错。解决方案也不难我的第一个直观想法是构建脚本当中的 npm install 改为 npm ci 这里插入一下 npm ci 和 npm install 的区别npm ci 需要项目必须要含有 package-lock.json 或者 npm-shrinkwrap.json 文件如果上述两种 lock 文件和 package.json 声明的依赖产生冲突npm ci 命令会强行退出并报错而 npm install 命令会更新 lock 文件npm ci 命令会全量安装项目所有的依赖无法添加单独依赖项目如果项目中已经存在 nodemodulesnpm ci 命令会删除 nodemodules 文件并重新安装npm ci 命令不会写 package.json 内容以及 lock 文件内容因此不难看出在构建阶段本就应该使用 npm ci 命令这也是 npm ci 命令命名的由来。但使用 npm ci 和中台缓存 node_modules 的行为又相矛盾无可避免地增加了构建的耗时。在任何公司的构建部署系统中npm ci 安装依赖的时间一定会是不可忽略的大头之一。有没有更“优雅”的方法呢我坚信“ PR makes world better”。让我们回到本质核心问题在于「中台缓存了 nodemodules导致 post-install 无法触发进而无法安装 Cypress binary 到容器系统路径」如果我们也能缓存 Cypress binary 到指定路径且在执行 cypress.run() 以及 cypress.verify() 时让 Cypress 去设定的缓存路径下找 Cypress binary 是不是就能解决问题。那么这个”缓存路径“当然就是 nodemodules 文件下的某个路径即可(因为中台缓存了 node_modules 文件)。总结一下关键点在于Cypress 需要新增可配置环境变量用于指明 Cypress binary 的安装路径我们设置环境变量 CYPRESSCACHEFOLDER 为 ./node_modules/.cache/cypress/cypress.run() 触发 cypress.verify() 执行时去 CYPRESSCACHEFOLDER 指定的路径下查找 Cypress binary 是否存在此时整体流程如图对于增加配置环境变量使得容器环境执行 Cypress 更加灵活的提议当然也得到了 Cypress 官方的认可此问题暂告解决。同时对于 Cypress 本身体积较大安装耗时且不稳定的问题我们同样使用一个 CYPRESSINSTALLBINARY 环境变量指明默认的 Cypress 软件下载地址。我们在公司内网保存一份内网下载 Cypress 既迅速又可靠。最终的构建部分脚本如下 (采用 yml 格式不影响读者理解)build:# export cypress variables- export CYPRESS_CACHE_FOLDERnode_modules/.cache/Cypress export CYPRESS_INSTALL_BINARYhttp://内网地址/cypress.zip## application build- yarn- yarn build其中可见在执行依赖安装(yarn)和构建项目(yarn build)之前我们声明并导出了相关环境变量。前端和中台化 打通基于 Cypress 的 NodeJS 服务任督二脉解决了 Cypress binary 安装问题我们在执行过程中遇到的第二个问题也很有趣。在 cypress.run() 执行时得到报错信息“CI stage dependency missing in docker”经过和官方团队的讨论我们严重怀疑容器上执行 Cypress 出错的原因在于容器系统版本过低。中台当前提供的容器系统版本均为Debian 8.2(jessie)NodeJS v10.14.0即 docker 基础镜像声明为baseimage: nodejs/v10.14.0jessie (debian 8)。为此我们组织中台团队以及公司内部安全组进行沟通并制作出加入了安全包的新版本 baseimage: nodejs/v12.13.0stretch (debian 9)的基础镜像供项目容器使用。基础镜像的升级并不是简单制作一个镜像那么简单其中涉及到较多“技术之外”的探索和磨合这里我们不过多展开。总之中台团队的存在对于各种类型 NodeJS 应用/服务的落地和发展至关重要。同时中台方面涉及到的能力是传统前端开发所欠缺的。因此项目推动能力跨团队沟通能力也是 NodeJS 发展甚至任何一项前端技术都不可忽视的一环。此外 Cypress 作为一个复杂的端到端测试框架它本身需要很多系统级的依赖比如 Xvfb(is an X server that can run on machines with no display hardware and no physical input devices/虚拟屏幕虚拟输入设备)等这里梳理总结一下必备系统依赖包包括xvfblibgtk-3-devlibnss3libxss1libasound2xz-utils到此为止简要总结一下“端到端测试上容器”这一过程遇见的关键问题以及解决方案Cypress binary 安装问题提 PR 解决提供 Cypress binary 缓存安装路径Cypress 安装超时且不稳定提 PR 解决提供内网获取路径从内网下载容器系统不兼容制作镜像并推动中台升级容器系统基础镜像当然以上问题并不是全部但极具代表性也能总结出任何一个前端团队在公司内推广落地新技术时可能会遇见的问题。具体的挫折可能来自 NodeJS 服务自身也可能来自于和已有技术体系的不兼容解决方案有技术方向的努力也有项目推动方向的尝试。到此我们涉及了服务粗略设计以及基础环境的搭建。接下来我将重点介绍一下容器化端到端测试服务的技术体系架构设计。一个完善、易扩展的 NodeJS 服务设计文章主题围绕着如何开发一个“容器上运行的端到端测试系统”展开前面也提到过其实就是在合适的时机去触发端到端框架的执行想来就这么简单。但是我们在设计一个系统一个平台时应该考虑更多问题比如横向多项目扩展能力平台化服务能力运行效率极致化设计方案通报与预警中断机制合理选型技术方案以及存储方案我们的端到端服务起名为「Goalkeeper」意为“守门员”希望它像一名优秀的守门员一样守卫着我们产品质量的最后一道防线。横向多项目扩展能力Goalkeeper 目前已经进入成熟阶段从立项的角度来说该 NodeJS 服务不能只服务于一个项目测试理想地它应该具备支持公司内所有产品接入的能力并将接入过程和复杂度降到最低。当办公室环境部署完成后中台请求 Goalkeeper Post 接口 https://api.goalkeeper.com/run这个接口提交数据字段包括{stage_name: office,description: style: 1221 活动页样式兼容低版本安卓,mr_iid: 2049999,app_name: xen,author: houce,event_name: deployment_finished,candidate_id: 6666,deploy_id: 6666}app_name 字段为唯一的项目名称配合其他表意字段(应该不难理解这里不再一一说明)这样的接口设计自然支持全公司所用应用的接入仅从接口设计上具备先天扩展能力。接下来的说明也将进一步就横向扩展来展开。Goalkeeper 首页仪表盘页面选择查看应用项目运行效率极致化设计为了最高效地进行端到端测试我们分析对于不同的应用应该多核多进程执行端到端测试保证不同应用测试任务执行的并行性即对于多个项目的部署端到端测试不会发生阻塞不排队对于同一个项目应用必须要避免短时间内多次不同部署之间的互相影响对于这些端到端测试执行任务应该正交化设计串行展开。具体实施就需要一个消息队列不同应用采用不同消息队列 tube相同应用在同一个 tube 中串行生产和消费。因为 Goalkeeper 是一个服务内的消息队列设计因此我选用了轻量且兼具强大功能的 Beastalkd 作为消息队列的技术选型。Goalkeeper 某个应用下部署列表页面点击查看具体信息平台化服务能力具备了支持多应用的能力接下来很自然地就想到「开发者如何查看测试报告和了解测试细节呢」Goalkeeper 的设计包含了非常重要的一块内容 —— 平台化展示。这其实就是一个典型的基于 Koa 的 NodeJS 后端服务前端采用 React 作为多页面应用方案。具体来说每次端到端测试服务完成之后将产生的所有测试报告类数据存入 Redis开发者访问 https://www.goalkeeper.com/dashboardKoa 基于服务端渲染获取相关数据进行单页面应用的平台化展示。这些相关数据不仅包含了每个端到端测试的 case 内容、case 执行信息和结果还包含了测试产生的富媒体文件地址(包括测试录像测试截图等)。关于测试产生的富媒体文件我们采用了容器持久化技术进行存储并对外提供静态服务。换句话说Goalkeeper 在 NodeJS 的服务层面提供了容器上的端到端测试整套单页应用服务(包括查询平台和富媒体静态服务等)比如对于某项目应用某次部署测试信息可查询视频信息以及截图信息通报与预警中断机制为了更好地服务线上应用我们也设计了高效的通报与预警机制。通报机制是指在一次提交部署开始对应相关的端到端测试完成之后通过企业微信和邮件将测试信息和测试平台展示地址发送给提交人或负责人。预警中断机制是指在端到端测试发现异常结果时阻断上线流程并强通知给提交人和负责人。我们的异常结果不仅包含测试 case 的失败更具特色的是也包含了视觉比对测试(visual testing)的异常。基于 Cypress我们封装了一套视觉测试插件它能够在任意节点自动对测试页面进行全量截图并保存为对比基准图片。在下一次测试进行时对当前最新测试的相同节点进行页面全量截图并进行和基准图片的比对如果两幅图片的不同像素超过一定百分比或一定像素阈值则认为视觉比对失败。如图视觉比对测试能够大大解放测试 case 编写的复杂度非常适合样式类测试的回归。当然对于预期之中的图片对比失败比如是正常的页面 UI 改版我们提供了「跳过视觉比对并更新基准图片」的能力。在任何一种测试失败时我们都会出发预警中断流程。如图架构和流程再梳理我们通过分析一个请求的流程再来总结梳理一下整个设计过程如图简单示例更细节一点的图示例当开发者提交代码被合并Merge Request id 为 123 的相关部署到内网环境之后触发中台 hook中台会请求端到端测试 Goalkeeper 服务接口 ./run该服务会为每一个应用创建一个进程处理利用消息队列机制跑该次部署的端到端测试并最后将测试状态结果(running/success/fail)写入 Redis 当中。在这个过程中中台可以根据轮询接口 ./consult该接口查询 Redis 中相关 Merge Request id 的测试状态中台根据该结果值进行解锁上线流程或继续锁定上线。同时在该次部署所对应的端到端测试结束时会更新测试报告平台内容方便开发者访问最新部署产生的端到端测试报告以及录像等富媒体信息。相应的通知和预警机制也会在该阶段触发。整套系统的关键依赖的服务项如图整个 Goalkeeper 平台主要依靠 KoaKoa-staticKoa-router 来处理测试服务请求并提供可查询的测试报告平台后台服务。Cypress 是主要的端到端测试框架它提供了丰富的插件和扩展能力我们在 Cypress 的基础上封装了大量贴合自己业务的插件和扩展比如实现视觉比对测试的 kfe/goalkeeper-image-snapshotkfe/goalkeeper-image-snapshot-runner。Cypress 对应的测试脚本 cases 我们用一个单独的 Gitlab 仓库维护每次在部署发生并启动端到端测试时拉取最新的测试 cases 代码。最后kfe/goalkeeper-report-generator 是整个可查询平台的仓库它是一个完整的基于 React SSR 的单页面应用根据 React-router提供了首页仪表盘页面(/dashboard)该页面展示了所有已接入的应用项目基本信息应用项目详情页(/:app)该页面展示了当前应用项目的基本信息项目部署列表页(/:app/mrList)该页面展示了当前应用项目下所有的部署信息列表测试详情页(/:app/mrList/:mrId)该页面展示了当前部署对应的测试信息测试媒体查询页面(/:app/:mrId/media)该页面展示了当前部署对应测试的富媒体信息包括测试录屏、应用截图等总结这篇文章我们介绍了 NodeJS 助力传统端到端测试最终实现破局和创新的双赢项目案例。具体实施上文章分析了如何实现容器上执行测试如何接入 CI/CD pipeline如何打造一个面向任何项目的 Goalkeeper 平台。对于前端开发者来说学习并实施 NodeJS最关键的就是格局。我们要熟知 NodeJS 特性更要有所谓的“后端”思维架构思维。我相信 NodeJS 的发展和落地不是因为它一定具有了某种与生俱来的力量而是它的某些特点符合技术发展趋势或自然更迭规律。我相信自己生来如同璀璨的夏日之花不凋不败妖冶如火承受心跳的负荷和呼吸的累赘乐此不疲。—— 泰戈尔 Tagore我想开发者们一定会承受心跳的负荷和呼吸的累赘但对于技术的发展乐此不疲。请关注我们后续会带来更多前端技术实践和各种知识关于本文 作者LucasHC 原文https://juejin.im/post/6883397368155373576LucasHC曾分享过【第2026期】「可视化搭建系统」——从设计到架构探索前端领域技术和业务价值【第1706期】不只是同构应用(isomorphic 工程化你所忽略的细节)欢迎自荐投稿前端早读课等你来