- 微服務架構深度解析:原理、實踐與進階
- 王佩華編著
- 3614字
- 2021-07-07 16:03:54
1.3 架構設計哲學
如果說軟件開發的本質是不斷挖掘問題領域中隱藏的錯綜復雜性,那么架構解決的問題就是如何管理這些復雜性。而在軟件領域,最為復雜的軟件實體莫過于軟件操作系統。從數以千計的工程師參與開發的UNIX操作系統到Linux開源系統的成功,越來越多的人開始關注和思考UNIX技術背后隱藏的設計哲學。
UNIX設計哲學概括為一句話就是“小而專注”??梢哉f,微服務架構理念和UNIX設計哲學一脈相承,微服務將UNIX設計哲學中的核心準則通過概念的抽象,描述成了更加通用的架構風格和設計原則。下面,讓我們跟隨經典重新認識在AT&T公司誕生的UNIX操作系統和它背后的設計哲學,所謂“溫故而知新”,這些經典思想能加深我們對微服務架構的認識和理解。
1.3.1 小即是美
在軟件的設計開發過程中,軟件系統的規模很容易膨脹,工程師喜歡將紛繁復雜的功能全部堆積在一個程序中,這樣的好處是代碼唾手可得。然而,根據二八法則,實際運行中的代碼其實往往只占到我們代碼量的20%。所以,切忌將大而全的工程作為我們的目標,相反,我們應該將功能設計成為實用且簡潔的小程序[2]。
在我的職業生涯中,經歷過很多設計復雜、規模龐大的單體系統。有的是基于C++編程語言的后端大型系統,有的是使用J2EE-XML編程風格、交織著龐大功能模塊的巨石型應用,這些項目通常由10人以上的研發團隊負責,動輒百人/月的開發計劃。這種大型單體系統在上線后,時常發生系統內存溢出、系統宕機等問題,讓開發人員心驚膽戰。往往一個小問題會影響整個系統的正常運行,排查解決過程需要檢查整個工程的代碼邏輯,開發人員疲于解決生產上線后的各種問題和Bug修復。
事實證明,我們需要將龐大、復雜的系統分解成小程序。正如UNIX系統中強調的KISS(Keep it Simple and Stupid)原則,可以說整個UNIX系統都是由數目眾多的小程序組合完成各種復雜的操作系統功能的。每個小程序只完成其中一小部分功能。表面上,這些小程序都很低效,但是正是通過像搭積木一樣將不同功能的小程序通過變換順序和組合的方式,完成了各種意想不到的豐富而強大的功能。
小程序之美體現在響應變化上。通過小程序可以將這種改變控制在足夠小的范圍,保證不會給整個系統帶來巨大的影響。
小程序之美體現在工程結構的簡化和完備上。當你的程序充滿了個性化的反射調用和程序員獨特的編程思維痕跡,那么應該反省你的代碼是不是已經脫離了小程序應有的代碼簡潔和易于理解。
小程序之美體現在性能優勢上。小程序對外部的依賴少,從而可以快速啟動,基于資源收斂式的反應式編程模型可以提升性能并減少資源浪費。
小程序之美體現在團隊合作和獨立演進上。清晰的邊界劃分是團隊協作的有效手段,而體現在微服務架構上就是服務提供者和消費者可以預先訂立契約,可以根據契約獨立、并行開發各個服務,這樣就實現了服務之間的解耦,使其功能能夠獨立進化。
1.3.2 做好一件事
小程序應該只做好一件事,應該保持對一件事的專注力。在UNIX設計哲學中,解決一個問題并將問題解決到完美,比同時解決多個問題更為重要。然而在我們的工程中,隨著項目的進展,我們很難將龐大的代碼庫進行清晰的模塊劃分,更糟糕的是我們很多時候并不知道這些模塊之間的服務界限。
在UNIX操作系統中,我們可以發現很多命令的強大之處正是只有單一的功能,并將這件事干好,也就是所謂的“Do one thing,do it well”。而且UNIX首創的管道可以把這些命令任意地組合,以完成一個更為強大的功能。這些哲學到今天都在深深地影響著整個計算機產業,下面我們羅列一些經典的UNIX命令程序,如下表所示。

