微服務(wù)拆分之道
解耦何物,何時(shí)解耦
當(dāng)單體系統(tǒng)龐大到無(wú)法應(yīng)付時(shí),大多企業(yè)都會(huì)被迫把它們拆分成微服務(wù)。這是一次值得的旅行,但卻非坦途。我們學(xué)到了一些如何把這件事情做好的東西。我們需要從簡(jiǎn)單的服務(wù)入手,然后把對(duì)業(yè)務(wù)重要的經(jīng)常變化的垂直功能的服務(wù)挑出來(lái)。這些服務(wù)首先應(yīng)該是大的,而且最好不依賴(lài)單體系統(tǒng)的其他部分。我們應(yīng)該確保遷移的每一步都代表一個(gè)對(duì)整個(gè)架構(gòu)的原子性改善。
遷移單體系統(tǒng)到一個(gè)微服務(wù)生態(tài)系統(tǒng)將會(huì)是一場(chǎng)史詩(shī)搬的旅行。開(kāi)啟這段旅行的人們都希望能夠擴(kuò)大運(yùn)維的規(guī)模,加快變更的步伐以及避免變更的高昂代價(jià)。他們希望他們團(tuán)隊(duì)的數(shù)量獲得增長(zhǎng),同時(shí)可以彼此獨(dú)立并行的交付價(jià)值;他們希望快速試驗(yàn)核心業(yè)務(wù)功能,更快地交付價(jià)值;他們也希望避免因?qū)σ延袉误w系統(tǒng)的改變產(chǎn)生高成本。
把一個(gè)單體系統(tǒng)拆分進(jìn)微服務(wù)生態(tài)系統(tǒng),解耦什么功能,何時(shí)及如何增量遷移是架構(gòu)挑戰(zhàn)的一部分。在這篇文章中,我將分享一些技巧以指導(dǎo)交付團(tuán)隊(duì)——開(kāi)發(fā)人員、架構(gòu)師及技術(shù)經(jīng)理——在這次旅途中如何拆分服務(wù)。
為了闡明這些技巧,我要借用一個(gè)多層的在線(xiàn)零售系統(tǒng)。該系統(tǒng)把用戶(hù)接口,業(yè)務(wù)邏輯和數(shù)據(jù)層緊密結(jié)合在一起。我選擇這個(gè)例子的原因是因?yàn)樗募軜?gòu)具有許多運(yùn)行業(yè)務(wù)的單體應(yīng)用的特征,而且它采用的技術(shù)棧也是很先進(jìn)的,非常適合分解,而不是完全重寫(xiě)和替換。
目的地——微服務(wù)生態(tài)系統(tǒng)
在踏上旅途之前,擁有一個(gè)對(duì)微服務(wù)生態(tài)系統(tǒng)的一致的理解對(duì)我們來(lái)說(shuō)是關(guān)鍵的。微服務(wù)生態(tài)系統(tǒng)就是一個(gè)服務(wù)平臺(tái),其上的每個(gè)服務(wù)封裝了一個(gè)業(yè)務(wù)功能。業(yè)務(wù)功能代表在一個(gè)具體的域里面為了實(shí)現(xiàn)業(yè)務(wù)目標(biāo)和職責(zé)而做的事情。每個(gè)微服務(wù)暴露一個(gè)API,開(kāi)發(fā)者可以發(fā)現(xiàn)并以自服務(wù)方式使用它。微服務(wù)擁有獨(dú)立的生命周期。開(kāi)發(fā)者可以獨(dú)立的構(gòu)建、測(cè)試和發(fā)布每個(gè)微服務(wù)。微服務(wù)生態(tài)系統(tǒng)實(shí)施的組織結(jié)構(gòu)中,團(tuán)隊(duì)是自治的且長(zhǎng)期存在的,每個(gè)團(tuán)隊(duì)負(fù)責(zé)一個(gè)或多個(gè)服務(wù)。與人們普遍的認(rèn)知不同,與微服務(wù)這個(gè)詞中的‘微’字面意思不同,每個(gè)服務(wù)的大小并不重要,它會(huì)因組織運(yùn)維成熟度不同而變化。正如Martin Fowler所說(shuō):“微服務(wù)是一個(gè)標(biāo)簽而不是一種描述”。

