CocaColf

庭前桃李满,院外小径芳

代码质量检测工具服务端设计

2021-12-28


TL;DR

    今年很多时间都在开发和维护代码质量检测工具,到现在已经稳定运行了两个多月。之前写过它的 cli 端,本文是关于支撑该 cli 端以及质量报告平台的服务端的设计。它由设计文档简化精炼而来,作为个人记录和回顾所用。代码质量检测服务端开发的那段时间是我非常高效、聪明、有激情的一段时间,我很感谢它带给我的快乐。每个软件开发工程师在自己的编码生涯里都会有自己的代表作,它不一定有多厉害。你报之它以你的时间、智慧、苦恼,它报之你以喜悦和成就感,它让你感知到自己的能力和成长,让你在某些场合下嘴里滔滔不绝,眼里四射光芒。我喜欢这样的作品,我喜欢这样的过程,我喜欢这样的回忆。


需求分析

    如果有阅读过之前写的代码质量检测工具可能会对 bcode 有印象,但这里我还是简单描述一下:代码质量检测工具的起点是一个命令行工具,安装且在代码目录下执行 bcode check [文件(夹)] 后会扫描代码并生成一系列报告文件提供给服务端,服务端承载后续的工作,同个项目的扫描记录和数据分析会以报告汇总的形式在 Web 平台上查看。

如下导图所示,需求按照三种场景来进行分析。

需求分析

由于无法从内网捕获截图演示,这里对标注的几点进行扩展解释,序号和导图中一致。

  1. 为了让工具更好的自动化,此工具将和 CICD 结合使用。然而许多产品线的流水线环境中 Node 版本过低,因此提供了远程扫描功能。

  2. bcode 的使用很简单,一行命令即可。但会存在较复杂的场景,比如某些文件要忽略检查;甚至每个维度都有单独的忽略文件规则,因此为了方便使用,可以将这些规则定义在约定的配置文件(bcode.config.js)中,执行命令时工具会去寻找和读取该配置。我们在 web 平台上项目设置里也提供了这些参数的设置,因此这个配置文件省略,每次工具扫描时会从服务端获取该项目的配置。(还是很贴心的吧)

  3. bcode 有个维度是拼写检查,那么有很多单词是词典非法但大众默认合法的(比如 nums),所以有项目词库这种需求出现。每次扫描时服务端会下发单词词库,cli 端扫描时会将这些单词当作合法单词。

  4. 扫描结束后,会根据配置的通知方式将报告结果进行通知。

将需求进行收敛后,得到如下的功能点:

功能点

大部分功能都是比较简单的,下面内容聚焦在几个主要的功能模块上。

整体静态结构

整体静态结构

模块说明
扫描模块主要处理扫描相关的工作:
    1. Cli 扫描上报的文件处理
    2. 扫描数据处理
    3.远程扫描
词库管理主要处理词库相关是事情:
    1.词库上报、删除
    2.项目词库同步到平台词库
项目管理主要工作是:
    1.将相同扫描项目的记录汇总
    2.处理项目的扫描配置
用户管理处理用户登录、记录用户信息等
定时任务所有的定时任务在这里注册和处理:
    1.文件的定期清理

流程设计

扫描整体流程

扫描分为本地扫描和远程扫描两种场景:

静态结构

扫描静态结构

处理流程

整体扫描流程

序号说明
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. 减少项目需要上报的合法词汇的数目

服务会在两个地方进行平台词库的数据维护:

  1. 平台词库模块启动时
  2. 有项目词汇上报请求时
  3. 有项目词汇删除请求时

处理流程

平台词库维护处理流程

序号说明
1词汇管理模块启动时开始工作,它只会工作一次
2当有词汇上报请求时开始工作
3在服务投入生产之前,先通过扫描成熟的大项目,已经提取出了许多合法词汇以配置文件的形式放置在项目中
4平台词库的整理操作:
    1.平台词库的整理是异步的,通过事件触发。所以在该步骤之前,先响应用户的请求,再整理词库
    2. 如果上面流程传来的词汇是数组形式,则该步骤的后续操作需要循环数组中每个词汇进行处理
5当有项目词汇删除时,我们需要对删除词汇进行判断,如果该词汇被删除后不再满足平台词汇中存在的条件(至少3个项目存在),则需要把它过滤出来
6将 5 中的词汇从平台词库中删除

各种机制设计

可调试性设计

主要依托以下几种方式提高应用的可调式性。

机制一:日志齐全、分级、输出内容可以动态调节。

  1. 在应用的各个关键阶段,均有日志输出。

  2. 日志分级和携带标识

日志分为以下几个级别:

此外,日志输出应该携带有标识,这样可以方便我们进行日志过滤,以及特定的追踪某一次扫描的问题。标识分为两种:

  1. 日志输出可以调节

日志的输出可以通过环境变量来控制,这样可以在排查问题时排除其他日志的干扰。

机制二:上传的文件保留7天,可复现问题。

上传的扫描结果或源码文件均会在服务器上保留 7 天,因此如果某个扫描出问题的话,可以拿到当时的文件信息,通过 postman 等请求工具模拟当时的过程进行问题复现。

机制三:模块之间界限要清晰,数据流明确

1.架构上:由于 Web 服务框架使用的 Nest.js ,本身分层非常清晰,从数据到异常都会有对应层进行处理; 2.业务上:服务中的处理函数尽可能抽成纯函数,让执行过程和数据流清晰。

可测试性机制分析及设计

  1. 核心函数抽成单个的纯函数,方便单独拿出来测试或进行单元测试
  2. 服务都以 API 的形式对外,可以用 postman 等工具发送请求进行测试
  3. 不同维度的检查和数据处理都拆成了单个函数,同时可以通过标识符进行检查与否的控制,因此很方便对单个维度进行测试
  4. 不同的业务拆分成不同的服务进行模块化,该服务只做自己的事情,要进行其他服务操作需要调用 API 。
  5. 有效的日志:分级、动态开关

    以上就是一个代码质量检测平台的主要模块设计,由于篇幅和其他原因这里没有涉及到数据库的设计,在实际开发过程中细节还是比较多的。

    代码质量检测服务端开发的那段时间是我非常高效、聪明、有激情的一段时间,我很感谢它带给我的快乐。每个软件开发工程师在自己的编码生涯里都会有自己的代表作,它不一定有多厉害,你报之它以你的时间、智慧、苦恼,它报之你以喜悦和成就感,它让你感知到自己的能力和成长,让你在某些场合下嘴里滔滔不绝,眼里四射光芒。我喜欢这样的作品,我喜欢这样的过程,我喜欢这样的回忆。

Comments: