編者按: 如果你的工作圍繞一個(gè)大型的,復(fù)雜的單體應(yīng)用,可能你每天開發(fā)和部署應(yīng)用的工作都是進(jìn)展緩慢而痛苦的。微服務(wù)看起來像是一個(gè)遙不可及的天堂,幸運(yùn)的是,有方法可以幫助你逃離單體架構(gòu)的地獄。本文將會(huì)介紹如何逐步地將單體應(yīng)用改造為一系列的微服務(wù)。
作者簡(jiǎn)介: Chris Richardson是Cloudfoundry.com的創(chuàng)始人,現(xiàn)在為提供開發(fā)和部署應(yīng)用的咨詢服務(wù)。
本文是微服務(wù)系列文章的第七篇,這個(gè)系列的文章都很精彩,第一篇介紹了微服務(wù)架構(gòu)的模式,討論了微服務(wù)架構(gòu)的優(yōu)勢(shì)和缺點(diǎn),接下來的文章討論了微服務(wù)架構(gòu)的不同方面:API Gateway,進(jìn)程間的通信,服務(wù)發(fā)現(xiàn),事件驅(qū)動(dòng)的數(shù)據(jù)管理和部署微服務(wù)。你還對(duì)哪篇 文章感興趣,小編可以翻譯:
將單體應(yīng)用改造為微服務(wù)實(shí)際上是應(yīng)用現(xiàn)代化的過程,這是開發(fā)者們?cè)谶^去十年來一直在做的事情,所以已經(jīng)有一些可以復(fù)用的經(jīng)驗(yàn)。
全部重寫是絕對(duì)不能用的策略,除非你要集中精力從頭構(gòu)建一個(gè)基于微服務(wù)的應(yīng)用。雖然聽起來很有吸引力,但是風(fēng)險(xiǎn)很大,很有可能會(huì)失敗。就像MartinFowler所說的: 『The only thing a Big Bang rewrite guarantees is a Big Bang!』
你應(yīng)該循序漸進(jìn)地重構(gòu)你的單體應(yīng)用。你可以逐步地構(gòu)建一個(gè)部分微服務(wù)化的應(yīng)用,然后和你的單體應(yīng)用集成起來。單體應(yīng)用的實(shí)現(xiàn)的功能會(huì)逐漸變少,最終消失或變成一個(gè)新的微服務(wù)組件。
Martin Fowler稱這種應(yīng)用現(xiàn)代化的策略為Strangler Application。這個(gè)名字來源于在熱帶雨林發(fā)現(xiàn)的一種植物Strangler vine, 為了夠到充足的陽光, 它們繞樹生長(zhǎng),一直向上。當(dāng)樹木死后,只會(huì)留下一個(gè)樹形的藤蔓。應(yīng)用的現(xiàn)代化就是類似的模式,我們會(huì)在舊有的應(yīng)用上,構(gòu)建一個(gè)新的包含微服務(wù)的應(yīng)用,慢慢取代舊的應(yīng)用。下面一起來看下這些策略:
Law of Holes告訴我們,如果你正在一個(gè)洞里,就不要繼續(xù)再挖了。當(dāng)你的單體應(yīng)用已經(jīng)變得無法管理的時(shí)候,就不要再繼續(xù)擴(kuò)大它的規(guī)模了。 比如你想添加新功能,不要在單體應(yīng)用中添加代碼,而要將新的代碼放在另一個(gè)單獨(dú)的微服務(wù)中。 下圖展示了使用這種方法后的系統(tǒng)架構(gòu):
除了新服務(wù)和舊的單體應(yīng)用,還有兩個(gè)組件。一個(gè)是 請(qǐng)求路由 (request router),用來處理過來的(比如HTTP)請(qǐng)求,類似于API網(wǎng)關(guān)。這個(gè)路由發(fā)送與新功能相應(yīng)的請(qǐng)求到新的服務(wù)上,將舊服務(wù)相關(guān)的請(qǐng)求路由到單體應(yīng)用上。
另一個(gè)組件是 膠水代碼 (glue code),用來將服務(wù)與單體應(yīng)用集成起來。一個(gè)服務(wù)很少是隔離存在的,需要訪問單體應(yīng)用的數(shù)據(jù)。膠水代碼就負(fù)責(zé)這些數(shù)據(jù)集成。微服務(wù)組件可以通過它來讀寫單體應(yīng)用中的數(shù)據(jù)。
一個(gè)服務(wù)可以通過三種方式訪問單體應(yīng)用中的數(shù)據(jù):
通過調(diào)用單體應(yīng)用提供的遠(yuǎn)程API
直接訪問單體的數(shù)據(jù)庫
保存一份數(shù)據(jù)的副本,和單體數(shù)據(jù)庫保持同步
膠水代碼有時(shí)被稱為 防腐層(anti-corruption layer) ,可以防止擁有自己原始領(lǐng)域模型的服務(wù),被來自單體領(lǐng)域模型的概念所影響。
膠水代碼可以在兩個(gè)不同的模型間充當(dāng)翻譯官,防腐層這個(gè)詞最初出現(xiàn)在Eric Evans寫的《Domain DrivenDesign》一書中。開發(fā)一個(gè)防腐層不是一個(gè)小工程,但如果你想從單體地獄中走出來,這是很重要的。
用輕量級(jí)的服務(wù)實(shí)現(xiàn)一個(gè)新功能,有很多好處。首先,可以防止單體應(yīng)用變得更難以管理;其次,這個(gè)應(yīng)用可以被獨(dú)立地開發(fā),部署和擴(kuò)展。
然而,這個(gè)方法并不能解決在舊有的單體部分遇到的問題,你還需要破壞原有的單體部分。
縮減單體應(yīng)用的一個(gè)策略是將表現(xiàn)層從業(yè)務(wù)邏輯和數(shù)據(jù)訪問層中分離出來,一個(gè)典型的企業(yè)應(yīng)用至少包括三種組件:
表現(xiàn)層: 這層組件用來處理HTTP請(qǐng)求,實(shí)現(xiàn)(REST)API或者基于HTML的Web UI。在一個(gè)有著復(fù)雜的用戶接口的應(yīng)用中,表現(xiàn)層通常有大量的代碼;
業(yè)務(wù)邏輯層: 應(yīng)用的核心代碼,用來實(shí)現(xiàn)業(yè)務(wù)規(guī)則;
數(shù)據(jù)訪問層: 訪問數(shù)據(jù)庫或信息中介的組件。
在表現(xiàn)邏輯與業(yè)務(wù)和數(shù)據(jù)訪問邏輯之間通常有著明顯的區(qū)分。業(yè)務(wù)層有一個(gè)粗粒度的API,包含一個(gè)或多個(gè)外立面組成,外立面封裝了業(yè)務(wù)邏輯組件。這個(gè)API是自然的『縫合』,所以可以將單體分割為更小的應(yīng)用,一個(gè)應(yīng)用包含了表現(xiàn)層,另一個(gè)應(yīng)用包含了業(yè)務(wù)和數(shù)據(jù)訪問層。分隔后,表現(xiàn)邏輯層應(yīng)用可以遠(yuǎn)程調(diào)用業(yè)務(wù)邏輯層應(yīng)用,下圖展示了改造前后的架構(gòu):
這樣分隔單體應(yīng)用有兩個(gè)主要好處。 首先你可以獨(dú)立地開發(fā),部署和擴(kuò)展兩個(gè)應(yīng)用, 比如對(duì)于表現(xiàn)層開發(fā)者來說,他們可以實(shí)現(xiàn)用戶界面的快速迭代,A/B測(cè)試也很容易實(shí)現(xiàn); 其次,這樣做會(huì)向外開放一個(gè)微服務(wù)也可以調(diào)用的遠(yuǎn)程API。
但是這個(gè)策略只是部分解決方案,很有可能會(huì)變成兩個(gè)混亂的單體應(yīng)用。需要用下面第三個(gè)策略去減少單體部分的比重。
策略3:提取服務(wù)
第三個(gè)策略的目的是將單體中的模塊,轉(zhuǎn)變?yōu)閱为?dú)的微服務(wù)。每次提取一個(gè)模塊,就改造為微服務(wù),單體部分就縮減了。一旦你轉(zhuǎn)化了足夠的模塊,最后不管單體部分是完全消失了,還是變小成了另一個(gè)微服務(wù),都不是問題了。
一個(gè)大型的復(fù)雜的單體應(yīng)用,通常包含數(shù)十甚至上百個(gè)模塊,都可以被提取出來,選擇先提取哪個(gè)是個(gè)問題。可以從容易被提取的開始,積累微服務(wù)的經(jīng)驗(yàn),然后提取那些能給你帶來最大好處的模塊。
通常提取那些頻繁變化的模塊很有用 ,一旦你將這個(gè)模塊提取出來,就可以獨(dú)立開發(fā)和部署它了,可以加速開發(fā)。
另外一個(gè)就是提取那些資源需求和其它部分有很大不同的模塊。 比如將一個(gè)有內(nèi)存數(shù)據(jù)庫的模塊轉(zhuǎn)變?yōu)榉?wù),就可以把它部署在內(nèi)存很大的主機(jī)上;同樣的,提取那些實(shí)現(xiàn)復(fù)雜算法的模塊,就可以把它部署在CPU多的主機(jī)上??傊@樣做有助于你擴(kuò)展應(yīng)用。
當(dāng)決定了提取哪個(gè)模塊后,需要看下現(xiàn)有的粗粒度邊界,可以幫助你將模塊轉(zhuǎn)化為服務(wù)。比如一個(gè)只會(huì)通過異步信息和其它應(yīng)用交互的模塊,就很容易能被改造為微服務(wù)。
第一步是在模塊和單體之間定義一個(gè)粗粒度的接口。 由于單體和微服務(wù)的數(shù)據(jù)互相都有需求,所以它 很像一個(gè)雙向的API。但是在這個(gè)模塊和應(yīng)用的剩余部分之間,有著混亂的依賴關(guān)系和細(xì)粒度的交互模式,所以實(shí)現(xiàn)這個(gè)API還是很有挑戰(zhàn)的。通過域模型實(shí)現(xiàn)的業(yè)務(wù)邏輯,改造起來尤其困難,因?yàn)楦鱾€(gè)域模型間的關(guān)系復(fù)雜。通常需要進(jìn)行大量的代碼修改,去打破這些依賴。
一旦你實(shí)現(xiàn)了細(xì)粒度的接口,就可以將模塊改造為一個(gè)獨(dú)立的服務(wù)。要寫代碼實(shí)現(xiàn)單體和微服務(wù)間的通信,通過使用了IPC機(jī)制的API。下圖展現(xiàn)了一個(gè)架構(gòu)在改造前,改造中和改造后的樣子:
在這個(gè)例子中,模塊Z是待提取的模塊。模塊X使用了Z的組件,Z又使用了模塊Y。
改造的第一步是定義一對(duì)粗粒度的API,第一個(gè)接口是模塊X調(diào)用模塊Z的入站接口,第二個(gè)是模塊Z調(diào)用模塊Y的出站接口;
第二步是將這個(gè)模塊改造為獨(dú)立的服務(wù)。入站接口和出站接口都通過IPC機(jī)制的代碼實(shí)現(xiàn)??赡苣阈枰ㄟ^結(jié)合模塊Z到微服務(wù)底盤框架(用來處理橫切關(guān)注點(diǎn),比如服務(wù)發(fā)現(xiàn))上,來構(gòu)建這個(gè)服務(wù)。
一旦你提取了這個(gè)模塊,就可以獨(dú)立地開發(fā),部署和擴(kuò)展它了。你甚至可以從頭重寫這個(gè)服務(wù),將這個(gè)服務(wù)和單體結(jié)合起來的API代碼就成了防腐層,相當(dāng)于兩個(gè)域模型之間的翻譯官。每次提取一個(gè)服務(wù),都是向著微服務(wù)又進(jìn)了一步,單體的比重會(huì)逐步縮減。
將單體架構(gòu)改造為微服務(wù)的過程是一種應(yīng)用現(xiàn)代化的形式,不應(yīng)該從頭重寫來實(shí)現(xiàn)。而是應(yīng)該循序漸進(jìn)地改造你的應(yīng)用成為一系列微服務(wù)。有三種策略可以應(yīng)用: 用微服務(wù)實(shí)現(xiàn)新的功能;將表現(xiàn)層組件從業(yè)務(wù)和數(shù)據(jù)訪問組件中分離出來;改造單體中的模塊為微服務(wù)。 隨著時(shí)間的推移,你的微服務(wù)比重會(huì)增大,你的開發(fā)團(tuán)隊(duì)的靈活性和速度也會(huì)提高。
靈雀云 ∣ 釋放云的無限潛能
長(zhǎng)按,識(shí)別二維碼,加關(guān)注
聯(lián)系客服