【开源协同】一种基于nohost和tolstoy的云联调方案

作者 覃淑慧 腾讯前端开发组

引言

在现代化工程化的前端开发环境下,需要搭配先进的联调工具进行食用,体验更佳。本文主要介绍利用nohost和tolstoy实现联调简化,提升联调开发效率的方案及总结。

背景

联调,一直是个开发流程中的痛点,但因为它位于前后端沟通中的过渡区域,位于人力分工中的尴尬位置,因此联调相对于其他技术范畴来说,可以算是没有发展得非常成熟。如果简单地把开发步骤分为:开发,联调,测试,部署,那么,随着开发框架及各种工具的完善,开发及运维效率得到了快速提升,联调则越发成为了效率提高的瓶颈。

随着部门中的权限收拢,开发权限也受到了前所未有的压缩,再加上外包同事的大量加入,权限的封锁使其无法工作,我们亟需快速有效地打破这一僵局。

云联调是什么?

一般联调中,前后端采用直连或者简单代理的方式进行接驳沟通,这种方式原理简单,操作自由,简单图示如下:

这种直连方法更接近于真实运行情况,通用且灵活,但在大团队开发中,很多弊端就浮现出来了:

  1. 联调环境不稳定:后端挂了前端就不能用了。

  2. 代理配置复杂:如果一个项目中需要多个后端服务提供的接口,那代理的配置就很繁琐了。

  3. 业务存量接口影响:存量接口无数据,存量接口无mock,存量接口复用,存量接口规范无文档…

由于一般联调的局限性,我们希望利用“ ”的思想去突破这些限制。利用云,作为一个中转站,在抹平各个后端的差异的同时,提供公共能力,为无数的前端开发提供稳定全面且灵活的服务。概念简图如下:

开发上云,服务上云,那联调也肯定是可以上云的。联调上云的愿景很简单,包括:

  • 在联调时,一切数据唾手可得

  • 少量配置,全员皆可使用

之后我们决定往云联调这个方向探索一番。

基于nohost的云代理

代理是前后端联调沟通的关键,所以云联调中首先要解决的就是代理问题。

一般我们在联调开发中需要使用一种或若干种工具:抓包工具(MiniSniffer,Fiddler或Wireshark),代理工具,host配置工具(switchHost)等,但现在有更好的集大成者—— whistle ,这里不作过多赘述。但whistle在实际项目中,因为配置项多,操作感强,所以还是存在一定的学习成本,对于未接触过代理的新同学以及外包同学来说比较难以理解。因此我更希望的是有一种代理,可以一个人配置,其他全部人都可以直接使用。

鉴于whistle的缺点,我们找到了它的更高一层的封装工具—— 环境代理配置神器 Nohost

Nohost是基于whistle实现的多任务调试代理服务,可以部署在公共的服务器上供多人同时使用,每个人可以拥有一个独立的代理及配置多个相互独立的环境,每个环境上都可以配置whistle的所有规则。

直接介绍 nohost 对一些同学来说可能比较抽象,因此我们先了解一下代理的基础知识。

1 代理的分类与演进

代理的方式从原始到抽象,从简单到复杂大概可以分为以下四种模式: 单机模式,跨域模式,本地代理模式,云代理模式 。每个模式都需要基于前端开发工具的proxy配置(比如:webpack devServer的proxy)。每个方案都有其优缺点,我们需要根据不同的角色不同的开发环境条件来选择不同的方案。下图是我们在实际应用中四种代理的演进历程:

(1)单机模式

单机模式顾名思义就是前后端都在本地跑起来运行,相互通过localhost+端口进行访问。这种方式适用于前后端不分离,或者前后端分离但开发者不分离,需要一人同时开发前后端的全栈开发的情况,这种方式下开发者能节省了很多沟通了工作,绑字段的时候没准还能默写出来。我们在前端后分离前,一直都是用这种方式进行开发,直到现在我们的后端同事在联调时也是用这种方式。

(2)跨域模式

跨域模式是指,前端直接在本地localhost发起请求,代理直达后端服务器。由于浏览器自身的跨域限制,我们可以利用webpack的devServer提供的proxy配置,解决跨域的问题。这种方式仅限于在无严格鉴权限制的系统中使用。也可能前后端初步分离,前端不想运行后端代码,基础建设还没非常完善的时候会用到。

(3)本地代理模式

本地代理模式是指,利用fiddler或whistle等代理工具,在本地配置代理规则。这种方式是最直观的使用代理的模式,开发者自由度很高。但缺点就是有较大的学习成本,每个人需要学习 增、删、改、导入、导出 管理规则,各自管理配置也有较大的维护成本。我们一开始只有一两个外包同学的时候,就用git在工程中管理代理配置文件,使用的时候再每个人各自导入规则,代理规则有变更时则需要导出并上传,相当麻烦。