旅行指南
在開(kāi)始深入了解本指南之前,認(rèn)識(shí)到拆分已有系統(tǒng)到微服務(wù)總體成本會(huì)很高而且可能需要多次迭代很重要。開(kāi)發(fā)者和架構(gòu)師必須仔細(xì)評(píng)估是不是有必要分拆單體系統(tǒng),微服務(wù)本身是不是其真正的歸宿。講完這些,讓我們來(lái)看看本指南。
從簡(jiǎn)單,能合理解耦的功能開(kāi)始熱身
開(kāi)始微服務(wù)之旅需要一個(gè)最低級(jí)別的運(yùn)維就緒。它需要按需訪(fǎng)問(wèn)部署環(huán)境,創(chuàng)建新類(lèi)型的CI管道,獨(dú)立編譯,測(cè)試和部署可執(zhí)行的服務(wù)。同時(shí)它還需要為分布式系統(tǒng)提供安全,調(diào)試以及監(jiān)控的能力。無(wú)論是構(gòu)建全新(greenfield)服務(wù)還是分解已有系統(tǒng)都需要成熟的運(yùn)維就緒環(huán)境。關(guān)于運(yùn)維就緒的更多信息,可以閱讀Martin的關(guān)于微服務(wù)前置條件的文章。不過(guò),好消息是從Martin的那篇文章之后微服務(wù)架構(gòu)運(yùn)維技術(shù)得到快速的演進(jìn)。其中包括誕生了Service Mesh——一個(gè)具體的運(yùn)行快速、可靠及安全的微服務(wù)網(wǎng)格的基礎(chǔ)架構(gòu)層,提供更高層面的部署架構(gòu)抽象的容器編排系統(tǒng),像GoCD這樣的以容器的方式編譯,測(cè)試和部署微服務(wù)的CD系統(tǒng)。
我的建議是開(kāi)發(fā)和運(yùn)維團(tuán)隊(duì)構(gòu)建底層基礎(chǔ)架構(gòu),為第一個(gè)和第二個(gè)被分解出來(lái)或新構(gòu)建的服務(wù)構(gòu)建CI管道和API管理系統(tǒng)。我們從單體系統(tǒng)中能合理分解出來(lái)的功能開(kāi)始。因?yàn)?,他們不需要改變?cè)S多正在使用單體系統(tǒng)的面向客戶(hù)的應(yīng)用,可能也不需要數(shù)據(jù)存儲(chǔ)。在這個(gè)時(shí)候交付團(tuán)隊(duì)需要優(yōu)化他們交付方法,為團(tuán)隊(duì)成員提供技能培訓(xùn),建立最小化的基礎(chǔ)架構(gòu)來(lái)交付安全的可獨(dú)立部署的暴露自服務(wù)API的服務(wù)。比如,對(duì)于在線(xiàn)零售系統(tǒng),第一個(gè)服務(wù)可能是這個(gè)單體系統(tǒng)調(diào)用驗(yàn)證用戶(hù)的“終端用戶(hù)驗(yàn)證”服務(wù)。第二個(gè)可能是“用戶(hù)Profile”服務(wù), 它是一個(gè)面板服務(wù),為新的客戶(hù)應(yīng)用提供一個(gè)更好地消費(fèi)者視圖。
首先,我建議解耦簡(jiǎn)單的邊界服務(wù)。其次我們采用不同的方法來(lái)分解深度嵌入在單體系統(tǒng)中的功能。之所以建議一開(kāi)始分解邊界服務(wù)是因?yàn)樵谶@段旅行之初,交互團(tuán)隊(duì)最大的風(fēng)險(xiǎn)在于不能對(duì)微服務(wù)進(jìn)行合適的運(yùn)維。所以可以使用邊界服務(wù)來(lái)實(shí)踐他們所需的“運(yùn)維前提”。一旦他們解決了運(yùn)維先決條件相關(guān)的問(wèn)題,他們就能夠應(yīng)對(duì)拆分單體系統(tǒng)的關(guān)鍵問(wèn)題。

