千萬別用微服務(wù)!
本文轉(zhuǎn)載自:眾成翻譯
譯者:賀賀呵呵
鏈接:http://www.zcfy.cc/article/1347
原文:http://basho.com/posts/technical/microservices-please-dont/
關(guān)于譯文,本文英文題目為Microservices - Please Don't,在此我聯(lián)想到多年前的一本書叫做《千萬別學(xué)英語》,其本意是千萬別這樣學(xué)英語,本文的目的亦是如此,因而這里我將題目翻譯為千萬別用微服務(wù),實(shí)則是想建議讀者,千萬別這樣使用微服務(wù)。在此聲明,避免誤解~。
本文改編自2015年12月份我在波士頓Golang大會上的一個(gè)簡短演講。
一時(shí)間,仿佛全世界都在癡迷于討論微服務(wù)(microservice)。無數(shù)的新聞?chuàng)涿娑鴣恚荚诖蹬踔V說微服務(wù)模式如何拯救了他們的工程架構(gòu)。你甚至可能曾在這樣的公司工作過,他們大肆宣傳這種輕量神奇的微小服務(wù),并極力宣傳他們是如何使用微服務(wù)解決舊系統(tǒng)的冗余,難以維護(hù)等詬病的。
當(dāng)然,就理想目標(biāo)而言,沒有什么比現(xiàn)在的技術(shù)更先進(jìn),更靠譜的了。理想的美便在于這幾個(gè)月以來,我們始終在為接近完美而不斷地努力著。
我將闡述一些關(guān)于微服務(wù)的主要誤區(qū)和坑點(diǎn),并借助于一些像上述那樣,將龐大的遺留系統(tǒng)按照微服務(wù)的理念進(jìn)行拆分的企業(yè)作為我的論據(jù)。當(dāng)然,我并不是想用此文來說明微服務(wù)是不好的,而是想通過我的敘述,能夠讓大家在決定使用這種模式之前提出一些問題,多一些思考,再決定是否向這種模式遷移,是否能夠真正的適合你們自己的項(xiàng)目。
到底什么是“微服務(wù)”?
目前真的沒有對于微服務(wù)到底由什么組成或者不包含什么的一個(gè)完美的定義,但是已經(jīng)有少數(shù)微服務(wù)的支持者,編撰處理一套比較合理的說明。
再強(qiáng)調(diào)一下,微服務(wù)不是一個(gè)復(fù)雜的整體。在實(shí)踐過程中,微服務(wù)真正被賦予的意義在于,其僅僅能夠處理盡可能有限的域空間,以便其能夠盡可能做最少的事情,在整個(gè)應(yīng)用棧中達(dá)到預(yù)期的服務(wù)目標(biāo)。舉一個(gè)具體的例子,如果你有一個(gè)需要“登錄服務(wù)”的銀行,你想要做的最后一步便是獲得用戶的交易記錄。你需要將這一需求移交給“交易服務(wù)”一類的進(jìn)行處理(記住,命名是一件值得商榷的事情,并不是輕而易舉的)。
此外,當(dāng)人們討論微服務(wù)的時(shí)候總是在潛意識里認(rèn)為服務(wù)之間是通過遠(yuǎn)程交互的。因?yàn)樗鼈兪仟?dú)立的進(jìn)程,而且經(jīng)常運(yùn)行在遠(yuǎn)離其他服務(wù)的地方,所以使用REST,或者RPC協(xié)議一類的遠(yuǎn)程協(xié)議進(jìn)行通信變得十分自然。
一開始,這看起來著實(shí)很簡單,我們好像只需要把像REST API之類的按照其作用域,封裝成幾個(gè)小的包,并使這些包之間通過網(wǎng)絡(luò)進(jìn)行通信。以我的經(jīng)驗(yàn)來說,這里有5條大家所認(rèn)為的“真理”,在我看來是有一定問題的:
微服務(wù)能夠使代碼簡潔化
微服務(wù)能夠使對具有單一目的事物進(jìn)行編碼變得更簡單
微服務(wù)的使用能讓其比一個(gè)整體系統(tǒng)運(yùn)行的更快
微服務(wù)能夠使得工程師們不用只限于工作在同一個(gè)代碼庫,方便解耦協(xié)作
微服務(wù)使得系統(tǒng)的彈性更容易掌控,通常在這里以使用Docker的方式來完成
誤區(qū) #1: 更簡潔的代碼
“你不需要引入一個(gè)網(wǎng)絡(luò)邊界作為借口來寫更好的代碼”
一個(gè)簡單的事實(shí)是微服務(wù),與其他的建模方法相同,需要更簡潔,維護(hù)性更高的代碼。因?yàn)槲⒎?wù)確實(shí)能夠使得參與工程的部分相對減少,但是這就幾乎等同于說你是通過拋棄一些期望的需求,來解決你所謂的困難。你并沒有真正意義上的攻克難關(guān),而只是拋棄了許多你可以選擇的需求點(diǎn)。
一個(gè)主流的設(shè)計(jì)微服務(wù)的方式是,圍繞邏輯上擁有獨(dú)立域的“服務(wù)”,來設(shè)計(jì)你的內(nèi)構(gòu)件架構(gòu)。這從一方面反映出微服務(wù)可以幫助你確保針對域開發(fā)的獨(dú)立性,也有利于幫助你防止從主要業(yè)務(wù)邏輯,逐漸蔓延到其他地方。另一方面,使用這些微服務(wù)可以避免大量的網(wǎng)絡(luò)使用,以及降低從網(wǎng)絡(luò)中帶來的潛在風(fēng)險(xiǎn)。
使用微服務(wù)的更大好處是,能夠更好的體現(xiàn)面向服務(wù)架構(gòu)(SOA)的思想,一旦你決定使用微服務(wù)架構(gòu),那么你事先一定已經(jīng)做了足夠的工作,進(jìn)行了充分的準(zhǔn)備,并且對你所定義的域有了非常到位的理解,以至于你能夠從中準(zhǔn)確的抽取這個(gè)域。一個(gè)經(jīng)得起推敲的SOA架,應(yīng)該構(gòu)始于代碼本身,并且隨著時(shí)間推移,最終能夠落實(shí)到具體應(yīng)用棧的物理拓?fù)浣Y(jié)構(gòu)中去。
誤區(qū) #2: 微服務(wù)更簡單
”分布式事務(wù)從來不會很容易“
盡管一開始覺得微服務(wù)看上去很簡單,但是大部分的域(特別是在需要開發(fā)原型,制定戰(zhàn)略以及多次重復(fù)定義域本身的一些新興企業(yè))對于整齊切分業(yè)務(wù)成一個(gè)一個(gè)的小方塊是沒有什么幫助的。更多的情況是,一個(gè)域想要正確地完成其使命,必須要從借助其他部分獲得數(shù)據(jù)才可以。特別是當(dāng)其需要委托外部域進(jìn)行寫數(shù)據(jù)任務(wù)時(shí),這會變得尤為復(fù)雜。一旦你打破了一個(gè)域的封閉環(huán)境,并且需要在外部其他域存儲更改數(shù)據(jù),那么你在做的已經(jīng)是分布式事務(wù)了(例如Sagas)。
請求多個(gè)遠(yuǎn)程服務(wù)會帶來許多復(fù)雜的問題。是并行還是串行處理請求?你是否發(fā)現(xiàn)了所有可能在流程中任何節(jié)點(diǎn)上發(fā)生的潛在風(fēng)險(xiǎn)(在應(yīng)用和網(wǎng)絡(luò)層面上),以及對這些風(fēng)險(xiǎn)的評估?通常情況下,這些分布式事務(wù)有自己的風(fēng)險(xiǎn)異常處理方式,需要做許多努力以確保其不僅僅能發(fā)現(xiàn)異常,并且使其能夠決定如何處理并修復(fù)自身的問題。
誤區(qū) #3: 微服務(wù)運(yùn)行更快
“你可以通過簡單地應(yīng)用一些額外的法則,使得一個(gè)龐大的系統(tǒng)獲得卓越的性能”
拆分服務(wù)是一件非常困難的事情,實(shí)際上你經(jīng)常可以通過減少系統(tǒng)處理事務(wù)的數(shù)量,以使得獨(dú)立系統(tǒng)獲得更高的運(yùn)行速度,或者減少獨(dú)立系統(tǒng)加載的依賴等等。
但是從根本上來說,這是種非常逗比的辦法。當(dāng)然我不是質(zhì)疑那些專注于微服務(wù),通過代碼隔離以加速服務(wù)的人,但是你要明白,這樣做的同時(shí),為了處理大量的調(diào)用,你也帶來了更多的網(wǎng)絡(luò)開銷。雖然網(wǎng)絡(luò)可以“足夠快”,但是它也快不過直接調(diào)用內(nèi)存中的代碼。
此外,許多講述性能優(yōu)化的故事大多是完全以新的語言或技術(shù)棧描述的,而不只是講述在微服務(wù)中構(gòu)建代碼的一些概念。使用新語言像Scala或Go語言(兩種在微服務(wù)中比較流行的語言)去重構(gòu)一個(gè)用Ruby on Rails或者Django,甚至是NodeJS寫的應(yīng)用,依賴于技術(shù)選型本身來提升性能。但是這些語言并不“關(guān)心”你是否將它用來構(gòu)建“微”服務(wù)進(jìn)程,它們之所以性能更好,原因很簡單,因?yàn)樗鼈儽旧淼木幾g環(huán)境等語言特性就很出色,與你的微服務(wù)其實(shí)并沒太大關(guān)系。
最后,對于處在僅需要啟動(dòng)時(shí)使用的應(yīng)用來說,CPU、內(nèi)存的性能幾乎不成問題。問題主要出現(xiàn)在I/O上,一個(gè)額外的網(wǎng)絡(luò)調(diào)用只能帶來更多的I/O負(fù)載,反而適得其反。
誤區(qū) #4: 方便工程師解耦協(xié)作
“一群軟件工程師維護(hù)一個(gè)獨(dú)立的代碼將可能導(dǎo)致“不是我的問題”的詬病”
好像從表面上看這使得使用更小規(guī)模的團(tuán)隊(duì)解決一個(gè)較小規(guī)模的問題變得簡單,但是最終可能總會帶來一些其他問題,比如解決問題的余地變小,使得最終成果物無法達(dá)到預(yù)期水準(zhǔn)。
最繁瑣的事情往往是最簡單的,可是一個(gè)微小的改動(dòng)都使得你不得不去運(yùn)行不斷增長的服務(wù)。這意味著你必須投入時(shí)間精力去為軟件工程師在本地構(gòu)建和維護(hù)一個(gè)能夠簡單地運(yùn)行所有服務(wù)的系統(tǒng)。使用Docker可以方便地實(shí)現(xiàn),但是當(dāng)有變動(dòng)發(fā)生時(shí),這仍需要有人維護(hù)。
此外,微服務(wù)使得測試變得更難,因?yàn)橐鲆淮渭蓽y試,需要去了解與之有關(guān)的全部服務(wù),捕獲全部可能發(fā)生的錯(cuò)誤情況等等。簡單理解系統(tǒng)就可以花費(fèi)很長的時(shí)間,有這時(shí)間還不如去繼續(xù)開發(fā)系統(tǒng)。我不是說告訴任何工程師,理解系統(tǒng)是一種浪費(fèi)時(shí)間的表現(xiàn),我只想告誡人們在你真正需要這些東西之前,不要過早的引入過高的復(fù)雜度。
最后,這還會帶來一些社會問題。存在于N多服務(wù)之間的bug,需要多個(gè)團(tuán)隊(duì)殫精竭慮的協(xié)作才能得以修復(fù)。同時(shí)這會使得團(tuán)隊(duì)缺少責(zé)任感,并講責(zé)任推卸給其他團(tuán)隊(duì)。而當(dāng)所有人都圍繞著相同的代碼開發(fā),他們會彼此了解,反而會使得系統(tǒng)能夠順利發(fā)展。人們在一起解決問題時(shí)會變得更有積極性,相反他們只會像各個(gè)諸侯國的國王一樣死守著自己的一畝三分地。
誤區(qū) #5: 更好的彈性
“使一整個(gè)龐雜系統(tǒng)變得有彈性的方式,與使用微服務(wù)一樣簡單”
不能說將你的服務(wù)打包成一個(gè)一個(gè)的獨(dú)立單元,然后使用像Docker這樣的得力工具進(jìn)行水平彈性擴(kuò)展是一件錯(cuò)誤的事情。
但是,如果說只能使用微服務(wù)之類的來實(shí)現(xiàn)上述情況,那么就大錯(cuò)特錯(cuò)了。一個(gè)龐雜的整體程序同樣也可以使用上述方法來創(chuàng)建。你可以將這個(gè)整體應(yīng)用劃分成許多邏輯簇,每一個(gè)簇僅負(fù)責(zé)整體系統(tǒng)的一個(gè)子集部分。舉例來說,API請求,你的個(gè)人儀表板,以及你的后臺服務(wù)器,可能都需要共享一個(gè)代碼,但是你不需要在每一個(gè)簇上去處理全部這三個(gè)子集。
那么優(yōu)點(diǎn)是,就像在微服務(wù)中存在的那樣,你可以按照既定負(fù)載去客制化定義每個(gè)簇的范圍,并根據(jù)既定負(fù)載對他們分別進(jìn)行流量調(diào)整。所以在你決定使用微服務(wù)之前,你也可以用同樣的方法來使你的龐雜系統(tǒng)變得有彈性。
我們什么時(shí)候應(yīng)該使用微服務(wù)?
“當(dāng)你已經(jīng)準(zhǔn)備好組織一個(gè)工程”
我偏向于使用通過討論什么時(shí)候是采用到這種方法的正確時(shí)間的方式,來決定是否選用這個(gè)方法。(如果你已經(jīng)開始使用了這種方法,那么應(yīng)該討論使用這種方法開始的方式是否正確)。
創(chuàng)建一個(gè)可靠的,可用的微服務(wù)的一個(gè)非常關(guān)鍵的步驟就是理解“域”的具體內(nèi)容。如果你不能很好的理解它,或者你還在嘗試去搞懂它,那么這樣的微服務(wù)可能是百害而無一利的。但是如果你能夠深入的理解“域”, 你便可以清晰地描述“域”的邊界是什么,它依賴于什么,這樣的微服務(wù)才有可能是一個(gè)正確的選擇。
創(chuàng)建一個(gè)可靠的,可用的微服務(wù)的另一個(gè)關(guān)鍵的步驟是你的工作流,特別值得注意的是它們是如何與分布式事務(wù)產(chǎn)生關(guān)聯(lián)的。如果你了解各種請求在系統(tǒng)中的處理方式,并且知道這些處理方式會在哪里宕掉,怎么宕掉的以及它們?yōu)槭裁村吹舻鹊龋憧梢蚤_始著手去創(chuàng)建一個(gè)分布式模型,來處理你的請求。
同時(shí),理解你的工作流程也是一定程度上在監(jiān)控(monitoring)你的工作流,并且這在某種程度上應(yīng)該成為你工程的核心部分。你需要精通系統(tǒng)的各個(gè)部分并且需要大量的數(shù)據(jù)來分析為什么某些部分表現(xiàn)欠佳,甚至是報(bào)錯(cuò)。如果你有一個(gè)靠譜的方法監(jiān)控著整個(gè)系統(tǒng)的各個(gè)部分,那么當(dāng)你水平擴(kuò)展系統(tǒng)時(shí),你就可以開始理解你的系統(tǒng)行為了。
最后,當(dāng)你能證明項(xiàng)目和業(yè)務(wù)的實(shí)際價(jià)值時(shí),轉(zhuǎn)移到微服務(wù)將幫助你發(fā)展,壯大,以及獲得更多的利潤。雖然嘗試新興技術(shù)是件非常有趣的事情,但是許多企業(yè)到頭來還是受制于成本。如果你因?yàn)橐粋€(gè)說“整體且繁雜的系統(tǒng)是不可取的”博客,不得不推遲發(fā)布一個(gè)可以給企業(yè)帶來紅利的新功能,那么你就需要通過某些方式向企業(yè)證明為什么其不可取。有些時(shí)候這種權(quán)衡考慮是值得的,而有些時(shí)候卻不盡如人意。所以如何抉擇,選擇正確的技術(shù)路線,將在很長一段時(shí)間使你所在的企業(yè)飛黃騰達(dá)。
結(jié)論
希望當(dāng)下一次有人建議你使用微服務(wù)架構(gòu)時(shí),你能夠多一些思考,想一想上述的那一系列條件。像我開頭說的那樣,我并不是說微服務(wù)是不可取的,相反,如果不從上述的觀點(diǎn)進(jìn)行細(xì)致的權(quán)衡,就跳入微服務(wù)的浪潮中去,難免會像一個(gè)不會沖浪的人,沒有精湛的技術(shù)與清醒的頭腦迎擊浪潮,最終沉入大海,以失敗而告終。
如果你想問我應(yīng)該怎樣做,我想說,我主張通過使用簡潔的代碼定義模塊,以及切分獨(dú)立服務(wù)等方式構(gòu)建“內(nèi)部”服務(wù),如果到時(shí)候真正需要這么做的話。這并不是唯一的方法,更不是拯救糟糕代碼的靈丹妙藥。但是這將會使你比在做好準(zhǔn)備之前,去嘗試處理微服務(wù)做的更好,更快。
Sean Kelly(我們親切地稱其為"Stabby")已經(jīng)從事軟件工程師行業(yè)12年了。他是Basho公司“Taishi”項(xiàng)目的參與成員,現(xiàn)在在Komand公司工作,一個(gè)致力于安全服務(wù)編排,無需編碼,授權(quán)給安全團(tuán)隊(duì)迅速進(jìn)行自動(dòng)化構(gòu)建安全環(huán)境的自動(dòng)化平臺的公司。Komand可以幫助團(tuán)隊(duì)連接他們的工具,構(gòu)建動(dòng)態(tài)工作流,優(yōu)化人力,加快事件的響應(yīng)速度。你可以在twitter上關(guān)注他 @StabbyCutyou