當(dāng)談到使用DDD劃分微服務(wù)的好處的時(shí)候,經(jīng)常會(huì)說(shuō)DDD能夠讓相關(guān)的業(yè)務(wù)邏輯更加內(nèi)聚,并且降低服務(wù)之間的耦合性,從而最終實(shí)現(xiàn)達(dá)到降解系統(tǒng)的復(fù)雜性。但是在這里,不論是高內(nèi)聚,低耦合,甚至我們經(jīng)常說(shuō)的系統(tǒng)復(fù)雜性,我們有沒(méi)有一個(gè)客觀的可以量化的指標(biāo)來(lái)衡量這些概念。由于無(wú)法量化,所以就沒(méi)法度量,這樣當(dāng)團(tuán)隊(duì)在討論一個(gè)系統(tǒng)是否復(fù)雜,有多復(fù)雜這些問(wèn)題的時(shí)候,就很容易陷入各種主觀直覺(jué)的爭(zhēng)論中。
為了能夠讓團(tuán)隊(duì)不再陷入這種無(wú)意義的爭(zhēng)論中,我嘗試查找看看有沒(méi)有大牛深度的分析過(guò)這問(wèn)題,最好還能是在計(jì)算機(jī)和互聯(lián)網(wǎng)出現(xiàn)以后的分析和思考。非常幸運(yùn)的是我找到了這本書《復(fù)雜》,這是一本研究復(fù)雜系統(tǒng)的通識(shí)著作,作者在書中嘗試回答這些復(fù)雜系統(tǒng)著迷而令人費(fèi)解的問(wèn)題:
- 螞蟻在組成群體時(shí)為何會(huì)表現(xiàn)出如此的精密性和具有目的性?
- 數(shù)以億計(jì)的神經(jīng)元是如何產(chǎn)生出像意識(shí)這樣極度復(fù)雜的事物?
- 是什么在引導(dǎo)免疫系統(tǒng)、互聯(lián)網(wǎng)、全球經(jīng)濟(jì)和人類基因組等自組織結(jié)構(gòu)?
這篇文章基于這本書的思想框架,嘗試能夠給出一個(gè)度量微服務(wù)系統(tǒng)的方法。
復(fù)雜的定義
先看看在自然界中有哪些復(fù)雜系統(tǒng):一個(gè)上百萬(wàn)只螞蟻組成的蟻群,由神經(jīng)元組成的人類大腦,生物體內(nèi)的免疫系統(tǒng),當(dāng)今世界的經(jīng)濟(jì)體系,還有我們現(xiàn)在已經(jīng)離不開(kāi)的互聯(lián)網(wǎng)。
這些系統(tǒng)在細(xì)節(jié)上不一樣,但是如果從抽象層面來(lái)看由很多共性:
- 這些系統(tǒng)都是由個(gè)體組成,并不存在中央控制。大量的個(gè)體的行為產(chǎn)生出復(fù)雜,不斷變化且難以預(yù)測(cè)的行為模式。
- 這些系統(tǒng)都會(huì)利用來(lái)自內(nèi)部或外部的信息或信號(hào),同時(shí)也產(chǎn)生信息或信號(hào)。
- 這些系統(tǒng)都通過(guò)學(xué)習(xí)和進(jìn)化過(guò)程進(jìn)行適應(yīng),改變自身的行為以增加生存的機(jī)會(huì)。
對(duì)于一個(gè)微服務(wù)系統(tǒng)來(lái)說(shuō),上面三個(gè)特性同樣適用。一個(gè)微服務(wù)系統(tǒng)由很多個(gè)微服務(wù)組成,這些微服務(wù)的行為產(chǎn)生出復(fù)雜的行為模式。整個(gè)微服務(wù)系統(tǒng)會(huì)通過(guò)API利用內(nèi)部或外部的信號(hào),同時(shí)在系統(tǒng)內(nèi)部也是通過(guò)服務(wù)之間的接口進(jìn)行信息傳遞。一個(gè)微服務(wù)系統(tǒng)并不是靜態(tài)的,而是會(huì)不斷適應(yīng)業(yè)務(wù)變化,改變系統(tǒng)的API或者系統(tǒng)內(nèi)部的組織方式來(lái)增加生存的機(jī)會(huì)。
以上三點(diǎn)是一個(gè)微服務(wù)的復(fù)雜特性,那么如何從量的角度來(lái)衡量微服務(wù)的復(fù)雜程度呢??jī)蓚€(gè)微服務(wù)系統(tǒng)都很復(fù)雜,那么如何說(shuō)一個(gè)比另一個(gè)更復(fù)雜呢?這是一個(gè)很重要也很困難的問(wèn)題,現(xiàn)在在科學(xué)界對(duì)于復(fù)雜系統(tǒng)的度量也沒(méi)有形成一個(gè)共識(shí),不過(guò)我想從復(fù)雜系統(tǒng)的前兩個(gè)特性出發(fā),畢竟一個(gè)微服務(wù)系統(tǒng)的個(gè)體數(shù)量和服務(wù)之間的信號(hào)傳遞是能夠記錄和量化的,嘗試找到一個(gè)能夠度量微服務(wù)系統(tǒng)復(fù)雜度的方法。
度量復(fù)雜性
在《復(fù)雜》這本書中,作者列舉了在科學(xué)界度量復(fù)雜性的9種方法:
- 用大小度量復(fù)雜性,這是一種簡(jiǎn)單的度量方法,通常來(lái)說(shuō)數(shù)量越多就越復(fù)雜
- 用熵度量復(fù)雜性,就是使用香農(nóng)熵的概念,混亂程度越高則熵越高
- 用算法信息度量復(fù)雜性,這種方法把事物的復(fù)雜性定義為能夠產(chǎn)生對(duì)事物完整描述的最短計(jì)算機(jī)程序的長(zhǎng)度。
- 用邏輯深度度量復(fù)雜性,邏輯深度要求構(gòu)造事物的信息必須要有邏輯,不能是簡(jiǎn)單無(wú)意義的隨機(jī)值。
- 用熱力學(xué)深度度量復(fù)雜性,熱力學(xué)深度首先是確定產(chǎn)生出這個(gè)事物最科學(xué)合理的確定事件序列,然后測(cè)量構(gòu)建過(guò)程需要的能量和信息總量。
- 用計(jì)算能力度量復(fù)雜性,用一個(gè)事物的計(jì)算復(fù)雜度來(lái)度量這個(gè)事物的復(fù)雜度
- 用統(tǒng)計(jì)復(fù)雜性度量,度量用來(lái)預(yù)測(cè)系統(tǒng)將來(lái)的統(tǒng)計(jì)行為所需的系統(tǒng)過(guò)去行為的最小信息量。
- 用分形維度度量復(fù)雜性,動(dòng)力系統(tǒng)理論的概念度量事物復(fù)雜性的方法
- 用層次性度量復(fù)雜性,復(fù)雜系統(tǒng)最重要的共性就是層次性和不可分解性,所以可以使用層次來(lái)度量復(fù)雜性
度量微服務(wù)系統(tǒng)復(fù)雜性
根據(jù)上面給出的9種度量復(fù)雜性的方法,以此為基礎(chǔ)設(shè)計(jì)了三種度量方法用來(lái)度量微服務(wù)的復(fù)雜性:API服務(wù)比例,頻率加權(quán)服務(wù)節(jié)點(diǎn)數(shù),API鏈路平均相似度。下面將詳細(xì)介紹一下這三個(gè)指標(biāo)。
指標(biāo)1: API服務(wù)比例
對(duì)于微服務(wù)系統(tǒng)來(lái)說(shuō),所有的服務(wù)接口都是以API的方式呈現(xiàn),因此在大小數(shù)量上很容易統(tǒng)計(jì)處整個(gè)軟件系統(tǒng)有多少個(gè)API。同樣,由于提供這些API的個(gè)體是微服務(wù),所以也很好統(tǒng)計(jì)出軟件系統(tǒng)中微服務(wù)的數(shù)量。
API服務(wù)比例 = 所有服務(wù)提供的API數(shù)量/所有微服務(wù)數(shù)量
基于這個(gè)公式,如果一個(gè)系統(tǒng)一共有100個(gè)API,但是這個(gè)系統(tǒng)是一個(gè)大單體,那么這時(shí)API服務(wù)比例就是100,很明顯當(dāng)這個(gè)度量值過(guò)大時(shí)對(duì)于整個(gè)系統(tǒng)來(lái)說(shuō)并不是一個(gè)好事情。再考慮另外一種場(chǎng)景,一個(gè)系統(tǒng)一共有100個(gè)API,但是這個(gè)系統(tǒng)由100個(gè)微服務(wù)構(gòu)成,這時(shí)API服務(wù)比例就是1,在這種場(chǎng)景下,當(dāng)業(yè)務(wù)需要修改一個(gè)API的時(shí)候可能會(huì)涉及到多個(gè)微服務(wù),所以當(dāng)這個(gè)度量值過(guò)小時(shí)也不是一個(gè)好事情。
那么這個(gè)度量值究竟在一個(gè)什么范圍內(nèi)會(huì)比較好呢,通常來(lái)說(shuō)根據(jù)DDD來(lái)設(shè)計(jì)微服務(wù)時(shí),一個(gè)微服務(wù)會(huì)包含2-5個(gè)聚合,一個(gè)聚合會(huì)有1-3個(gè)實(shí)體,如果假設(shè)每個(gè)實(shí)體都需要有API進(jìn)行創(chuàng)建,修改,刪除,和查詢,那么就意味著一個(gè)微服務(wù)的API數(shù)量應(yīng)該是從8到60之間,這就是API服務(wù)比例的一個(gè)健康范圍,高于這個(gè)范圍時(shí)說(shuō)明服務(wù)太少,某些服務(wù)承擔(dān)的API太多,需要考慮進(jìn)一步進(jìn)行服務(wù)拆分,低于這個(gè)范圍時(shí),說(shuō)明服務(wù)拆分過(guò)細(xì),過(guò)多,需要考慮進(jìn)行服務(wù)合并。
考慮到這個(gè)數(shù)據(jù)在分布上應(yīng)該呈現(xiàn)一定的正態(tài)特性,所以在實(shí)際中可以把這個(gè)范圍稍微放的更緊一點(diǎn)。
指標(biāo)2: 頻率加權(quán)服務(wù)節(jié)點(diǎn)數(shù)
前面一個(gè)指標(biāo)是從簡(jiǎn)單的數(shù)量上來(lái)度量微服務(wù)復(fù)雜性的,很明顯這種度量稍顯簡(jiǎn)單,因?yàn)槊總€(gè)API的業(yè)務(wù)價(jià)值是不相同的,頻率加權(quán)服務(wù)節(jié)點(diǎn)數(shù)就是以API的調(diào)用頻率作為權(quán)重,來(lái)衡量整個(gè)系統(tǒng)在提供API服務(wù)時(shí)需要多少個(gè)服務(wù)節(jié)點(diǎn)來(lái)支持。
假設(shè)一個(gè)系統(tǒng)對(duì)外一共暴露了4個(gè)API(外部主要指前端和外部系統(tǒng)),在過(guò)去一個(gè)月4個(gè)API的訪問(wèn)量分別如下:API1 10次,API2 30次,API3 55次,API4 5次。并且根據(jù)服務(wù)鏈路跟蹤分析我們知道,API1的鏈路上有5個(gè)微服務(wù),API2的鏈路上有3個(gè)微服務(wù),API3的鏈路上有6個(gè)微服務(wù),API4的鏈路上有7個(gè)微服務(wù)。
那么這時(shí)候頻率加權(quán)服務(wù)節(jié)點(diǎn)數(shù)這個(gè)指標(biāo)的計(jì)算過(guò)程就是:
10% * 5 + 30% * 3 + 55% * 6 + 5% * 7 = 5.05
通常來(lái)說(shuō),一個(gè)系統(tǒng)的絕大部份有業(yè)務(wù)價(jià)值的API在鏈路上都不應(yīng)該有太多的服務(wù)節(jié)點(diǎn),所以如果一個(gè)系統(tǒng)的這個(gè)指標(biāo)大于3時(shí),說(shuō)明很多業(yè)務(wù)價(jià)值較高的API的鏈路有些復(fù)雜了。在實(shí)際場(chǎng)景中,如果某個(gè)API的鏈路很復(fù)雜需要10多個(gè)服務(wù)節(jié)點(diǎn),比如說(shuō)報(bào)表的API,但是如果這個(gè)API的調(diào)用頻率非常低,可能一個(gè)月都不到5%,那么基于這個(gè)API的業(yè)務(wù)價(jià)值來(lái)考慮,整個(gè)系統(tǒng)是可以忍受這個(gè)高復(fù)雜低價(jià)值的API的。
這個(gè)指標(biāo)的計(jì)算需要在生產(chǎn)環(huán)境上使用鏈路跟蹤工具進(jìn)行統(tǒng)計(jì),比如pinpoint或者Zipkin來(lái)完成。這個(gè)指標(biāo)的最終計(jì)算結(jié)果是整個(gè)系統(tǒng)按照頻率加權(quán)的節(jié)點(diǎn)數(shù),用來(lái)從整體上衡量一個(gè)系統(tǒng)的復(fù)雜程度。但是在計(jì)算這個(gè)指標(biāo)的過(guò)程中,也可以輕松的幫我們找到調(diào)用頻率高,鏈路復(fù)雜的API進(jìn)行進(jìn)一步的分析。
指標(biāo)3: API鏈路平均相似度
在分析API鏈路時(shí),有時(shí)候會(huì)遇到這樣的場(chǎng)景:
- API1的調(diào)用鏈路是 服務(wù)A接口1-服務(wù)B接口1-服務(wù)C接口1-服務(wù)D接口3-服務(wù)E接口2
- API2的調(diào)用鏈路是 服務(wù)A接口2-服務(wù)B接口1-服務(wù)C接口1-服務(wù)F接口1-服務(wù)G接口1
- API3的調(diào)用鏈路是 服務(wù)H接口1-服務(wù)L接口1-服務(wù)C接口1-服務(wù)D接口3-服務(wù)E接口2
從上面三個(gè)API鏈路中很明顯三個(gè)API存在一定的相似度,當(dāng)系統(tǒng)中大部分的API鏈路相似度都比較高的時(shí)候,那么就說(shuō)明系統(tǒng)里面的微服務(wù)個(gè)體可能存在著進(jìn)一步優(yōu)化的空間。所以需要能夠?qū)@些鏈路的相似度進(jìn)行計(jì)算和度量。由于每個(gè)API鏈路都可以看做是一個(gè)序列,所以鏈路相似度的計(jì)算就是這個(gè)鏈路序列的相似度計(jì)算。
對(duì)于序列相似度,有很多中算法可以用來(lái)實(shí)現(xiàn)這個(gè)計(jì)算,個(gè)人推薦采用Levenshtein距離來(lái)計(jì)算序列相似度,Levenshtein距離的含義是求將a變成b(或?qū)變成a),所需要做的最少次數(shù)的變換。
舉個(gè)例子,字符串”kitten”與”sitting”的萊文斯坦距離是3, 應(yīng)為將一個(gè)字符串變?yōu)榱硪粋€(gè)字符串,最小需要做三次變換:
- kitten → sitten (字符k變?yōu)閟)
- sitten → sittin (字符e變成i)
- sittin → sitting (在末尾插入字符g)
而基于這個(gè)距離,兩個(gè)字符串的相似度就是 1-(Levenshtein距離/最長(zhǎng)字符串長(zhǎng)度)= 1 -(3/7)= 0.5714
這個(gè)算法詳細(xì)原理有點(diǎn)復(fù)雜,如果對(duì)此有興趣可以參看 https://en.wikipedia.org/wiki/Levenshtein_distance
使用相似度算法,我們就可以得出來(lái)每個(gè)API鏈路和其他鏈路之間的相似度,有了這個(gè)計(jì)算結(jié)果以后,對(duì)我們來(lái)說(shuō)有兩個(gè)價(jià)值:
- 根據(jù)相似度進(jìn)行排序,然后重點(diǎn)分析相似度較高的鏈路,看看是不是兩個(gè)API提供了相同價(jià)值的服務(wù),或者說(shuō)這兩個(gè)鏈路上的服務(wù)應(yīng)該進(jìn)行合并
- 可以根據(jù)這些鏈路相似度計(jì)算整個(gè)系統(tǒng)的相似度平均值,整個(gè)系統(tǒng)的相似度平均值較高時(shí)(比如高過(guò)0.7,但是要注意不通算法計(jì)算出來(lái)的相似度結(jié)果意義并不相同)這時(shí)候就說(shuō)明系統(tǒng)存在優(yōu)化空間,通過(guò)優(yōu)化可以降低整個(gè)系統(tǒng)的復(fù)雜度。
系統(tǒng)復(fù)雜度度量框架
以上三個(gè)指標(biāo)有些是可以通過(guò)靜態(tài)分析統(tǒng)計(jì)出來(lái)的,有些是需要上線運(yùn)行以后才能統(tǒng)計(jì)得出的。因?yàn)樵诳紤]微服務(wù)系統(tǒng)的時(shí)候,我們主要考慮是構(gòu)成整個(gè)系統(tǒng)的微服務(wù)個(gè)體,以及這些個(gè)體提供的API能力,所以在度量復(fù)雜度的時(shí)候也主要是從兩個(gè)概念開(kāi)始入手。但是如果我們換個(gè)視角,想看看一個(gè)微服務(wù)內(nèi)部的復(fù)雜度,只要把上面兩個(gè)概念轉(zhuǎn)變成聚合和聚合提供的方法,那么這套框架依然可以應(yīng)用到微服務(wù)內(nèi)部。
因此對(duì)于所有符合三個(gè)基本特征的復(fù)雜系統(tǒng)來(lái)說(shuō),我們都可以從這個(gè)系統(tǒng)的個(gè)體數(shù)量,這個(gè)系統(tǒng)內(nèi)部的信息傳遞方式來(lái)度量系統(tǒng)復(fù)雜度。