最小化對(duì)單體系統(tǒng)的向后依賴(lài)
作為一個(gè)最基本的原則,交付團(tuán)隊(duì)?wèi)?yīng)該最小化新形成的微服務(wù)對(duì)原單體系統(tǒng)的依賴(lài)。微服務(wù)主要的一個(gè)優(yōu)勢(shì)在于具有一個(gè)快速且獨(dú)立的發(fā)布周期。與原單體系統(tǒng)的數(shù)據(jù)、邏輯或者API的依賴(lài)會(huì)把服務(wù)與單體系統(tǒng)耦合在一起,妨礙微服務(wù)優(yōu)勢(shì)的發(fā)揮。通常我們從單體系統(tǒng)分解出服務(wù)的原因是與其綁定的功能變更的高成本和低效率。所以,我們希望通過(guò)消除對(duì)這個(gè)單體系統(tǒng)的依賴(lài)逐步解耦這些核心功能。假如團(tuán)隊(duì)遵守這個(gè)原則構(gòu)建功能服務(wù),他們會(huì)發(fā)現(xiàn)依賴(lài)都是反向的從單體系統(tǒng)到服務(wù)。這是所期望的依賴(lài)方向,因?yàn)檫@不會(huì)影響新的服務(wù)的變更速度。
考慮在線(xiàn)零售系統(tǒng),“購(gòu)買(mǎi)”和“促銷(xiāo)”都是核心功能?!百?gòu)買(mǎi)”的功能結(jié)賬的時(shí)候會(huì)使用“促銷(xiāo)”功能根據(jù)用戶(hù)購(gòu)買(mǎi)的產(chǎn)品為他們提供有效的最佳優(yōu)惠。假如我們需要決定先解耦這兩個(gè)功能的哪一個(gè),我建議先解耦“促銷(xiāo)”,再解耦“購(gòu)買(mǎi)”。因?yàn)榘凑者@個(gè)順序我們就能夠減少對(duì)這個(gè)單體系統(tǒng)的依賴(lài)。按照這個(gè)順序,“購(gòu)買(mǎi)”起初還會(huì)維持在老的單體系統(tǒng)內(nèi),但卻是依賴(lài)新的“促銷(xiāo)”微服務(wù)。
下一個(gè)指導(dǎo)原則是關(guān)于開(kāi)發(fā)者決定解耦服務(wù)順序的其他方式。這意味著他們不總是能夠避免要反向依賴(lài)原單體系統(tǒng)。如果新的服務(wù)最終需要回調(diào)單體系統(tǒng),我建議從單體中暴露一個(gè)新的API,在新的服務(wù)中通過(guò)一個(gè)“防腐”層來(lái)調(diào)用該API以確保單體系統(tǒng)的概念不會(huì)浸染微服務(wù)。要盡量確保這個(gè)API按照定義良好的域慨念和結(jié)構(gòu)定義,即使單體系統(tǒng)內(nèi)部實(shí)現(xiàn)可能不同(注:該新的API可以理解成是一個(gè)適配器,它按照新的微服務(wù)定義良好的業(yè)務(wù)域概念設(shè)計(jì),但它需要實(shí)現(xiàn)單體系統(tǒng)中老的域概念到新的域概念的適配。通過(guò)這個(gè)API使得拆分出來(lái)的微服務(wù)對(duì)單體系統(tǒng)中舊的域概念透明,防止了舊的域概念“入侵”。該API起到了防腐層隔離的作用)。在這種不好的情況下,交付團(tuán)隊(duì)可能承受改變單體系統(tǒng)帶來(lái)的成本和難度的問(wèn)題,需要配合單體系統(tǒng)的發(fā)布一起測(cè)試發(fā)布的新的系統(tǒng)。