微服務的目標是獨立地完成一件事并做到最好。微服務可以根據業務的邊界來確定應用的行為,這樣它不僅可以很容易地確定某個功能邏輯對應的代碼,而且由于該服務專注在某個邊界之內,因此可以更好地避免由于代碼庫過大衍生出的復雜性問題。
1.3.3 快速建立原型
只做好一件事的小程序可以讓我們的項目輕裝前行,我們不必再擔心系統會成為一個龐然大物而無法控制。對于單體巨石應用,需要制定周密的計劃去編寫設計文檔,以及為聚合不同模塊之間功能而準備腳本和代碼,相比之下,自治的小程序更加清楚自己的業務職責和業務范圍。開發人員可以對小程序快速建立原型,縮短服務的交付周期,迅速構建可以供用戶使用的程序和服務,并從結果中得到反饋,向最終的目標前進。
在UNIX設計哲學中,軟件發生變化是不可避免的,這種變化來源于溝通的失敗、需求理解的差異、知識經驗的限制等,所以軟件工程相比任何其他工程都更加容易返工,需要軟件從業者不斷試錯、總結、驗證,并根據期望重新建立共識。盡快建立原型是一個重要的步驟,有了具體的原型,可以降低項目的風險,可以給客戶展示和進行可視化跟蹤。往往這個過程是伴隨著系統的迭代和演進的。
回到微服務,我們說微服務架構在建立原型上有天然的優勢,微服務架構的很多特性能讓我們快速落地和逐步地獨立完善和迭代項目。
● 微服務架構強調細粒度的服務,在服務規模上盡量精簡和務實。
● 微服務架構只做好一件事,沒有過多的其他因素干擾,我們可以將注意力集中在完成這件事情上,盡量排除干擾因素。
● 微服務架構提供輕量級的通信集成方式,有利于集成測試和驗證結果。
● 微服務架構建立在已有的技術基礎之上,能夠更加快速地構建、發布和體驗應用。
1.3.4 軟件的復利效應
在軟件工程實踐中,在項目的啟動或者早期階段,經常會面臨技術方案的選擇問題。一些軟件工程師會陷入自我保護的狀態,認為別人的方案存在缺陷,自己如果重新做會比現有方案做得更好,但其實可能是因為他不了解“每個軟件可能都是在某種約束條件下工作的,而且適用于某些特定的場景”。我們把這種現象稱為“NIH(Not Invented Here)綜合征[3]”。NIH綜合征的結果就是重新發明輪子,患有NIH綜合征的人相信內部開發更安全、更高效、速度更快、維護成本更低,然而,其實他們并不一定能夠使用創新的方案來解決實際的問題。
目前,軟件正朝著規?;蜆藴驶姆较虬l展,標準驅動下的軟件開發和集成方式,要求你的工作能夠集成到標準中,而不是另起爐灶。NIH綜合征的危險之處就是,你的軟件、你的服務無法與標準對接,你的系統可能成為一個孤島系統。例如,在你提供了私有協議的RPC方式暴露服務的情況下,你的服務只能生存在自己的閉環體系中,而且基于標準的HTTP接口API的調用方式很難與你的服務集成。
項目的生命周期受很多因素的影響,預算、人員成本、推廣等都會給項目的持續發展帶來風險,NIH綜合征會給項目的持續發展帶來額外的成本問題,因為你缺少相關人員、資料、生態的支持,而采用標準化的技術可以從互聯網中得更多的技術、社區和人員的支持。
下面讓我們看看UNIX的實踐,所謂“前人種樹、后人乘涼”。查閱UNIX坎坷的發展歷程,我們就會發現,UNIX操作系統是在數千名工程師的辛勤努力的基礎上發展起來的。UNIX中復雜的邏輯都是由小的程序累積而成的,聰明的程序員總是可以借用前人寫的優秀代碼實現自己的功能,這樣才能更快、更好地擴大軟件的影響力和威力,放大自己的工作成果。
在微服務領域,我們已經看到非常多優秀的微服務技術框架,例如本書后續會詳細介紹的Spring Boot微服務腳手架。它是在Spring生態的基礎上發展起來(Spring Boot也是軟件復利的成果)的開發框架。在Java世界,Spring Boot帶給廣大開發者的是更加簡化的工作流程,以及更高的開發效率。這也是Spring出色的地方,彌補了Java在簡化工作上的空白,讓開發者開發者避免了大量的重復工作。Spring Cloud則降低了建立分布式系統的復雜度,Spring的忠實熱愛者可以充分享受軟件復利效應帶來的福利。
1.3.5 可移植性優先
可移植性在軟件工程中的重要性無論怎么強調都不過分,因為這個哲學正是UNIX操作系統能夠成為“常青樹”的秘訣。在UNIX環境下,Shell腳本具備更好的可移植性,Shell腳本通常由多個UNIX命令組成,Shell可執行文件間接由UNIX命令解釋器解釋執行。如果你對效率要求不高,可以盡量使用Shell腳本執行,Shell腳本語法簡單、使用方便、運行之前不需要編譯、具備很強的文本處理能力,開發效率也比較高。
但是Shell歸根結底還是一種弱類型語言,沒有嚴格的數據類型檢查。Shell的缺點是I/O性能不高,同時因為是解釋性語言,對于值計算類型的問題也沒有很好的支持。如果你想讓程序具有更好的可移植性,可以選擇具備跨平臺能力的編程語言。Java便是一種跨平臺語言,基于JVM虛擬機平臺,Java制定了字節碼執行引擎規范,可以滿足程序的可移植性要求。這也是Java語言發展這么多年依然具有強大生命力的重要原因。
在微服務時代,容器的隔離性和可移植性可以說為軟件開發帶來了革命性的顛覆,Docker技術通過采用LXC[4]虛擬化手段,利用Linux系統的Namespace和Cgroup技術確保了應用程序與資源的隔離。Docker通過和各大廠商聯合發起OCI(開發容器標準),規范了應用運行時的容器鏡像標準。鏡像的打包、構建、部署、命名過程都按照統一規范進行,進而標準化了底層運行時支撐環境,這樣你就可以在統一的容器環境下靈活地交付、部署和移植代碼。通過采用Docker容器技術,我們將不需要再關注操作系統的特殊性和差異性,可以更關注應用程序本身,底層多余的環境因素可以通過容器提供的虛擬環境來屏蔽。也就是說,我們的微服務具備了更好的可移植性,真正做到了“一次構建,隨處運行”。