不过在项目类型多样,环境条件复杂且没什么规律的项目组中,则通常需要每个人都使用本地代理模式,用git管理代理规则还是是比口口相传优越多了。

(4)云代理模式

本地代理中,每个人需要维护大量的配置,总配置数可以通过一条简单的算式得到: 配置数 = 开发人数 x 项目数 x 系统环境数 ,很快就暴露出了弊端。再加上,因为业务权限管理加强,测试环境权限收拢,开发同学开发需要申请,某些项目外包同学直接禁止申请,这导致我们必须得加上mock环境,加上了mock环境之后,代理配置就更多了,本地代理的手动配置根本无法cover,新增业务还能试着一个个添加,但是存量业务就真的无能为力了。

这时候我们的出路只有一条,那就是代理必须上云!云代理模式就是在本地代理的基础上,把个性配置留在本地,把公共配置提取到了云上统一管理,这样既保留了灵活性,又享受了云上的便利服务。

一般fiddler + willow代理配置:

2 反向代理

代理工具问题解决了之后,我们尝试进入开发,但是后发现有登录cookie或者是网关限制我们跨域访问。

为了解决登录问题,有些代理工具需要输入cookie,但是这样并不能覆盖所有场景而且存在一定的风险。代理要做得简单,首要解决的就是登陆问题。

我们不能解决它还不能绕过它?因此产生了“反向代理”的思路:直接访问目标域名,然后把目标域名的静态资源指向localhost的静态资源,把前端热更新的请求也指向本地,其他请求(多数为后端请求)则直连目标域名,我们暂且把这种做法叫做“反向代理”。以haha.com为例,详细做法如下图:

首先我们打开浏览器,
第一步:在浏览器直接访问haha.com目标域名,此时haha.com的所有资源都从远程获取;
第二步:打开代理工具,加入规则——静态资源与热更新转发到本地;
第三步:刷新haha.com域名,此时请求被代理抓取,静态资源请求被发到localhost,返回本地资源,其他请求无匹配规则,则直连原始域名;
第四步:前端代码修改保存,触发热更新,请求被代理抓取,同样被转发回localhost。

优势

  • 免上传cookie


    Alibaba Cloud Tookit
    ,需要拿到cookie并且复制到工具中,这种做法不说多了一道工序,还有上传登陆cookie这种做法就让人很不放心。

缺点

  • 让人难以理解。使用者在不了解原理的情况下使用,会对这种做法 相 当 难 以 理 解 ,导致在遇到问题时根本不知道从何入手。

3 我们的代理现状

nohost官网提供了三种使用nohost的方式,但我一般比较推荐直接使用nohost客户端的方式,只需要在第一次用的时候配置一次,后面每次使用时只需要双击打开nohost客户端就好了,基本不用动脑。使用nohost客户端的方式对于前端、后端、设计具有普适性,极大降低了沟通成本和管理难度。但对于玩家级的同学来说,希望获得更大自由度,也可以使用whistle + SwitchyOmega的方式。

我们代理的现状,各部分的流转原理如下:

这是一种在上面所说的反向代理的基础上,融合了nohost云代理的模式,实际操作步骤如下:

第一步:nohost客户端加入默认规则

/.*hot-update.js(on)?/ 127.0.0.1:8081
localhost/sockjs-node/info 127.0.0.1:8081

/\S*xx.xx.com\/static\//  127.0.0.1:8081
/\S*xx.xx.com\/app\//  127.0.0.1:8081
/\S*xx.xx.com\/__webpack_hmr/  127.0.0.1:8081
/\S*xx.xx.com\/$/ 127.0.0.1:8081

PS: 默认规则全员统一,基本不怎么变,玩家请自行增改

第二步:前端代码准备

webpack的proxy配置target为 test.xx.xx.com ,运行前端代码 npm run serve,监听端口为8081。

第三步:访问目标域名 test.xx.xx.com

进入目标域名后,nohost会在页面注入 小圆点 ,我们可以通过小圆点切换环境,我们可以选择正式环境和mock环境,正式环境即当前目标域名,mock环境即tolstoy平台,后面会有相关描述。

这样,我们后续每次只要运行项目,打开nohost,2步就好。

4 效果对比

根据我们组好几个新外包同学的配置使用情况,做了一个简单的效果对比:

代理方式配置 耗时
本地代理模式 2h
云代理模式 25min

