微服務是現今常見的一個理想模式,然而它們可能不該有這一地位。本文作者Sean Kelly解讀了關于微服務的5個謬誤,并告訴你究竟什么樣的企業才適合采用微服務架構。
有一段時間,好像人人都對微服務推崇不已。每打開一次新聞訂閱,你都免不了會看見有些從來沒聽說過的公司大肆鼓吹微服務如何救他們的工程組織于水火之中。你所在的公司也很可能受到了微服務宣傳的狂轟濫炸。這些神奇的小東西好像真的無所不能,恰恰是你公司里那個龐大而老舊的數據庫的救星。
當然了,現在回過頭來看,這些話簡直假得沒邊。這種后見之明的魅力就在于,它總是相當接近于我們在幾個月以前以為自己擁有的2.0眼力。
我在本文中將總結幾個最主要的謬論以及微服務運動中出的各種岔子,這些素材來源于一位公司員工。他所在的公司也被微服務無所不能、趁早打散整合應用才是上策的論調席卷其中。雖然我這篇博文的本意并不是“微服務就是很差勁”,但我還是希望每一個讀者看完以后都能從多角度考慮一下微服務架構對他們來說是否適合。
到底什么是“微服務”呢?
雖然有些支持者已經逐條編出了一套蠻合理的微服務條件,但對于微服務包括什么不包括什么,實在是沒有一個明確的定義。
再回到它的名字來說,這自然不是一塊龐然大物。但在實踐中,它的實際含義是一種盡可能少涉及不同領域的一種微型服務,這樣做的目的是只專注于其使用者設定的目標而精簡步驟。舉一個具體的例子吧,如果你開了一家有登錄功能的銀行,你最不想做的事情就應該是獲得讀取用戶金融交易記錄的權限。你會把這個功能扔進“交易服務”之類的部分里去(要記住起名字也不是件簡單的事?。?/p>
此外,人們在提及微服務時,他們還暗指那些需要和其他服務遠程交互的服務。既然它們是各不相同的進程,又經常是各自不在同一個位置,一般就需要使用REST(代表性狀態轉移)服務或者某種RPC(遠端進程調用)協議使這些進程能夠遠程交互。
一開始這事看起來很簡單。似乎我們只需要把這些小東西放進REST的應用程序接口之類的東西,然后它們就會在網上交互啦。然而我的經驗表明,人們深信不疑的這五個“事實”其實并不是真的。
1、微服務會讓代碼更加整潔。
2、編寫單一目的程序很簡單。
3、它們比整合性的程序更快。
4、工程師們在不同代碼庫下工作比較簡單。
5、這是搞定自動擴展的最簡便方式(包括Docker在內)。
謬誤#1 代碼更整潔
“就算沒有這個網絡邊界,你也完全有理由把代碼寫得更好。”
最顯而易見的事實是,要寫出更整潔或者更可持續的代碼,微服務或者其他建構技術堆棧的方式并不是必要條件。沒錯,程序涉及的方面少了,你偷懶或者不經思考就敲代碼的幾率也會下降。然而這個說法就像是,把人們想要的東西都下架了,犯罪幾率就會下降一樣。你并沒有解決問題,只是排除掉了很多可能性罷了。
把代碼內部建構在占有部分域的邏輯性服務上是現在的一種常見做法。這一方法化用了微服務的一個重要概念:在明確管理域的同時保持核心商業邏輯不會分散到不同部分。此外,使用這一方法也防止了網絡被過度占用的情況,因此也不會有其引發的潛在錯誤出現。
基于其仿照了圍繞微服務建立的服務導向型結構,這一方法的額外收益是一旦你決定要改用微服務方式,很多設計工作都已經提前完成了,對于域的理解也已經足夠充分可以具體化了。一個嚴密的服務導向型架構會從代碼本身出發,隨時間推移向堆棧中的物理拓撲拓展。
謬誤#2 更簡單
“分散式交互永遠都不會簡單。”
一開始這樣做看上去會很簡單,但是大多數域(尤其是那些需要創建原型、依原型為中心發展并逐步重新定義域的新公司來說)都不會任你雕琢然后裝進不同的小盒子里的。一個域的給定部分經常需要獲取其他部分的數據以正常運作,當他們需要委派在域外寫入數據的權限時,這就變得更難了。一旦突破了自己的影響范圍,需要把其他部分也卷入到儲存修飾數據的請求列中,就進入了分散式事務(有時也稱作長事務)處理的領域。
在給定請求中牽涉多個遠程設備這一步驟困難重重。你是能并行調用,還是只能逐個完成呢?你是否了解這一連串環節中任何節點都可能會出現的可能錯誤(應用層面和網絡層面皆有可能)?這對于請求列來說又意味著什么呢?分散式事務處理的每一部分都需要自我解決可能出現的問題的途徑,這工作量可不小,既得了解可能出現的問題,還得決定應該如何解決這些問題。
謬誤#3 更快速
“只要多應用一些附加規則,你就可以讓整合應用的表現大大提高。”
想要去除這一謬誤還是有些困難的,因為事實上減少任務和加載物數量等等之后個人系統總會變得更快。
但這一說法終究是不具備普遍性的,我不懷疑那些轉向微服務的人們在服務速度提高的同時也發現了個人代碼路徑的孤立,但你得了解到你也是在許多調用之間加入了網絡部分。互聯網永遠都不會像共駐代碼調用的速度那么快,雖然很多時候它是“足夠快”的。
此外,關于提高性能的說法中,很多實際上都完全是在吹捧技術堆棧的一種新語言,而不僅是關于在外部構建代碼以運用微服務的概念。用Scala或者Go(兩種微服務建構的常用工具)的語言重寫Ruby on Rails,Django或者NodeJS這些老軟件本來就會基于其所用技術帶來性能上的提升。但是這些語言并不“在意”你是否把他們運載的進程叫做“微”服務,他們只是因為編譯方式等因素而有更好的表現而已。
而且,對于初創公司的大部分應用,CPU或者內存的性能幾乎從來都不是問題。問題在于產出比,而額外的網絡調用只會增大你的投入產出比。
謬誤#4 對工程師來說更簡單
“一批在孤立代碼庫中工作的工程師只會招致‘不怪我’綜合癥。”
雖然在測試階段,讓小團隊集中在小問題上看起來比較簡單,但這最終會經常帶來很多其他問題。你解決問題的空間會縮小,收獲也會縮水。
最大的問題是,不管要做什么,就算做出最小的改變,你也得同時運行越來越多的服務。這意味著單單這種讓工程師在本地運行所有程序的方法,也需要你投入精力和時間來打造和維護。像Docker這樣的工具可以把這件事變得更簡單,但是隨著程序的改動,還是需要有人來進行維護。
此外,這也會讓寫入測驗變得更加困難,因為編寫一套合適的綜合測試要求編寫者了解所有給定交互會涉及到的服務、捕捉到所有可能的錯誤案例等等。單單是了解系統就要花掉更多的時間,而這些時間本來是可以用來改進系統的。雖然我從來都不會跟程序員說花在理解系統上的時間是無謂的,但我絕對會勸告人們不要在確定自己需要之前過早提高系統的復雜程度。
最終,它還會帶來人際問題。橫跨多個服務、需要多次調節的漏洞真的能讓人心力交瘁,因為它需要多組工程師協調、同步努力來解決。還可能出現這種情況:人們都覺得自己不應該負責,盡可能地把問題都推給別的組。而當工程師們在同一個代碼庫中工作時,他們對于彼此的了解會不斷深化,系統本身也會隨之不斷發展,他們會精誠協作解決問題,而不是各據一方互不往來。
謬誤#5 可擴展性更強
“擴展微服務和擴展整合程序其實是一樣簡單的。”
不是說將服務分成離散單元然后通過Docker這樣的工具來擴展不是一個平行擴展的好方法,但是要說這種方法只能用于微服務,那可就不對了。整合應用也完全可以用這種辦法。你可以創建一個整合應用的邏輯集合,只用來處理流量的一個子集。比如,你的內置程序接口請求、儀表前段和背景工作服務器可以共用一個代碼庫,但是你不用分別解決這三個子集的問題。
這一點的好處是,在微服務中,你可以將單個集群調整到其給定的工作負載,以及單獨擴展它們以響應給定工作負載的流量激增。所以,在一開始微服務引導你使用這種方法的時候,你應該知道它也完全適用于擴展整合應用的堆棧。
那你應該在什么時候用微服務?
“在你們的整個工程師團體都準備好之后。”
讓我們來復習一遍應該什么時候轉而使用這一方法(或者是,如果你的公司剛開始運行,該怎么判斷這是否是正確的開始方式)并以此作為結尾。
要構建一個嚴密的、可工作的微服務方式,最重要的那一步就是搞清楚你在哪一領域工作。如果你現在還不了解或者還在努力弄懂,微服務可能對你來說將是弊大于利。然而,如果你已經有了深刻的理解,并且知道了邊界在哪里、相關性如何,那么微服務方式對你來說可能是正確的選擇。
另外一件需要掌握的事情是你的工作流——具體來講,是工作流與分散式事務如何關聯。如果你知曉每一種類的請求在你的系統中運行的路徑,還了解這些路徑可能出錯的位置、方式和原因,那么你就可以開始建立處理請求的分散式模型了。
同時對于工作流的了解也算是一種監控。關于監控的學問可比“微服務與整合應用孰優孰劣”大多了,但它仍然應該是你進行編程工作時的重點。要弄懂你系統的各個部分中為什么有的表現不佳甚至頻出錯誤,你得有大量數據以供調遣。如果你建立了一個堅實的監控系統,你就能隨著系統運作增進對其各部分的平行了解。
最終,當你能夠給你的工程團隊展現出真真切切的價值時,微服務就能給你帶來公司成長、規模擴大以及收入增加了。雖然創造和嘗試是很有趣的,但在一天工作結束的時候,對很多公司來說最重要的東西還是他們的底線在哪。如果你因為看到一篇博客說整合應用不怎么樣而推遲上線一個可以給公司帶來盈利的新功能,那你得好好跟公司交代一下了。有時候這些抉擇是值得的,而有些時候不是。選擇好自己的戰場,打你該打的技術仗,這樣以后你才能游刃有余。
摘要:
我希望下次再有人給你推薦微服務時,你會考慮到這些新的條件和問題。如我開頭所說,我寫這篇文章不是為了告訴你微服務是不好的,而是說,不假思索地采用微服務會給未來的發展埋下隱患。
如果你要問我提倡哪種,我會建議先通過定義清晰的代碼模塊構建內部服務。如果未來有需要,再把他們分置到不同的服務中。這一方法不是唯一解,也不是解決代碼混亂的靈丹妙藥,然而比起在沒做好充足準備之前就開始應付一大堆的微服務來說,這一方法可以讓你前進得更快一些。