代码质量检测工具服务端设计
2021-12-28
TL;DR
今年很多时间都在开发和维护代码质量检测工具,到现在已经稳定运行了两个多月。之前写过它的 cli 端,本文是关于支撑该 cli 端以及质量报告平台的服务端的设计。它由设计文档简化精炼而来,作为个人记录和回顾所用。代码质量检测服务端开发的那段时间是我非常高效、聪明、有激情的一段时间,我很感谢它带给我的快乐。每个软件开发工程师在自己的编码生涯里都会有自己的代表作,它不一定有多厉害。你报之它以你的时间、智慧、苦恼,它报之你以喜悦和成就感,它让你感知到自己的能力和成长,让你在某些场合下嘴里滔滔不绝,眼里四射光芒。我喜欢这样的作品,我喜欢这样的过程,我喜欢这样的回忆。
需求分析
如果有阅读过之前写的代码质量检测工具可能会对 bcode 有印象,但这里我还是简单描述一下:代码质量检测工具的起点是一个命令行工具,安装且在代码目录下执行 bcode check [文件(夹)]
后会扫描代码并生成一系列报告文件提供给服务端,服务端承载后续的工作,同个项目的扫描记录和数据分析会以报告汇总的形式在 Web 平台上查看。
如下导图所示,需求按照三种场景来进行分析。
由于无法从内网捕获截图演示,这里对标注的几点进行扩展解释,序号和导图中一致。
为了让工具更好的自动化,此工具将和 CICD 结合使用。然而许多产品线的流水线环境中 Node 版本过低,因此提供了远程扫描功能。
bcode 的使用很简单,一行命令即可。但会存在较复杂的场景,比如某些文件要忽略检查;甚至每个维度都有单独的忽略文件规则,因此为了方便使用,可以将这些规则定义在约定的配置文件(bcode.config.js)中,执行命令时工具会去寻找和读取该配置。我们在 web 平台上项目设置里也提供了这些参数的设置,因此这个配置文件省略,每次工具扫描时会从服务端获取该项目的配置。(还是很贴心的吧)
bcode 有个维度是拼写检查,那么有很多单词是词典非法但大众默认合法的(比如 nums),所以有项目词库这种需求出现。每次扫描时服务端会下发单词词库,cli 端扫描时会将这些单词当作合法单词。
扫描结束后,会根据配置的通知方式将报告结果进行通知。
将需求进行收敛后,得到如下的功能点:
大部分功能都是比较简单的,下面内容聚焦在几个主要的功能模块上。
整体静态结构
模块 | 说明 |
扫描模块 | 主要处理扫描相关的工作:
|
词库管理 | 主要处理词库相关是事情:
|
项目管理 | 主要工作是:
|
用户管理 | 处理用户登录、记录用户信息等 |
定时任务 | 所有的定时任务在这里注册和处理:
|
流程设计
扫描整体流程
扫描分为本地扫描和远程扫描两种场景:
- 本地扫描:用户使用 cli 扫描,将扫描结果文件上报服务端处理。
- 远程扫描:用户上传源码到服务端,服务端在服务器上模拟本地扫描。
静态结构
处理流程
序号 | 说明 |
1 | 远程扫描和本地扫描的判断,已在 4.2.1远程扫描方案选型中讲述,通过 body 参数来判断 |
2 | 进行远程扫描,详细流程在 4.4.2 中讲述 |
3 | 远程扫描本质是在服务端模拟本地扫描,因此此处相当于本地扫描完成,又将上传扫描结果文件到服务端 |
4 | 该结束流程是指扫描任务结束,数据分析等都已完成,用户可以在平台上通过编号查询报告。消息通知就无需用户等待了。 |
5 | 触发消息发送事件,如果需要发送消息通知,则发送消息 |
远程扫描流程
静态结构
处理流程
序号 | 说明 |
1 | 远程扫描使用 bcode 库(cli)来扫描,这里采用的方案是在源码目录下创建一个扫描脚本,通过 fork 子进程的方式执行该脚本扫描 |
2 | 远程扫描的任务会加到任务队列中,每一个任务有唯一的任务 id |
3 | 为了避免客户端持续等待扫描完成,我们可以先将任务 id 返回给客户端,结束本次请求。客户端后续可以通过任务 id 查询任务完成状态 |
4 | 开启子进程,执行扫描脚本 |
5 | 给子进程发送扫描配置数据,扫描配置数据是 cli 端在执行命令行时的参数信息 |
6 | 扫描任务完成后,更新记录在 Redis 中的任务 id 的信息,此时客户端通过任务 id 查询时就可获悉任务结束 |
关键算法描述
之前在这篇文章有记录。
结果处理评分流程
静态结构
处理流程
平台词库自动维护流程
平台词库出现的目的是为了解决:
- 提高项目冷启动(第一次使用)时拼写检查扫描的准确度
- 减少项目需要上报的合法词汇的数目
服务会在两个地方进行平台词库的数据维护:
- 平台词库模块启动时
- 有项目词汇上报请求时
- 有项目词汇删除请求时
处理流程
序号 | 说明 |
1 | 词汇管理模块启动时开始工作,它只会工作一次 |
2 | 当有词汇上报请求时开始工作 |
3 | 在服务投入生产之前,先通过扫描成熟的大项目,已经提取出了许多合法词汇以配置文件的形式放置在项目中 |
4 | 平台词库的整理操作:
|
5 | 当有项目词汇删除时,我们需要对删除词汇进行判断,如果该词汇被删除后不再满足平台词汇中存在的条件(至少3个项目存在),则需要把它过滤出来 |
6 | 将 5 中的词汇从平台词库中删除 |
各种机制设计
可调试性设计
主要依托以下几种方式提高应用的可调式性。
机制一:日志齐全、分级、输出内容可以动态调节。
在应用的各个关键阶段,均有日志输出。
日志分级和携带标识
日志分为以下几个级别:
- 普通日志
- 告警日志
- 错误日志
- 调试日志
此外,日志输出应该携带有标识,这样可以方便我们进行日志过滤,以及特定的追踪某一次扫描的问题。标识分为两种:
- 携带有模块名
- 如果是属于扫描模块,携带有扫描任务编号
- 日志输出可以调节
日志的输出可以通过环境变量来控制,这样可以在排查问题时排除其他日志的干扰。
机制二:上传的文件保留7天,可复现问题。
上传的扫描结果或源码文件均会在服务器上保留 7 天,因此如果某个扫描出问题的话,可以拿到当时的文件信息,通过 postman 等请求工具模拟当时的过程进行问题复现。
机制三:模块之间界限要清晰,数据流明确
1.架构上:由于 Web 服务框架使用的 Nest.js ,本身分层非常清晰,从数据到异常都会有对应层进行处理; 2.业务上:服务中的处理函数尽可能抽成纯函数,让执行过程和数据流清晰。
可测试性机制分析及设计
- 核心函数抽成单个的纯函数,方便单独拿出来测试或进行单元测试
- 服务都以 API 的形式对外,可以用 postman 等工具发送请求进行测试
- 不同维度的检查和数据处理都拆成了单个函数,同时可以通过标识符进行检查与否的控制,因此很方便对单个维度进行测试
- 不同的业务拆分成不同的服务进行模块化,该服务只做自己的事情,要进行其他服务操作需要调用 API 。
- 有效的日志:分级、动态开关
以上就是一个代码质量检测平台的主要模块设计,由于篇幅和其他原因这里没有涉及到数据库的设计,在实际开发过程中细节还是比较多的。
代码质量检测服务端开发的那段时间是我非常高效、聪明、有激情的一段时间,我很感谢它带给我的快乐。每个软件开发工程师在自己的编码生涯里都会有自己的代表作,它不一定有多厉害,你报之它以你的时间、智慧、苦恼,它报之你以喜悦和成就感,它让你感知到自己的能力和成长,让你在某些场合下嘴里滔滔不绝,眼里四射光芒。我喜欢这样的作品,我喜欢这样的过程,我喜欢这样的回忆。