5 代理配置问题

(1)部署问题

由于不同机器上存在策略限制,因此nohost的部署要根据具体业务条件和各环境部署情况进行考虑。

我们的各个系统一般有三个环境,test,uat,prd,其中uat和prd环境使用真实数据,内容敏感,因此极不推荐使用这两个环境的数据进行开发联调,也有严格的权限控制。但是,为了防止意外发生,我们直接把nohost服务部署在了测试环境的机器(devnet)上,保证绝大多数情况下不会与敏感的环境打通,降低风险。

但如果存在一定要用uat环境的情况,也可以直接使用nohost官网提供的第一种模式进行开发,直接使用whistle+SwitchyOmega,此时则需要第二道防线——权限控制来保证安全。但如果你的业务权限管理不严格,也可以把nohost部署在更通用的地方。

下面是我们的部署方案的简图:

图中忽略网关,同时注意酌情申请nohost部署环境与mock环境(tolstoy)之间的策略

(2)与ioa代理冲突问题

无论是打开fiddler还是打开nohost客户端之后,经常ioa会弹出提示问“是否阻止代理服务修改?”,我们为了保证nohost的正常使用,选择“允许”。有时候代理的修改会影响我们正常的上网冲浪,此时可以点击ioa的“修复工具,修复下代理配置。

(3)为什么不是fastest ?

fastest 平台化程度更高,比nohost功能更加强大,按照我们自身情况,最终选择nohost的原因主要有:

  • nohost的用法更接近whistle,使用者的基础知识能融会贯通

  • nohost部署较灵活,fastest相对来说更加抽象一些,复杂环境下解决问题难度较大

基于tolstoy的mock平台

在联调工作中,mock则是地位更加尴尬的一项工作,大家都知道要用,但优先级永远放在最后一位。因此,我们需要一个工具,可以以最少的工作量,做到即插即用。

1 工具选择

mock的方式有非常多,前端有著名的 mockjs ,自制mockServer的教程也有很多,但是这些工具更多还是在于“如何模拟数据”,我们的需求则更偏向于“自动化模拟数据”。因此我们选择了平台,而非工具。以下是两个备选方案:

tolstoy

:是公司内的一个比较成熟的mock平台。


yapi
:是一个开源的可本地部署的、打通前后端及QA的、可视化的接口管理平台。

考虑到财付通技术闭环的问题,首选yapi,但是考虑到后续运维及发展问题,在了解Tolstoy的落地数据的合规性后,还是选择了Tolstoy,但Tolstoy还是有一点小问题,希望后续能继续优化好。

选定了mock平台,我们的整体mock方式,将以mock平台为主,其他方式为辅进行联调。下面是该方案在实际应用中的使用介绍:

UI重构阶段

在UI重构阶段,我们推荐使用Tolstoy平台+mockjs的形式。有人可能会疑惑mock平台在UI重构阶段能做啥?其实,可以利用mock平台来存放一些常用的接口,经典的例子比如: 成功失败的返回格式

像我们有 C# java 两种后端,当两边加入新同事的时候,无论你积累了多么完善的规范文档,总有人是看不到的,但由于人又是视觉动物,遇到问题第一时间会去找目所能及的地方,此时前端开发通常就成为了 问题路由器

另外还有一些业务组件的数组格式,还有约定俗成的弱规范,也都可以用mock平台记录下来。这样,通过一种 协议为先 的方式,打通多技术栈间的规范,大大地降低运维、管理和沟通的成本。

联调阶段

在联调阶段,mock平台则成为了主力,在tolstoy里,我们可以直接导入后端提供的接口文档(swagger)等,直接批量生成mock。这样做最大的好处是,海量的祖传接口终于有一次性被mock掉的方案了!从此

  • 再也不怕测试环境挂了就不能开发了

  • 再也不怕权限封锁了

  • 再也不用等待后端制造mock数据了

2 mock接口新增最佳实践

总结使用tolstoy平台的方式有三种,从简单到复杂分别如图三种方式所示。在实际工作中,为了适应不同的项目特性,我们通常三种方式混合使用。

2.1 批量导入

首先要获取到整个项目的接口文档文件,直接导入到Tolstoy里,即可一次性生成所有接口的mock配置。这种方式操作最简,效率最高,但是在实际项目中,这样一次性大量导入,经常会由于某些接口文档格式不规范而造成报错,导入失败。总的来说,批量导入能解决绝大部分的问题了。

2.2 单个导入