黏連性功能早拆分
假設(shè)現(xiàn)在交付團(tuán)隊(duì)可以很愉快的構(gòu)建微服務(wù)了,并且準(zhǔn)備好了解決棘手的問(wèn)題。然他們發(fā)現(xiàn)接下來(lái)的功能拆分不依賴(lài)單體系統(tǒng)是不可能的。存在這個(gè)問(wèn)題的根本原因常常是因?yàn)閱误w系統(tǒng)內(nèi)的功能域概念設(shè)計(jì)部完整,沒(méi)有良好定義,單體系統(tǒng)內(nèi)許多其他的功能依賴(lài)它。為了能夠繼續(xù),開(kāi)發(fā)者需要識(shí)別這類(lèi)黏連性功能,分解成定義良好的域概念,然后把這些域概念具體化到不同的服務(wù)中。
比如,在一個(gè)Web單體系統(tǒng)中“會(huì)話(huà)”的概念是最常用的耦合因子之一。 在那個(gè)在線(xiàn)零售的例子中,會(huì)話(huà)常常就是一個(gè)籃子,裝有許多屬性,從涉及不同域邊界的用戶(hù)偏好如發(fā)貨和支付設(shè)置等,到用戶(hù)的意圖和交互,如當(dāng)前訪(fǎng)問(wèn)頁(yè)面,點(diǎn)擊的產(chǎn)品和意向清單等。除非我們對(duì)當(dāng)前“會(huì)話(huà)”概念解耦,分解及具體化,否則我們將為解耦未來(lái)的諸多功能而疲于奔命,因?yàn)樗麄儠?huì)因?yàn)檫@個(gè)不嚴(yán)謹(jǐn)?shù)臅?huì)話(huà)概念和單體系統(tǒng)“糾纏”在一起。我也不鼓勵(lì)在單體系統(tǒng)之外創(chuàng)建“會(huì)話(huà)”服務(wù),因?yàn)樗仓粫?huì)導(dǎo)致和目前在單體進(jìn)程中存在的相似的緊密耦合。而且,情況會(huì)更糟糕,因?yàn)檫@種耦合是進(jìn)程外跨網(wǎng)絡(luò)的。
開(kāi)發(fā)人員可以從逐步從黏連性功能中提取出微服務(wù),一次一個(gè)。比如, 重構(gòu)“消費(fèi)者意向清單”,并提取出來(lái)創(chuàng)建一個(gè)新的服務(wù),然后重構(gòu)“消費(fèi)者支付偏好”,產(chǎn)生一個(gè)新的微服務(wù)。如此重復(fù)。

使用依賴(lài)和結(jié)構(gòu)化代碼分析工具,比如Structure101,來(lái)識(shí)別單體系統(tǒng)中耦合度最高,約束最大的功能因子。
垂直拆分并盡早切分?jǐn)?shù)據(jù)
從單體中把功能拆分出來(lái)主要是為了能夠單獨(dú)發(fā)布這些功能。第一個(gè)原則就是指導(dǎo)開(kāi)發(fā)人員進(jìn)行如何拆分。單體系統(tǒng)經(jīng)常是由緊密集成的多層,甚至是彼此依賴(lài)脆弱而且需要同時(shí)發(fā)布的多個(gè)(子)系統(tǒng)組成。比如,在線(xiàn)零售系統(tǒng)是由一個(gè)或多個(gè)面向客戶(hù)的在線(xiàn)購(gòu)物應(yīng)用,一個(gè)實(shí)現(xiàn)許多業(yè)務(wù)功能的后端系統(tǒng)以及用于保存狀態(tài)的中心化集成的數(shù)據(jù)存儲(chǔ)組成。
許多拆分都是企圖從提取出面向用戶(hù)的組件入手,同時(shí)拆分出幾個(gè)面板服務(wù)為開(kāi)發(fā)者提供友好的新式用戶(hù)界面的API。然而,數(shù)據(jù)依然維持在一個(gè)schema和一個(gè)存儲(chǔ)系統(tǒng)中。這種方法可以快速獲勝,比如可以更頻繁的改變用戶(hù)界面。但是當(dāng)設(shè)計(jì)核心功能時(shí),交互團(tuán)隊(duì)只能以單體系統(tǒng)和其數(shù)據(jù)存儲(chǔ)變更最慢的部分效率進(jìn)行。簡(jiǎn)單來(lái)說(shuō),數(shù)據(jù)沒(méi)有拆分,就不是微服務(wù)架構(gòu)。把所有數(shù)據(jù)保存在同一個(gè)數(shù)據(jù)存儲(chǔ)里也是不符合去中心化數(shù)據(jù)管理微服務(wù)特征。
為此,我們的策略就是把功能垂直移出,將核心功能和數(shù)據(jù)解耦,并且把所有前端應(yīng)用都重定向到新的服務(wù)API。
多個(gè)應(yīng)用共享中心化數(shù)據(jù)是切分拆分后服務(wù)所使用的數(shù)據(jù)的主要障礙。交付團(tuán)隊(duì)需要采用適合他們環(huán)境的數(shù)據(jù)遷移策略,這取決于他們是否能夠同時(shí)重定向和遷移所有的數(shù)據(jù)讀寫(xiě)者。Stripe的四階段數(shù)據(jù)遷移策略是其中之一,它被應(yīng)用到要求逐步遷移與數(shù)據(jù)庫(kù)集成的應(yīng)用,同時(shí)所有系統(tǒng)都可以持續(xù)運(yùn)行的環(huán)境中。

