如何度量微服务系统的复杂性

如题所述

第1个回答  2022-07-29
当谈到使用DDD划分微服务的好处的时候,经常会说DDD能够让相关的业务逻辑更加内聚,并且降低服务之间的耦合性,从而最终实现达到降解系统的复杂性。但是在这里,不论是高内聚,低耦合,甚至我们经常说的系统复杂性,我们有没有一个客观的可以量化的指标来衡量这些概念。由于无法量化,所以就没法度量,这样当团队在讨论一个系统是否复杂,有多复杂这些问题的时候,就很容易陷入各种主观直觉的争论中。

为了能够让团队不再陷入这种无意义的争论中,我尝试查找看看有没有大牛深度的分析过这问题,最好还能是在计算机和互联网出现以后的分析和思考。非常幸运的是我找到了这本书《复杂》,这是一本研究复杂系统的通识著作,作者在书中尝试回答这些复杂系统着迷而令人费解的问题:

这篇文章基于这本书的思想框架,尝试能够给出一个度量微服务系统的方法。

先看看在自然界中有哪些复杂系统:一个上百万只蚂蚁组成的蚁群,由神经元组成的人类大脑,生物体内的免疫系统,当今世界的经济体系,还有我们现在已经离不开的互联网。

这些系统在细节上不一样,但是如果从抽象层面来看由很多共性:

对于一个微服务系统来说,上面三个特性同样适用。一个微服务系统由很多个微服务组成,这些微服务的行为产生出复杂的行为模式。整个微服务系统会通过API利用内部或外部的信号,同时在系统内部也是通过服务之间的接口进行信息传递。一个微服务系统并不是静态的,而是会不断适应业务变化,改变系统的API或者系统内部的组织方式来增加生存的机会。

以上三点是一个微服务的复杂特性,那么如何从量的角度来衡量微服务的复杂程度呢?两个微服务系统都很复杂,那么如何说一个比另一个更复杂呢?这是一个很重要也很困难的问题,现在在科学界对于复杂系统的度量也没有形成一个共识,不过我想从复杂系统的前两个特性出发,毕竟一个微服务系统的个体数量和服务之间的信号传递是能够记录和量化的,尝试找到一个能够度量微服务系统复杂度的方法。

在《复杂》这本书中,作者列举了在科学界度量复杂性的9种方法:

根据上面给出的9种度量复杂性的方法,以此为基础设计了三种度量方法用来度量微服务的复杂性:API服务比例,频率加权服务节点数,API链路平均相似度。下面将详细介绍一下这三个指标。

对于微服务系统来说,所有的服务接口都是以API的方式呈现,因此在大小数量上很容易统计处整个软件系统有多少个API。同样,由于提供这些API的个体是微服务,所以也很好统计出软件系统中微服务的数量。

API服务比例 = 所有服务提供的API数量/所有微服务数量

基于这个公式,如果一个系统一共有100个API,但是这个系统是一个大单体,那么这时API服务比例就是100,很明显当这个度量值过大时对于整个系统来说并不是一个好事情。再考虑另外一种场景,一个系统一共有100个API,但是这个系统由100个微服务构成,这时API服务比例就是1,在这种场景下,当业务需要修改一个API的时候可能会涉及到多个微服务,所以当这个度量值过小时也不是一个好事情。

那么这个度量值究竟在一个什么范围内会比较好呢,通常来说根据DDD来设计微服务时,一个微服务会包含2-5个聚合,一个聚合会有1-3个实体,如果假设每个实体都需要有API进行创建,修改,删除,和查询,那么就意味着一个微服务的API数量应该是从8到60之间,这就是API服务比例的一个健康范围,高于这个范围时说明服务太少,某些服务承担的API太多,需要考虑进一步进行服务拆分,低于这个范围时,说明服务拆分过细,过多,需要考虑进行服务合并。

考虑到这个数据在分布上应该呈现一定的正态特性,所以在实际中可以把这个范围稍微放的更紧一点。

前面一个指标是从简单的数量上来度量微服务复杂性的,很明显这种度量稍显简单,因为每个API的业务价值是不相同的,频率加权服务节点数就是以API的调用频率作为权重,来衡量整个系统在提供API服务时需要多少个服务节点来支持。

假设一个系统对外一共暴露了4个API(外部主要指前端和外部系统),在过去一个月4个API的访问量分别如下:API1 10次,API2 30次,API3 55次,API4 5次。并且根据服务链路跟踪分析我们知道,API1的链路上有5个微服务,API2的链路上有3个微服务,API3的链路上有6个微服务,API4的链路上有7个微服务。

那么这时候频率加权服务节点数这个指标的计算过程就是:
10% * 5 + 30% * 3 + 55% * 6 + 5% * 7 = 5.05

通常来说,一个系统的绝大部份有业务价值的API在链路上都不应该有太多的服务节点,所以如果一个系统的这个指标大于3时,说明很多业务价值较高的API的链路有些复杂了。在实际场景中,如果某个API的链路很复杂需要10多个服务节点,比如说报表的API,但是如果这个API的调用频率非常低,可能一个月都不到5%,那么基于这个API的业务价值来考虑,整个系统是可以忍受这个高复杂低价值的API的。

这个指标的计算需要在生产环境上使用链路跟踪工具进行统计,比如pinpoint或者Zipkin来完成。这个指标的最终计算结果是整个系统按照频率加权的节点数,用来从整体上衡量一个系统的复杂程度。但是在计算这个指标的过程中,也可以轻松的帮我们找到调用频率高,链路复杂的API进行进一步的分析。

在分析API链路时,有时候会遇到这样的场景:

从上面三个API链路中很明显三个API存在一定的相似度,当系统中大部分的API链路相似度都比较高的时候,那么就说明系统里面的微服务个体可能存在着进一步优化的空间。所以需要能够对这些链路的相似度进行计算和度量。由于每个API链路都可以看做是一个序列,所以链路相似度的计算就是这个链路序列的相似度计算。

对于序列相似度,有很多中算法可以用来实现这个计算,个人推荐采用Levenshtein距离来计算序列相似度,Levenshtein距离的含义是求将a变成b(或将b变成a),所需要做的最少次数的变换。
举个例子,字符串”kitten”与”sitting”的莱文斯坦距离是3, 应为将一个字符串变为另一个字符串,最小需要做三次变换:

而基于这个距离,两个字符串的相似度就是 1-(Levenshtein距离/最长字符串长度)= 1 -(3/7)= 0.5714

这个算法详细原理有点复杂,如果对此有兴趣可以参看 https://en.wikipedia.org/wiki/Levenshtein_distance

使用相似度算法,我们就可以得出来每个API链路和其他链路之间的相似度,有了这个计算结果以后,对我们来说有两个价值:

以上三个指标有些是可以通过静态分析统计出来的,有些是需要上线运行以后才能统计得出的。因为在考虑微服务系统的时候,我们主要考虑是构成整个系统的微服务个体,以及这些个体提供的API能力,所以在度量复杂度的时候也主要是从两个概念开始入手。但是如果我们换个视角,想看看一个微服务内部的复杂度,只要把上面两个概念转变成聚合和聚合提供的方法,那么这套框架依然可以应用到微服务内部。

因此对于所有符合三个基本特征的复杂系统来说,我们都可以从这个系统的个体数量,这个系统内部的信息传递方式来度量系统复杂度。