在某些场景下,比如上面说的批量导入会有少量失败接口,还有存量项目新增接口,这时候总不可能为了一个接口而再批量导入一次全量接口吧,万一把已有的配置覆盖了呢?此时,单个导入将作为批量导入的补充功能的方式。下面以swagger为例,介绍如何导入单个接口。

第一步:创建工具

接口文档肯定都不是只有单个接口的,我们需要创建一个提取工具,
无论你使用哪一种方式去实现,java,nodejs,serverless,这个工具应该是这样的:
输入:swagger.json,接口名
输出:单个接口的标准swagger.json(方便导入tolstoy)

我们的转换工具是用express搭建的简单服务。

第二步:获取单个接口的swagger.json

(1)选择项目的appId,比如: vcreport

(2)输入目标接口名,比如: /manualxxxxxxx/xxxxxxxxxxxxx

(3)点击查询,查询成功后,会在结果框显示出结果

(4)点击 下载json文件

第三步:导入tolstoy

把下载的json文件,导入tolstoy即可

但是,转换工具并不全能,例如某些巨石系统的接口文档,光打开都需要3min+,用转换工具一个个转耗时非常长,甚至超时报错。

2.3 手动加入

手动加入 适用于有固定数据或者是非标准json格式的返回数据。

云联调 = 云代理 + mock平台

现在基于nohost的云代理和基于tolstoy的mock平台都准备就绪了,可以打通实现云联调了。由于选择的两个工具刚好都是来自于同个部门,或者是在设计之初就有一定的共识,所以在使用上还是比较顺畅的。

1 加入代理规则

tolstoy新增了mock接口之后,会为每个接口提供唯一的Mock Url, 我们可以在代理工具加上这些代理规则来无侵入地使用这些Mock Url,有以下三种方式:

1.1 单个代理加入

  1. 查看tolstoy单个接口详情,复制 whistle代理规则参考

  2. 打开自己的nohost云代理平台,把配置复制到对应的账号/项目名下

1.2 批量代理

  1. 进入tolstoy对应需求 -> 批量导出代理规则 -> 复制whistle规则

  2. 重复1.1单个代理加入中的步骤2

  3. 全量去除规则中的 https: ,不然会有接口不通的问题(这个是bug,已提issue)

1.3 直接使用

直接使用Mock Url也是可行的,这种情况通常用于以下场景:

  1. 约定好的规范数据:通用组件数据格式,业务常用接口,这类接口通常不用等待接口文档,前端可以先用来重构

  2. 用于文档示例,比如 清风大前端 清风前端框架 等说明文档,用的就是这类Mock Url

2 常见问题

1 接口与Mock Url不对应

答:因为whistle规则是用正则匹配的,你可能有好几个名称类似的接口,都匹配到了一个接口上。解决办法:可以调整规则的排列位置,优先级,或者优化正则的匹配规则。

2 设置高级mock的时候,内容复制到编辑器成功了,但是保存却报错

答:tolstory一个接口最大支持64kb的mock配置,超过的请自行用mockjs解决。

3 云联调万能吗?
答:不是。比如一些特殊类型的数据是无法cover的,比如blob对象格式等,特殊情况还是需要跟真实接口进行联调。

后续发展

Tolstoy深入应用

Tolstoy的定位是“前后端联调平台”,我们这里仅仅用到了前端部分,希望后续后端部分也能找到接入场景,达到全面面向协议开发。
另外,希望mock平台可以增加匹配规则扩展,配合mockjs,根据参数名或者某些配置,给出具体的数据,而不是1111。

代码生成

还记得上面的“单swagger提取工具”吗?利用它的能力,再加上我们的前端的组件规范,代码生成是易如反掌的。可以想象,当接口文档出来之后,对应的联调环境跟前端代码都已经生成好了,又或者,一个系统都生成好了。概念大致如下:

总结

联调就是后端不好好写单元测试与集成测试,让前端发请求调用以达到测试的目的;前端不好好写Mock和测试,让后端输出数据以达到测试的目的。—— 引用自《前后端联调实践总结》

其实,联调问题就是社会发展人类分工细化的边缘性问题。我们可以对比从代理的“单机模式”到“云代理模式”,其实就是一个工作越来越细化的过程,这种变化使得我们的开发流程更长了,但是,这也是一个越来越规范化自动化的演变过程,类似这种问题慢慢地发展也会成为一个领域。

腾讯已成立了专门探索这一范畴的Oteam —— 前端联调体验Oteam,该Oteam将会结合文中提到的nohost、tolstoy,以及fastest、TSW等工具,全面打通联调体验。

发现产品机会点?试试用户分层

《动物森友会》如何以奖励设计让人喜喜爱爱?

带你了解腾讯最坚实的支撑事业群