避免僅僅拆分前端用戶(hù)面板和后端服務(wù)而不拆分?jǐn)?shù)據(jù)。
拆分重要且頻繁變化的業(yè)務(wù)
把功能從單體系統(tǒng)中拆分出來(lái)是件困難的事情。我聽(tīng)到過(guò)Neal Ford用周密的器官手術(shù)對(duì)此做過(guò)類(lèi)比。在在線(xiàn)零售應(yīng)用中,拆分出一個(gè)功能需要把該功能涉及的數(shù)據(jù)、邏輯和用戶(hù)接口組件仔細(xì)地剝離出來(lái),然后重定向到新的服務(wù)中。這需要花費(fèi)不少的工作量,開(kāi)發(fā)者需要基于他們獲得的好處,比如提高效率,擴(kuò)大規(guī)模等持續(xù)評(píng)估拆分的成本。舉例來(lái)說(shuō),如果交互團(tuán)隊(duì)目標(biāo)是加速對(duì)已有的被困于單體系統(tǒng)中功能的修改,那么他們必須識(shí)別出一直被修改的最多的功能然后把它拿出來(lái)。把那些不斷變化的代碼分離出來(lái)。這些代碼正在獲得開(kāi)發(fā)人員大量“關(guān)愛(ài)”,但又在束縛他們快速發(fā)布價(jià)值。交互團(tuán)隊(duì)可以分析代碼提交模式找出以往變化最多的代碼, 然后結(jié)合產(chǎn)品路線(xiàn)圖及其組合來(lái)了解最期望的在不久的將來(lái)亦最受關(guān)注的功能。開(kāi)發(fā)人員需要與業(yè)務(wù)和產(chǎn)品經(jīng)理來(lái)交談以了解對(duì)他們來(lái)說(shuō)真正重要的差異化的功能。
拿在線(xiàn)零售系統(tǒng)的例子來(lái)說(shuō),“客戶(hù)個(gè)性化”功能需要反復(fù)進(jìn)行試驗(yàn)以便為客戶(hù)提供最好的體驗(yàn)。所以它是一個(gè)好的拆分候選項(xiàng)。它是一項(xiàng)對(duì)業(yè)務(wù),對(duì)用戶(hù)體驗(yàn)很重要而且會(huì)頻繁變化的功能。

使用社交代碼分析工具如CodeScene發(fā)現(xiàn)最活躍的組件。如果編譯系統(tǒng)在每次代碼提交的時(shí)候恰好會(huì)觸及或者自動(dòng)產(chǎn)生代碼,請(qǐng)確保過(guò)濾掉了這些“雜音信號(hào)”。把這些頻繁變化的代碼和產(chǎn)品路線(xiàn)圖即將發(fā)生的變更相對(duì)應(yīng)然后找出需要拆分的交叉點(diǎn)。
拆分功能,而不是代碼
不管什么時(shí)候,開(kāi)發(fā)人員想從已有系統(tǒng)里頭剝離服務(wù)有兩種方式:代碼萃取和功能重寫(xiě)。
通常缺省的情況下,服務(wù)提取或者單體分解被假定為重用原來(lái)已有的實(shí)現(xiàn)并把它摘取出來(lái)放入單獨(dú)的服務(wù)中。這樣做的部分原因是我們對(duì)自己設(shè)計(jì)和編寫(xiě)的代碼存在認(rèn)知偏差。不管過(guò)程如何痛苦結(jié)果如何不完美,付出的勞動(dòng)會(huì)使我們對(duì)代碼產(chǎn)生感情。事實(shí)上,這被稱(chēng)作為宜家效應(yīng)。不幸的是,這種認(rèn)知偏差將會(huì)妨礙單體系統(tǒng)分解的工作。它會(huì)導(dǎo)致開(kāi)發(fā)人員,甚至包括技術(shù)經(jīng)理即使萃取成本高但價(jià)值低也要重用代碼。
作為一種選擇,交互團(tuán)隊(duì)可以重寫(xiě)功能,而廢除老的代碼。重寫(xiě)為他們提供一個(gè)機(jī)會(huì)來(lái)重新審視業(yè)務(wù)功能,啟動(dòng)與業(yè)務(wù)會(huì)話(huà),簡(jiǎn)化其往業(yè)務(wù)流程,挑戰(zhàn)隨著時(shí)間推移在老的系統(tǒng)形成的陳舊的假設(shè)和約束。這也為技術(shù)更新,為使用對(duì)具體服務(wù)最適合的技術(shù)棧及編程語(yǔ)言實(shí)現(xiàn)服務(wù)的機(jī)會(huì)。
就在線(xiàn)零售系統(tǒng)來(lái)說(shuō),“定價(jià)”和“促銷(xiāo)”功能是一塊復(fù)雜的智能化代碼。它能夠動(dòng)態(tài)配置應(yīng)用定價(jià)促銷(xiāo)規(guī)則,基于諸如消費(fèi)者行為、忠誠(chéng)度及產(chǎn)品包等各種因素提供折扣和優(yōu)惠。
上述功能比較適合代碼萃取重用。然而,“用戶(hù)Profile”是一個(gè)簡(jiǎn)單的CRUD功能,其主要由系樣板式的系列化,處理存儲(chǔ)和配置的代碼組成。所以它比較適合棄用老代碼而重寫(xiě)。
根據(jù)我的經(jīng)驗(yàn),在大部分分解場(chǎng)景中,團(tuán)隊(duì)最好重新實(shí)現(xiàn)新的功能而廢棄老的代碼。但因?yàn)橄旅娴脑?,可以考慮高成本低價(jià)值的重用:
存在大量的處理環(huán)境依賴(lài)的樣板式代碼,比如在運(yùn)行時(shí)訪(fǎng)問(wèn)應(yīng)用配置,訪(fǎng)問(wèn)數(shù)據(jù),緩存,或者是使用老的框架構(gòu)建的。這些樣板式代碼大部分需要重寫(xiě)。而新的運(yùn)行微服務(wù)的框架與十幾年前的老框架差別巨大,需要使用幾乎不同的樣板式代碼;
很有可能已有的功能不是圍繞清晰的域慨念構(gòu)建的。這會(huì)導(dǎo)致沒(méi)有反映新的域模型而需要進(jìn)行大重構(gòu)的數(shù)據(jù)結(jié)構(gòu)傳輸和存儲(chǔ);
長(zhǎng)期遺留的代碼,經(jīng)歷了許多次變更迭代,可能具有很高級(jí)別的代碼毒性和較低的重用價(jià)值。
除非是關(guān)聯(lián)的,和清晰的域概念保持一致的,具有高知識(shí)產(chǎn)權(quán)的功能,我強(qiáng)烈建議對(duì)其重寫(xiě),棄用老的代碼。

