去哪儿网系统防腐化治理实践

7,592 阅读15分钟

作者介绍

李佳奇

14年加入去哪儿,目前担任技术总监,负责机票主系统后服务和用户体验技术团队,同时担任业务架构SIG负责人,机票架构组负责人,在复杂系统架构设计, DDD落地,高并发高吞吐系统设计等领域积累大量经验。    

1.背景

2022年,Qunar 技术中心完成了轰轰烈烈的服务瘦身项目,在各个技术团队的努力下,最终完成了将上千个应用和千万行代码瘦身50%的目标。

在如此艰巨的任务达成目标之后,对于技术团队来讲,接下来的重点就变成了如何维持住这来之不易的成果,如何避免反弹。作为一家处于激烈竞争的互联网公司,Qunar 致力于不断为用户提供低价的产品,同时维持优秀的服务水平和用户体验,业务的演进和系统的迭代在持续不断的进行。随着业务的演进和系统的迭代,原本瘦下来的系统上会有新的代码填充进来,也会有新的应用加入或者拆分出来,这些动作都是会重新增加系统的臃肿程度,如果没有治理手段,会导致我们好不容易取得的瘦身成果慢慢流失。就像一个成功瘦身的人,如果瘦下来之后没有制定和执行合理健康的饮食计划和运动计划,迟早又会回到身体臃肿的状态。

基于此,在瘦身项目推进的同时,Qunar 技术中心启动了系统防腐化治理的项目,目的是在瘦身项目达成目标之后,建立和运用治理手段,防止或者减缓系统不受控制的膨胀,将系统的维护成本维持在较低的水平。这项工作由作者牵头,Qunar 技术中心业务架构 SIG 承接。

其实要做到这一点并不是一件可以一眼看到路径和方法的事情。首先应该如何确定工作方向,此时我们手上只有一个已知目标,就是巩固住瘦身成果,但是如何达成这个目标是不明确的,是需要我们尽快思考清楚的。在思考接下来的工作方向时,我们注意到,既然是为了巩固瘦身成果,那么我们做的瘦身动作的逆向操作所产生的影响,就对应了系统的膨胀,我们只要能够管理好这些逆向操作,就能够对抗系统膨胀的趋势。那么,会造成系统膨胀的动作有哪些呢?上面的已经提到过了,比如代码的增加,应用的添加和拆分,但是这里要注意,如果为了阻止系统膨胀而完全禁止代码添加和应用增加,那么就是因噎废食,舍本逐末了,业务要发展,系统要优化,必定会带来代码增加和应用增加。我们要管理的应该是无效或者低效的代码增加,以及无效或者低效的应用添加和拆分。而无效低效的代码和无效低效的应用带来的都是系统的腐化。因此我们就把巩固瘦身成果这一目标的实现方向确定为防止系统的腐化。

明确了防止系统腐化这个工作方向后,接下来的工作要解决的问题就变成了:

  1. 如何定义腐化
  2. 如何测量腐化
  3. 如何治理腐化

2.如何定义腐化

2.1 腐化指标识别和模型参考

如何定义腐化要解决的是腐化指标识别的问题,腐化指标的识别是一个由虚向实的过程。在寻找这些指标的过程中,我们发现我们在沟通系统腐化时,使用到的语言大部分是在描述系统的复杂度,因此我们决定使用系统复杂度来描述系统腐化,用系统复杂度指标来定义系统腐化。

所以接下来在定义部分的工作就是建立系统复杂度的评估模型。而建立复杂度评估模型,本质上是在完成下面的公式: 图片 也就是确定每个复杂度维度对应的复杂度计算值 factor k,确认每个复杂度维度对应的权重 weight k,然后计算各维度结果之和。

在对系统复杂度模型建模的过程中,我们尝试寻找业内的相关资料,但是业内公开资料中涉及到系统复杂度建模的资料并不是很多,这里给读者列举一些我们在过程中参考并且受益的相关资料。

参考的几个模型介绍:

The Open Group Conference 2015 中给出的复杂度评估模型

图片

该模型可以用于评估技术架构复杂度、应用架构复杂度以及业务架构复杂度。该模型给我们带来的参考意义在于,其描述了一种2*2维度来定义复杂度的方法。

图片

如上图所示,描述复杂度可以从被评估对象中元素和关系两个维度入手,每个维度可以通过其数量和分类(变化程度)这两个维度的数据来进行复杂度的定义。

国防科技大学学报41卷第1期-系统复杂性及度量

图片

国防科技大学的这篇论文主要提供了区分复杂度概念的方法,我们可以从系统的功能、系统的结构、系统的类别等方向来描述一个系统的复杂度,同时描述系统内部逻辑本身的复杂度也是可以纳入模型的指标。

