关于前端:在千亿的成交额背后前端工程师是如何做资损防控的

2次阅读

共计 6802 个字符,预计需要花费 18 分钟才能阅读完成。

前言

资损 —— 顾名思义就是平台产生了与用户或客户心理预期不符、间接或间接产生经济损失的场景。

始终以来,资损问题就在咱们的生产环境中一直产生,而且随着业务的规模和国土不断扩大,经济损失的规模也在不断扩大,这间接对平台、客户和用户都产生了十分不良的影响。尤其在某一时间段间断产生高资损危险问题,顽劣水平回升到团体,对平台的生产和经营产生很高的负面影响,所以大家高度重视资损危险的防控。

本文心愿通过咱们的思考以及淘系双 11 的实际为大家提供一些资损防控的教训参考,也欢送大家提出贵重的意见。

(更多干货内容请关注【淘系技术】公众号,每日更新阿里工程师技术干货)

摸索之路

在资损防控方面,服务端比前端起步要早,而且做得也十分业余,比方各种离线或实时的容灾幂等查看、链路对账告警、要害配置巡检、要害标巡检等等。

然而对于前端而言,阿里淘系技术部是从 2019 年双 11 前夕开始才开始器重起前端资损防护问题,所以一年前并没有积淀什么产品化计划,过后能采取的伎俩就是对案例的总结、对问题的定义、对水平的定级、对红线的定论,通过一些规章制度、学习考试的伎俩来强化资损防控的文化意识,通过一些人肉盘点、case by case 的人工预演形式来躲避资损危险保障业务的稳固。比方下文,就是人工预演这个很间接的土办法的介绍。

人工预演

因为短少产品化伎俩,去年的双 11、双 12 等大促,前端采纳的都是人肉盘点、人工预演的土办法来做保障。咱们剖析了前端所有可能呈现的资损危险点,并制订了一套前端的专属资防规章制度和危险编码表,并围绕这些资损危险点,盘点所有参加双 11 的业务前端代码,对资损问题的辨认、预防、止血和复原过程进行具体的人工预演。

印象中 2019 年双 11,前端 C 端资损防控,总共进行 6 天,每天 2 场(下午与早晨),每场均匀 3.5 小时,大略 4 位评委,共进行 73 例预演,每例预演大略 15 分钟且至多 2 位预演者参加,消耗人力总成本均值在 200 小时(折算 25 人日),这个数字相比双 11 整体的人力投入程度来说,也是十分恐怖的。

通过上述形式,过程中咱们属实提前发现了一些资损危险问题,尽管最初每场大促线上都没有呈现资损问题,但这个土办法在过程中人力和工夫耗费还是十分恐怖的,而且防控成果如何齐全依赖过后现场的评委 review 的成果好坏。由此可见,人工预演这种形式不仅工夫和人力老本过高,而且防护成果无限,并不适宜作为长期的防控伎俩。所以,咱们向前摸索筹备通过一些产品化的伎俩来解决防控成果和老本问题,就有了如下的一些尝试。

前后端对账

如上文所提,受人工预演形式的老本、防控成果限度,咱们去年双 12 之后开始尝试资损防控的产品化方案设计。依据以往权利营销流动呈现过的资损案例来看,当消费者看到的权利信息与理论到账的权利信息不统一时,容易引起大面积客户投诉。譬如:

  • 某短视频业务在权利发放时,在消费者支付胜利单品优惠券时谬误地表达成了红包;
  • 某红包发放业务谬误地应用红包列表接口作为中奖后果表白,发现时长长达 10.5 小时

对于上述这类权利信息前台展现和后端发放不统一的问题,及时的监控报警对于止血、管制资损规模至关重要。为此,咱们针对相似问题为业务产品的生产阶段设计了一套 前后端对账 的产品化计划。

前后端对账的整体思路如下:

对账计划整体波及采集层对立接入、数据实时处理对账以及报警订阅,具体如下:

  • 接入层:前端封装对立的 SDK,笼罩 web、weex 和 miniapp,在页面端采集权利的要害信息;
  • 数据层:基于 Blink 进行数据实时处理,存储到 SLS 日志以及 METAQ 音讯,并通过后端平台订阅日志音讯进行实时对账;
  • 应用层:订阅权利的对账音讯并买通实时报警流程,以及通过 SLS 日志,查看权利的实时大盘以及模块治理。

然而,我的项目上线一段时间后,咱们发现成果并不如预期:

  • 一方面,因为前端 SDK 对业务代码是有肯定侵入性的,所以各方业务在接入前后端对账时,或多或少还是存在肯定的老本。尤其是对一些稳固的线上老业务,反而容易在革新时引入新的问题;
  • 另一方面,前端 SDK 采集的权利信息无奈间接从 UI 展示层辨认(金额可能被截断),从报警状况来看,发现的问题均是各方的业务开发同学上报错权利字段而非真的前后端权利不统一导致的误报。

从理论体现来看,咱们本来冀望用 前后端对账 的形式可能及时发现业务产品在生产阶段中权利信息在前端表白和服务下发不统一的状况,然而这套计划因为前端拿不到 UI 的利益点字段、问题发现率很低且存在肯定接入老本等起因,并不能满足咱们的需要,咱们只能持续摸索其余的资损防控伎俩。所以咱们把眼光聚焦到业务产品的研发阶段,看看是否从产品的研发阶段中摸索出一些资损预防的产品化伎俩进去,所以就有了以下的尝试。

动态代码扫描

在尝试 前后端对账 计划有余预期后,咱们开始从新思考:人工预演的形式能够帮咱们发现潜在的资损危险问题,但其次要问题在于须要投入大量人力和工夫老本,那么为什么不想方法升高这个老本呢?

为此,咱们从代码的 CcdeReview 过程中摸索出了一种 基于 AST(Abstract Syntax Tree,形象语法树)的前端代码动态扫描计划,能够在肯定水平上躲避金额计算、数字造假、数字歧义、文案过期等问题。这种机器代替人为 CodeReview 的形式,不仅省去了人力老本,而且还为 CR 的品质提供了一道基准保障。

动态代码扫描的整体计划思路如下:

其背地具体的原理介绍如下。

AST

在计算机科学中,形象语法树(Abstract Syntax Tree,AST)或简称语法树(Syntax tree),是源代码语法结构的一种形象示意。它以树状的模式体现编程语言的语法结构,树上的每个节点都示意源代码中的一种构造。

这是摘自百科上对 AST 的一段解释,咱们再来看一个 code ⇌ AST 互相转换的简略示例:

如上所示,代码片段 var str = "hello world" 被拆解成了多个局部,最终以一棵树的模式示意进去(如果想查看更多源代码对应的 AST,能够应用神器 astexplorer 在线尝试)。

代码扫描的根底正是建设在仓库代码的 AST 解析和遍历之上的,为此,咱们须要借助 Babel 来实现这部分工作

Babel

Babel 其实是一个 JavaScript 编译器,次要用于在旧的浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容版本的 JavaScript 代码。

简略来说,为了将 ES2015+ 代码转换成向后兼容版本代码(比方 ES5),Babel 每次都须要先将源代码解析成 AST,而后批改 AST 使其合乎 ES5 语法,最初再从新生成代码。总结一下就是 3 个阶段:

parse -> transform -> generate。

由上能够看到 Babel 岂但实现了 AST 的解析工作,而且因为其编译 js 代码的使命,它还提供了一套欠缺的 visitor 插件机制用于扩大。无关 『如何自定义 Babel 插件』,能够查看这份 Babel 插件手册。依据手册,咱们就能够应用以下代码增加自定义规定来实现代码扫描工作:

自定义规定

介绍完 AST 和 Babel 后,咱们再回到资损防控问题上来。依据以往的教训来看,前端容易造成资损 / 舆情的代码往往有:

  • 金额赋默认值
  • 金额计算
  • 数字造假、固定金额 / 积分
  • 过期时效文案

因而,咱们就能够根据上述这些 case 自定义规定来编写 Babel 插件。就拿 『金额赋默认值』 为例,咱们能够列举日常代码中的一些 bad cases,而后应用 astexplorer 剖析其 AST,最初再针对性地编写匹配规定即可。

case 1: 间接赋默认值

依据下面的 code vs AST 关系图能够看到,咱们只有找到 VariableDeclarator 节点,且同时满足 id 是金额变量,init 是大于 0 的数值节点这两个条件即可。代码如下:

case 2: ES6 解构语法赋默认值

察看下面的关系图,咱们能够得出结论:找到 AssignmentPattern 节点,且同时满足 left 是金额变量,right 是大于 0 的数值节点这两个条件。代码如下:

case 3: “||” 运算符赋默认值

上图的规定同样也不简单,但须要留神一点:理论代码中,= 右侧的赋值表达式可能会简单的多,甚至蕴含了一些逻辑运算。因而,咱们须要扭转策略:遍历右侧的赋值表达式中是否蕴含 “|| 负数 ” 的模式。代码如下:

通过上述的 3 个例子,咱们就曾经能把绝大部分 『金额赋默认值』 的代码给扫描进去。其余的一些场景也是如此,只有依据代码生成的 AST 找到法则,而后编写对应的 Babel 插件即可。

小结

然而,动态代码扫描工具尽管可能帮忙咱们从代码层面上发现一些共性问题,但面对不同的业务逻辑依然是没有感知的,以至于不容易挖掘出代码中深层次的问题。因而,在面对 UI、多态、简单交互逻辑等场景时,纯靠动态代码扫描不足以齐全解决问题。所以在第一道防护工序之后,咱们又设计了第二道防护工序,具体介绍如下。

UI 测试扫描

依据以往产生的资损故障来看,问题往往多产生在代码变更时,开发同学的新改变影响到了业务的原有性能,而测试同学又恰好没有回归到这点。对测试同学而言,业务每次的全量回归工作量是微小且反复的。就拿支付红包的例子来说,不同的账号(人群)、支付胜利、网络超时、反复支付、没有资格、服务故障等等都是资损通常须要思考在内的测试用例。此时,如果能有一个 UI 自动化回归测试工具,既能为业务提供一个保障,又能解放测试同学。然而,传统的 UI 自动化测试须要开发同学编写对应的自动化测试代码,岂但有上手老本,而且还造成了额定的工作累赘。

为此,咱们又提出一种 基于录制 / 回放的 UI 测试扫描计划:开发 / 测试同学只需提供一个可失常拜访的页面地址,失常的功能测试录成测试用例,在我的项目公布的时候就会进行一次页面回放,最初再通过 UI 测试用例快照比对的形式断定本次性能回归是否通过。

UI 测试扫描的整体思路如下:

其背地的原理介绍如下。

页面代理

代理页面实质是一个 web 服务,它通过 url 参数形式接管原始页面链接和注入脚本地址,由服务器申请原始页面返回对应的 html 文档,并且在返回文档头部注入接口拦挡脚本、调试工具脚本以及 url 参数中获得的自定义注入脚本。代理页面领有和原始页一样的 html,同时也会增加上原始页的 query 参数,间接拜访代理页除了 js 的 location 变量,其余环境和原页面相差无几。至于 location 变量,试了很多计划,发现都改写不了,一重写页面就跳转,屡次尝试无果后咱们发现能够换个思路解这个问题,既然重写不了咱们就替换掉它,咱们将页面所有的 script 脚本包了一层 with,如下图所示,咱们将页面内所有的 js 脚本的上下文改写,使之读取的 location 是被咱们重写的,从而达到代理页的渲染运行后果和原始页统一的成果。

录制脚本

通过上述的页面代理,咱们就能够在拜访原页面的时候注入咱们的录制脚本。为了可能反对页面的回放,录制的时候就要提供两份数据:

  • 录制期间产生的所有网络拜访数据
  • 用户操作数据(包含点击、滚动等)

要想记录网络拜访的数据并不难,此处不开展多述,能够借用 AOP 的思维,在网络申请的回调中减少一个 interceptor,同时保留下本次申请的 url,param,response,以便回放的时候匹配应用。

再来看下又该如何劫持用户的操作数据,其实在 h5 页面中,用户的任何触摸操作都会顺次触发 onTouchStart、onTouchMove、onTouchEnd 事件,所以咱们只需拦挡这三个事件对应的 targetEvent 参数即可。就拿拦挡 onTouchStart 为例:

如上能够看到,咱们记录下了触发事件的事件类型、节点选择器、工夫戳、页面间隔顶部高度以及坐标地位等信息,这些都是回放时必不可少的数据。

再来看页面的滚动拦挡,只有劫持 onScroll 事件即可。不过这里须要留神一点,页面滚动分两种:手指接触屏幕时的拖动,手指来到屏幕后的惯性滚动。前者会同时触发 onTouchMove 和 onScroll,而后者只会触发 onScroll。

回放脚本

回放脚本同样依赖代理页面的注入能力,但做的事刚好和录制脚本相同:

  • 拦挡申请,应用录制时记录的网络数据 mock
  • 依照时序顺次派发 touchEvent 事件

相似地,拦挡申请和录制时的原理大体一致,只需依据本次申请的申请参数从录制时拦挡的数据中找到对应的匹配即可。

再来看下如何依据时序派发 touchEvent 事件来模仿回放,外围代码如下:

派发事件是基于 dom 的 dispatchEvent 办法,能够参考 MDN 文档,代码如下:

快照比照

难点剖析

依据前文的介绍,咱们曾经能够别离拿到录制和回放的最初一帧快照,所以接下来咱们就心愿设计出一套算法对这两个快照做内容一致性的自动化判断。从不同的输出案例来看,咱们次要面临以下的几个难点:

  • 雷同文字在不同型号的手机上会有不同的字体字号显示,像素级比对会将雷同文字误判为不统一。
  • 对于红包等弹层图片,只需关注红包弹层信息是否统一,无关的背景会导致模型误判。
  • 因为回放 / 录制在工夫戳上无奈保障严格一致性,两张快照往往存在地位上的位移偏差。

算法设计

整体的算法流程如下图所示,上面咱们分步骤论述算法思路。

  1. 首先对两张快照计算 SIFT 类似度进行初筛。匹配的外围问题是将同一指标在不同工夫、不同分辨率、不同光照、不同方向的状况下所成的像对应起来。图像的部分特色,对旋转、尺度缩放、亮度变动放弃不变,对视角变动、仿射变换、噪声也放弃肯定水平的稳定性。类似度低于阈值的两张快照判为不通过,通过快照会做进一步精密比对。
  2. 针对弹层图片,须要事后对背景等无关信息做去除,仅保留弹层信息。这边先将图片从 RGB 空间转换成 LUV 空间。L 重量会保留图片的亮度信息,便于依据亮度值二值化图片,去除有效背景,成果如下。

     

  1. 将预处理好的洁净图片送到 OCR(光学字符识别) 模型,提取出文字内容及绝对应的坐标信息。针对像素过小的文字信息进行删除,往往是噪声产生的错误信息。
  2. 依照返回的坐标信息进行文字的地位还原,不便下一步做内容比对。
  3. 由上一步产出的后果进行内容比对,在图上标注出两张快照不统一的中央作为输入,算法完结。

成果演示

不通过:

.jpg”)

通过:

.jpg”)

.jpg”)

小结

诚然,咱们通过 UI 扫描工具扭转了传统编写 UI 测试代码的形式,测试同学只需在功能测试的时候顺便录制一份测试用例即可,这岂但升高了测试同学的自动化的学习老本和回归的工夫老本,而且还为每次业务的公布提供了一道自动化回归保障。不过,在本次双 11 前端资防工作理论落地中,咱们依然遇到了一些问题:

  1. UI 测试扫描目前临时只反对 h5,还不兼容淘系内的其余一些前端技术栈(比方 weex、直播、小程序等),导致这些业务依然只能通过人工 review 的形式保障;
  2. 咱们尽管对快照比照的算法进行了优化,但在理论利用中仍遇到一些因为算法判断不准导致误判的状况。因而,咱们还将持续优化快照比照算法,进一步晋升判断的准确度。
  3. 目前的快照比照只校验了录制和回放的最初一帧(即最终状态),大量的中间状态信息没有被利用起来,从而失落了过程的校验。因而,后续咱们将思考引入视频的比照算法,达到真正的录制 / 回放全比照。
  4. 基于目前的页面代理机制,录制性能只反对当前页的操作录制,对于页面跳转类的测试用例还无奈笼罩。因而,咱们接下来也将持续降级页面代理和录制 / 回放脚本,反对链路层面的测试用例笼罩。

以上遇到的这些问题均是咱们接下来持续重点冲破的挑战。

总结与瞻望

如前文所述,淘系的前端资防工作一年内悄悄变动着,从最后的人工预演到目前的三道递进式的产品化防控伎俩:

  • 【已产品化】针对能够在代码级别动态扫描剖析进去的资损危险问题,做了第一道产品化防控伎俩——资损 / 舆情危险代码扫描工具;
  • 【已产品化】针对 UI、多态、简单交互逻辑等不能从代码级别剖析进去的资损危险问题,与测试团队单干做了第二道产品化防控伎俩——资损 / 舆情危险 UI 测试扫描,通过 UI 测试用例快照比对预防资损危险;
  • 【兜底计划】针对上述产品化伎俩不能笼罩的非凡场景,临时先依赖人工预演作为兜底防护计划。

相比于去年的双 11 资防工作,往年咱们依附上述计划甚至勾销了人工预演的环节。预演者也从去年须要筹备相干文档(如止血计划、预案等)变成往年录制 UI 测试用例,其中须要筹备的工夫老本简直打平,但大大节俭了预演的参会工夫;除此之外,预防成果也因其范畴更聚焦、防护组合更全面,要比去年成果更佳。当然了,目前的这些计划都还只是预防伎俩,无奈百分之百保障线上不会产生资损故障,每个人对于资防的态度仍不能漫不经心。

在接下来的工作中,思考到目前的资防计划仅能做代码缺点方面的预防,产品设计、经营配置等方向还没有实质性的防控办法,所以后续咱们将在构思链路级别的、生产环境以及经营环境上的防控伎俩,建设一些告警和主动止血机制为平台保驾护航。

(更多干货内容请关注【淘系技术】公众号,每日更新阿里工程师技术干货)
正文完
 0