一个有意思的点子
前言
前端基于运行时检测问题、定位原因、预警提示的可行性思考🤔
背景
部门要治理线上故障过多的问题,故障主要分后端异常以及前端的性能问题和业务问题。我们讨论的范围是前端业务故障,经过分析可以把它们大致分为UI、业务、工程技术、日志、三方依赖等。
首先要确定降低故障的指标,MTTI和MTTD是关键,因为只有及时发现和定位问题才能快速消灭它们。我们暂时没有统计线上MTTI、MTTD的数据,因为缺乏相关的预警所以问题的发现和定位耗时通常很久,下面的一些复盘统计显示发现问题的时间可以持续甚至好几个月。
这些问题中UI和业务异常占比超过80%,这些问题发现的不及时,一方面是问题本身不会阻碍核心链路,另一方面说明团队对业务的稳定缺少监控手段。
现有开发流程已经包含了Design QA验收交付的UI是否符合预期;开发工程师会和QA工程师一起执行Test Case验证业务的稳定并且在CI环节还有UT的保障。既然如此那为什么线上还是会不可避免的出现故障呢?
问题归因
在Dev和Stage阶段的验收能发现和处理绝显而易见的异常,但是这些验收的场景是有限的
- 开发环境数据集的局限
- 考虑到AB因素的影响,很难做到全场景全业务覆盖。
- 开发过程可能会无意间影响到其他业务,但是我们的注意力集中在现有的业务,也就导致问题难以被发现
所以归根到底,验收环节中数据和场景的局限以及人治导致一些Edge case被遗漏。
解决方案
我们该如何解决数据和场景的局限呢?这个其实通过Monkey和数据流量回放就能解决。
运行时阶段包含了所有业务和代码的上下文,所有在这个阶段发现问题、分析原因并预警效率是最高的,人治的问题可以得到改善。下面是这种机制执行的思路
- 自动化测试时,通过流量回放的形式模拟线上的数据和环境尽可能多的覆盖场景。
- 运行时阶段,前端通过UT代码验收业务(🤔UT只能在Unit Test阶段执行,运行时执行的原理后面会讲)
- 运行时阶段,分析UI元素间的关系并探测异常问题
方案实现
方案实现仅讨论前端的部分。
UI和业务检测比较通用,因为它们的判别条件是客观的。比如我们完全可以复用UT代码检测业务。
自动检测、定位原因、预警
这个机制实现没有困难。考虑到自动检测的范围在不同项目中不尽相同,所以实现的思路可以是插件的形式,模块间通过协议解耦。
主要功能模块有:
- 告警模块
- 日志生成模块
- 业务注册模块(接收业务自定义的检查日志)
- 内嵌的UI检测模块
UI检测
业务不同,遇到的UI问题会有差异,这部分需要具体问题具体分析,所以不做过多讨论。针对我们业务的现状Overlap、Truncate、Clip在UI中占比较高。我的做法是对显示的视图按多叉树遍历到叶子节点并分析子节点和兄弟节点间的关系,找到Overlap、Truncate、Clip问题。具体的实现可以参考代码LensWindowGuard.swift:31
业务检测
UT代码从逻辑上可以被分为三个部分:
- Give
- When
- Then
Given表示输入的数据,可以是真实接口也可以是Mock数据。
When表示调用业务函数,同时这里会产生一个输出结果。
Then表示检验输入的数据是否和输出的结果匹配,如果不匹配会在UT环节报错。
业务代码从逻辑上可以被分为两个部分
- Give
- When
Given可以是上下文的变量也可以是API调用
When表示执行业务的代码块
如果把UT的Then引入到业务的函数里,就可以实现运行时执行UT的效果了😁。
将UT代码引入到业务函数中,思路是首先把UT按照Given、When、Then三部分拆分,把Given和Then部分独立出去,独立的部分通过代码插桩的方式在UT和业务代码中复用。
不过到这里遗留了几个问题,暂时还没有太好的思路🧐
- 异步回调 - 业务代码或者UT检测逻辑只能执行一个
- 业务方法名的修改 - 使用Swift Macro插桩方式等同新增加一个全新的方法,所以运行时执行UT需要更改业务逻辑
- UT代码被执行多次 - 上面的图可以看出来,新的UT代码被同时插到旧的UT和业务代码中,所以执行UT时会UT逻辑被执行了两次
代码插桩
我们项目基于Swift,且插桩需要有判别逻辑,基于此选用AST的方式在编译期修改FunctionDecliation,把代码通过字符串的形式插到function body的合适位置。正巧2023 WWDC苹果发布了Swift Macro,其中的@Attached(Peer)正好可以满足对FunctionDecliation可编程修改,通过实现自动以@Attached(Peer)以及增加DEBUG宏(Swift Macro不能读取工程配置信息)可以改变UT的流程。真想说Swift太香了。
最终
完整的项目的整体架构大致如下,主要分了三部分。
- Hubble - 主要职责是提供基础协议、日志、预警和一些协助分析问题的工具集
- HubbleCoordinator - 主要职责是作为业务和组件交互的中间层,业务相关的逻辑都放在这里,隔离业务和Hubble
- 业务 - 仅处理Hubble初始化工作,对业务代码没有任何侵入
来源:juejin.cn/post/7274140856034099252