McCabe度量法

图片

熟悉软件工程测试的读者应该对此比较熟悉,上图所描述的方法又被称为环路度量法,是一种基于程序控制流的复杂度计算方法。当我们把程序流程看作强连通有向图时,可以下面的公式来计算其复杂度:

V(G) = M-N+2。其中 M 为图中边的个数,N 为图中顶点的个数。

读到这里的读者可以简单计算下上图的环路复杂度是多少。 McCabe 度量法后续又补充了更多的度量方法,也为我们提供了有价值的参考,这里简单罗列一下:

  • 圈复杂度
  • 基本复杂度
  • 模块设计复杂度
  • 集成复杂度
  • 行数
  • 规划复杂度
  • 全局/局部/病态数据复杂度

以上是我们在做自己的复杂度建模探索时,参考的相关模型和资料,但是由于业内相关资料不多以及成熟落地案例不多,因此大部分工作还是要我们自己进行探索。

2.2 Qunar复杂度模型建模过程

接下来介绍下 Qunar 系统复杂度建模中所做的工作。

我们对于系统复杂度的定义是:系统复杂度是指系统在自身描述、迭代、集成、维护、保证可控等方面的困难程度。

一个系统的复杂度可以从以下维度类别来进行描述:

  • 外部客观诉求
  • 系统自身特征
  • 系统所处链路
  • 系统集成难度
  • 复杂度影响表现

我们对系统复杂度建模过程就是复杂度维度的选取和计算方法的确认过程。

那么该如何进行复杂度维度的选取呢?基于这个问题我们有以下3点考虑:

  1. 模型通用性——由于我们是针对整个 Qunar 技术中心的系统进行复杂度建模,因此模型需要兼顾各类业务场景下的各类系统,模型中包含的指标应该足够通用化,避免使用带有特定场景特征的指标。
  2. 指标可测量性——模型是要服务后续的治理动作的,因此模型中包含的指标一定要能够实际可测量,要充分了解并考虑到目前技术基础设施层面对于系统各项指标测量的支持能力。
  3. 可解释性——模型中每个指标及其计算方法要充分可解释,满足各技术团队的接入需要,尽量减少技术团队对于模型的疑议。

因此,为了满足以上考虑点,我们在指标选取的过程中采用了如下步骤:

图片

简单解读下上面的步骤。首先,通过头脑风暴的方式,让业务架构SIG中各个业务线的技术同学根据自身业务场景和系统现状,发散思维,提供自己认为的可以作为复杂度指标的维度,这要做的目的是充分保证指标的丰富性,为后面能够得到足够通用的模型打下基础。在通过头脑风暴得到足够丰富的信息之后,接下来将大家贡献出来的内容识别成一个个维度,识别的标准是可以独立定义并测量的指标。接着,将这些识别出来的维度进行筛选,过滤掉具体业务特性的,不合理的,高度近似的维度。经过筛选后的维度需要进行精简,因为我们认为,一个模型最好的状态是只有一个指标,如果能够找到全面且唯一的指标是我们这个模型的最优解,在精简的过程中,我们进一步得到了彼此独立、能够测量、和复杂度高度相关的一组正交维度。

经过以上步骤,我们识别出了29个维度,经过筛选和精简最终我们得到了8个复杂度相关维度:

图片

这里需要为大家说明的是,在这8个维度中,有6个复杂度核心指标和2个辅助指标。辅助指标的意义在于,当我们衡量一个系统的复杂度是否合理时,需要结合辅助指标来判断合理性。比如,和业务重点方向(Epic)关联程度高的系统,对于复杂度的容忍上限就会高一些,同理,处于核心链路路径上的系统,具备相对高一些的复杂度也是合理的。

确定维度之后,下一个问题是维度的量化。通过上面的维度表格可以看到,我们选取的复杂度指标的量纲和量级差异比较大,一个系统的代码行数可能会有几万或者十几万,但是依赖数量可能是百级别或者几十这样的级别。量纲和量级的差异会导致这些指标难以进行加和。因此,需要对这些指标进行归一映射,我们将这些指标按照一定的算法映射到了[0,10]的区间。

同时,上文对于公式的描述中还提到了权重因子 weight k 的确认,根据维度含义和特性的不同,我们赋予了不同的权重。

需要特别说明的一点是,由于我们的模型指标中包含有效代码行数和代码总行数,为了保证这两个指标能够在代码覆盖率提升的过程中是收敛并复杂度应处于下降趋势,我们又引入了代码覆盖率因子加入到权重中。