使用代碼毒性分析工具如CheckStyle來(lái)決定重用還是重寫(xiě)
先大后小
從遺留的單體系統(tǒng)中尋找域邊界是一門(mén)藝術(shù)也是一門(mén)科學(xué)。作為一般規(guī)則,應(yīng)用域驅(qū)動(dòng)技術(shù)尋找定義微服務(wù)邊界的“有界上下文”是一個(gè)很好的起點(diǎn)。我承認(rèn),我經(jīng)常看到從大的單體到真正小的服務(wù)的“矯枉過(guò)正”。設(shè)計(jì)這些小服務(wù)是受已有的規(guī)范化的數(shù)據(jù)視圖啟發(fā)和驅(qū)動(dòng)的。這種識(shí)別服務(wù)邊界的方法幾乎總會(huì)圍繞創(chuàng)建、讀取、修改和刪除資源產(chǎn)生大量的弱服務(wù),從而形成“寒武紀(jì)生命大爆炸”。對(duì)于微服務(wù)架構(gòu)的新手來(lái)說(shuō),這會(huì)產(chǎn)生一個(gè)高摩擦的環(huán)境,最終導(dǎo)致無(wú)法使那些服務(wù)進(jìn)行獨(dú)立發(fā)布和運(yùn)行。這會(huì)創(chuàng)建一個(gè)難于調(diào)試的分布式系統(tǒng),一個(gè)跨事務(wù)因而難于保持一致性的分布式系統(tǒng),一個(gè)對(duì)于相對(duì)組織運(yùn)維成熟度來(lái)說(shuō)過(guò)于復(fù)雜的系統(tǒng)。盡管有一些關(guān)于微服務(wù)應(yīng)該多“微”的探討,如團(tuán)隊(duì)的大小,重寫(xiě)服務(wù)的時(shí)間以及什么樣的行為必須封裝等,我的建議是微服務(wù)的大小取決于交互運(yùn)維團(tuán)隊(duì)能夠獨(dú)立發(fā)布,監(jiān)控和運(yùn)維多少服務(wù)。開(kāi)始可以是圍繞邏輯域概念的大服務(wù),當(dāng)團(tuán)隊(duì)運(yùn)維就緒的話(huà),再分拆成多個(gè)服務(wù)。
就分解在線(xiàn)零售系統(tǒng)來(lái)說(shuō),開(kāi)發(fā)人員開(kāi)始可以使“購(gòu)買(mǎi)”服務(wù)既包含包含購(gòu)物袋上下文功能也包含購(gòu)買(mǎi)的功能,比如“結(jié)賬”。隨著他們能夠成立更小的團(tuán)隊(duì),能夠發(fā)布大量的服務(wù),他們可以把“購(gòu)物袋”分離出一個(gè)單獨(dú)的服務(wù)。