建模到这一步,已经很接近得到最终模型了。但是,在此之前,我们还需要思考一个问题:所有应用都通过这个模型计算出来复杂度然后放到一起对比是合理的吗?会不会存在一些不在核心链路但是仍然很重要的系统比如一些配置类系统无法充分评估其复杂度?

我们对这个问题思考的结论是:

  • 所有应用放到一起对比是不合理的,应该结合具体对应的业务一起来对比
  • 在应用和业务之间需要建立一个系统层,由若干应用组成一个系统来与业务产生关联,在进行复杂度计算时已系统作为基本单位
  • 应用是动态变化的,而业务和系统相对稳定
  • 通过系统这个概念,可以把核心应用和非核心应用放到一起参与评估

所以我们明确下应用和系统的概念,如下图:

图片

应用 A/B/C 组成系统 D,在进行复杂度评估时,将以系统 D 为基本单位,系统 D 的复杂度为应用 A/B/C 的复杂度之和。

2.3 建模结果

在完成上面的步骤之后,我们得到了最终的系统复杂度模型:

图片

整个模型比较复杂,读者可以点击图片查看大图。为了方便阅读理解,用不同颜色的框圈出了模型的不同部分。红颜色的框代表2个辅助指标,蓝颜色的框代表6个核心指标,粉颜色的框代表归一规则。

3.系统复杂度看板落地

通过上面的建模工作,我们得到了衡量系统复杂度的模型,接下来要做的事情是将模型进行数字化落地并实际应用到技术中心的系统测量中。

在这部分,我们主要做了下面三件事:

  • 搭建业务域管理平台,进行元数据管理和看板建设
  • 基于 Qunar 的应用管理平台进行应用和系统绑定
  • 实现模型的计算模块,对接到看板

相关的能力和系统如下图:

图片

在落地的具体过程总,秉持着不重复造轮子以及 Qunar 自身完善的技术基础设施,我们幸运地发现模型中各项指标的数据采集工作都已经通过各个基础设施能力实现了。在数据层上,我们充分利用了现有的基础设施,通过不太高的数据是配成本获得了模型需要的原始数据:

图片

其中:

  • JIRA 提供应用和业务重点项目 Epic 之间的关系
  • 发布系统提供应用和系统的绑定关系
  • Sonar 提供应用代码静态数据
  • Tracing 负责系统内外应用的链路解析和数据提供
  • 混沌入口基于混沌演练配置提供各业务和系统核心入口
  • 线上覆盖率系统提供代码覆盖率等模型计算需要的核心数据

基于上面数据采集和计算模块的实现,我们最终上线了系统复杂度管理系统:

图片图片图片

通过系统复杂度管理系统,我们对业务域、业务域下的系统以及系统中具体的应用的复杂度进行查询和追踪,使得我们接下来的防腐治理机制的实施成为可能。

4.防腐治理机制的实施

有了上面的建模和落地,我们得到了系统复杂度查询和跟踪的管理系统,接下来为了实现系统防腐的最终目标,我们结合 Qunar 技术中心应用的历史增长数据,制定了以年为周期的复杂度增长治理机制。

我们划定的增长率上限:10%/年,分摊到每个月是0.85%

计算方式是:代码行自然增长率*代码行维度指标在模型占比

同时我们选择的基线是22年底瘦身项目完成后的应用数据。

制定了基线和增长控制目标之后,接下来我们设计了系统腐化检测机制:

图片

检测机制会每日更新系统的最新复杂度数据,然后结合上面的增长率上限进行是否超限判断,一旦超限会被确认为系统正在腐化,会通过和我们的项目管理系统 JIRA 平台进行自动整合来创建腐化治理任务并设定完成日期。若对应技术团队未在完成日期前完成腐化治理,则会纳入到 Qunar 用于评估系统质量的防腐指标中并进行周知和升级。

5.成果和总结

基于以上机制的建立,Qunar 系统防腐项目目前的成果如下:

  • 目前防腐机制纳入了 Qunar 技术中心的1800+应用,涉及28个业务域和120个系统
  • 技术团队覆盖技术中心9个三级部门
  • 触达治理计划4个,完成3个
  • 目前整体复杂度增长控制在1.14%,在合理水平

Qunar 系统防腐这个项目从一个比较难以找到抓手的目标开始,通过对目标的分析,问题的转化和进一步明确,通过参考相关资料和团队自身的创造力和技术思维能力,最终得到了能够在 Qunar 落地的系统复杂度模型,并运用到系统防腐治理中。希望读者能够通过本篇文章获得一些启发,能够让读者能够通过本篇内容有所收获,不论是直接得到一个系统复杂度的评估模型,还是对于自身团队中类似问题的解决方法的参考,对于笔者来说都是非常有意义的事情。