使用Richardson Maturity Model L3(REST成熟度模型)和超鏈接來(lái)確保未來(lái)的服務(wù)拆分不會(huì)影響調(diào)用者。比如,調(diào)用者能夠發(fā)現(xiàn)如何結(jié)賬而不需要提前知道。
以原子性演進(jìn)方式遷移
傳統(tǒng)的單體應(yīng)用被分解成設(shè)計(jì)優(yōu)美的微服務(wù),然后讓單體應(yīng)用消失的無(wú)影蹤的想法有點(diǎn)荒唐,可以說(shuō)是不可取的。任何經(jīng)驗(yàn)豐富的工程師都可以分享一些關(guān)于嘗試遺留系統(tǒng)遷移和改進(jìn)的故事。這些嘗試都是在抱著對(duì)最終完成過(guò)分樂(lè)觀(guān)的狀態(tài)下計(jì)劃和開(kāi)始的,但大部分都是在足夠好的時(shí)間點(diǎn)被放棄了。這些長(zhǎng)期努力的計(jì)劃之所以取消是因?yàn)榍闆r發(fā)生了一些大的變化,比如項(xiàng)目費(fèi)用用完了;組織目標(biāo)轉(zhuǎn)移到其他方向上了;支持的領(lǐng)導(dǎo)層離職了。所以現(xiàn)實(shí)的做法是如何讓單體應(yīng)用踏上微服務(wù)之旅。我們稱(chēng)之為“架構(gòu)原子演進(jìn)式遷移”,這種方式的遷移每一步都是完整的,也是可以回撤。這一點(diǎn),在我們談到針對(duì)整個(gè)架構(gòu)的改善和服務(wù)解耦的增量迭代的方法時(shí)尤為重要。每個(gè)遷移增量必須使我們更好地朝架構(gòu)目標(biāo)前進(jìn)一步。借用“演進(jìn)式架構(gòu)”適應(yīng)度函數(shù)說(shuō)法,每一次原子性遷移之后,架構(gòu)適應(yīng)度函數(shù)應(yīng)該產(chǎn)生一個(gè)更接近于架構(gòu)目標(biāo)的值。
讓我來(lái)使用一個(gè)例子描述下這點(diǎn)。 想象一下,微服務(wù)架構(gòu)目標(biāo)是提高開(kāi)發(fā)人員修改整體系統(tǒng),交付價(jià)值的速度。團(tuán)隊(duì)決定基于OAuth2.0協(xié)議把終端用戶(hù)驗(yàn)證功能解耦到一個(gè)獨(dú)立服務(wù)中。這個(gè)服務(wù)用來(lái)替換已存在的(老的架構(gòu)中的)客戶(hù)端應(yīng)用用戶(hù)驗(yàn)證功能和作為新的微服務(wù)架構(gòu)中的用戶(hù)驗(yàn)證。讓我們把這個(gè)演進(jìn)式的增量稱(chēng)之為“驗(yàn)證服務(wù)引入”。引入該服務(wù)的一個(gè)方式就是先完成這些步驟:
(1) 構(gòu)建Auth服務(wù), 實(shí)現(xiàn)OAuth 2.0協(xié)議。
(2)增加一條新的驗(yàn)證路徑到單體系統(tǒng)的后端調(diào)用新構(gòu)建的Auth服務(wù),處理終端用戶(hù)請(qǐng)求。
假如團(tuán)隊(duì)到此為止,轉(zhuǎn)而去構(gòu)建其他的服務(wù)和功能,這會(huì)使得整個(gè)架構(gòu)處于熵增狀態(tài)。在這種情況下,有兩種方法驗(yàn)證用戶(hù),一種是通過(guò)新的OAuth 2.0,一種是通過(guò)老的用戶(hù)密碼/會(huì)話(huà)。這個(gè)時(shí)候,實(shí)際上團(tuán)隊(duì)離快速變化的總的目標(biāo)更遠(yuǎn)了。任何新的單體代碼開(kāi)發(fā)人員都需要處理兩條代碼路徑,增加了他們理解代碼的認(rèn)知負(fù)擔(dān),降低了修改測(cè)試代碼的速度。
然而,作為我們的一個(gè)原子性演進(jìn)單元,團(tuán)隊(duì)可以增加下面的步驟:
(3)替換老的用戶(hù)驗(yàn)證調(diào)用
(4)去除單體系統(tǒng)中老的用戶(hù)驗(yàn)證代碼
至此,我們可以說(shuō)團(tuán)隊(duì)已經(jīng)接近架構(gòu)目標(biāo)了。

單體拆分的一個(gè)原子單元包括:
拆分新的服務(wù)
重定向所有消費(fèi)者(服務(wù)消費(fèi)者)到新的用戶(hù)
移除單體系統(tǒng)中老的代碼
反模式:解耦新的服務(wù),新的消費(fèi)者使用新的服務(wù),但老的代碼永遠(yuǎn)不退休。
我經(jīng)常發(fā)現(xiàn)團(tuán)隊(duì)只構(gòu)建新的功能但不讓老的代碼退休就結(jié)束一個(gè)從單體系統(tǒng)功能的遷移,然后宣布全部完成。這就是上面描述的反模式。之所以存在這種情況的原因是,a)只關(guān)注引入一個(gè)新功能的短期好處;b)退休老的代碼所需要總的工作量和構(gòu)建新服務(wù)的優(yōu)先級(jí)有競(jìng)爭(zhēng)。為了做正確的事情,我們要盡可能的努力按原子性步驟去做。
使用這種遷移方法,我們可以把遷移之旅分成一段段更短的旅途,能夠安穩(wěn)的停下,然后重新開(kāi)始,確保整個(gè)旅行的順利,最終“消滅”單體。