顛覆微服務認知:深入思考微服務的七個主流觀點

一、逃離單體系統,擁抱微服務?

單體系統和微服務的區別在於,一個單體系統是一個大而全的功能集合,每個服務器運行的是這個應用的完整服務。而微服務是獨立自治的功能模塊,它是生態系統中的一部分,和其他微服務是共生關係。現在,業界對單體系統和微服務的普遍觀點是:單體系統非常容易開發、測試、部署,但是單體系統面對的問題也很多,例如開發效率變低、維護成本增加、部署影響變大、可擴展性較差、技術選型成本高,而引入了微服務可以實現每個微服務易於開發與維護,便於溝通與協作,很適合小團隊敏捷開發與持續交付;每個微服務職責單一,高內聚、低耦合。同時,每個微服務能夠獨立開發、獨立運行、獨立部署;每個微服務之間是獨立的,如果某個服務部署或者宕機,只會影響到當前服務,而不會對整個業務系統產生影響;每個微服務可以隨着系統規模的不斷擴大,面對海量用戶和高併發,獨立做水平擴展與垂直擴展;每個微服務可以使用不同的編程語言以及不同的存儲技術,使得我們更容易嘗試新的技術。此外,對單個服務進行業務重構,也不會面臨很大的業務負擔與技術債券。顛覆微服務認知:深入思考微服務的七個主流觀點

筆者對微服務系統的觀點是,我們從單體系統向微服務系統改造的過程中,需要認真思考什麼階段使用微服務。微服務不是銀彈,它對於設計和運維難度提出了更高的要求,同時也帶來了一些技術的複雜度。因此,我們需要思考與解決分佈式的複雜性、數據的一致性、服務的管理與運維、服務的自動化部署等解決方案。事實上,微服務通過拆分單體系統使其成爲多個體積更小的服務來降低單個服務的複雜性,但是,我們從整體來看,這種方式有造成了存在大量的服務,而服務之間的相互調用也會增多,從而導致整個系統架構變得更加複雜。

我們經常忽視業務價值和成本考量,而太過追求技術,那麼可能會導致我們精心設計的分佈式架構嚴重影響我們開發的速度和業務的快速迭代,並且隨着業務的不確定性往往導致我們的架構在半年到一年之內就已經不完全適用,推倒重來。此外,如果業務沒有發展起來也會導致前期大量的服務器資源盲目的浪費了,這對於初創業務得不償失。因此,我們在項目前期爲了保證快速增長業務,保證系統儘量減少依賴且獨立完整,減低引入微服務架構後的技術複雜度,例如它對於運維難度提出了更高的要求,因爲好的微服務架構需要穩定的基礎設施。隨着業務發展良好,系統規模會不斷擴大,它的擴展性、伸縮性、可用性和性能都限制了我們的業務發展,此時,我們懷着明確的業務思考和投入更多的資源再來考慮微服務改造。

微服務架構使用服務作爲模塊化的單元,那麼,我們可以在前期設計的時候通過 Maven 的 module 模塊化來初步隔離依賴,爲我們之後的改造預留空間。注意的是,微服務在生態系統中是共生關係。這裏,不僅僅侷限在它們可能存在鏈路依賴,同時它們的業務價值一定是共生的。因此,後期識別出單體系統的核心價值、關鍵功能,再把這些功能拆分成獨立且自完整的模塊。這裏,改造方案可以閱讀筆者的《高可用可伸縮微服務架構:基於Dubbo、Spring Cloud和Service Mesh》一書的第十二章 “遺留系統的微服務架構改造”。

總結一下,微服務通過拆分單體系統使其成爲多個體積更小的服務來降低單個服務的複雜性,但是,我們從整體來看,這種方式有造成了存在大量的服務,而服務之間的相互調用也會增多,從而導致整個系統架構變得更加複雜。因此,我們不單單隻關注技術,而需要考量投入產出比,保障當前階段的利益最大化。

二、擺脫單體系統就遠離大泥球?

單體系統讓很多人詬病的是其服務內聚混亂,看起來就像一個大泥球。那麼,服務化之後,就解決了這個問題了嗎?事實上,微服務通過拆分單體系統使其成爲多個體積更小的服務來降低單個服務的複雜性,讓單個系統看起來更加的職能清晰,但是,整個系統架構變得更加複雜。事實上,生產環境的多服務之間的調用可能如圖所示的場景。
顛覆微服務認知:深入思考微服務的七個主流觀點
通常情況下,生產環境的微服務生態比上面的案例複雜的多,可能存在幾十個到幾百個的服務。那麼,對於我們而言,如何系統地梳理服務之間的依賴關係和鏈路關係就顯得非常重要。尤其在大促的時候,需要對於核心鏈路進行強保障,這個工作就顯得更加重要了。對此,我推薦通過 APM 的流量採集實現自動化鏈路梳理。

此外,我們在設計架構,每當有服務粒度的劃分問題,例如新項目的創建,或者對於服務邊界模凌兩可的時候,我們需要對服務邊界討論清楚,儘可能讓我們的服務保持內聚性。

三、遷移微服務能提升系統健壯性嗎?

這裏,還有一個主流的觀點:一個單體系統是一個大而全的功能集合,如果某個服務出現故障,會對整個業務系統產生影響,然而使用微服務可以實現如果某個服務部署或者宕機,只會影響到當前服務,而不會影響到整個業務系統。

事實上,這個觀點看起來非常正確,但是在真實的業務場景下,並不是推動我們改造的關鍵原因。首先,一個單體爲了避免單點故障,肯定需要集羣和負載均衡,注意的是,集羣和負載均衡和微服務(服務垂直拆分)不是互斥關係,而是在高併發和分佈式中的共存關係。此外,爲了解決服務部署,我們可以考慮通過滾動發佈來實現服務的無中斷。所以,單體系統不一定就是不健壯的。同時,引入了微服務之後,從整體來看,這種方式有造成了存在大量的服務,而服務之間的相互調用也會增多,從而導致整個系統架構變得更加複雜,某個鏈路上的某個節點出現故障的機率就大大的增加了,更多的依賴也意味着發生更多問題的可能。此時,假設其中某條調用鏈路上某個微服務宕機而無法提供服務,那麼對其強依賴的上游服務,如何保障其自身可用性?(我在這裏特指強依賴調用,服務降級或者熔斷機制可能會對業務有損,並不是解決的有效方案。)因此,很多場景下,某個服務宕機也可能會影響到整個業務系統。

因此,如果我們沒有面向失敗設計,並且構建一套服務治理的體系,反而會導致整體服務的不健壯性。

四、遷移微服務能提升系統的性能嗎?

那麼,什麼纔是遷移微服務的主要動因了?我覺得最主要的利益動因是服務的可擴展性和提升性能方面的考量。一個服務因爲宿主機器的限制可能達到了瓶頸,爲了更加進一步的壓榨機器的資源,拆分服務是一個好主意。同時,我們還可以進一步實現自動縮擴容來調整機器的使用。但是,遷移微服務一定能提升系統的性能嗎?我的觀點是真不一定。服務化後,調用鏈路變長,原本的一次 RPC 通信可能變成幾次,性能損耗有所增加。例如,異地多活主要挑戰之一就是網絡時延,跨城市一定會有延時的問題,假設跨地域網絡延時可能在一百毫秒以內,一次 HTTP 請求涉及到一兩百次跨城市 RPC 調用,那麼整個響應時間會增加很多,所以延時帶來的挑戰非常大。那麼,阿里爲了解決數據延遲的問題,最早提出了單元化的解決思路,即將讓請求收斂到同一區域內完成,單元高內聚,不做跨區域訪問,即“單元封閉”。此外,還存在跨服務調用的網絡超時問題,通過重試也增加了同步阻塞的隱患性。

因此,服務化是犧牲了服務之間鏈路上的調用性能,來整體提高整個業務系統對於機器資源壓榨來提升系統的性能。

五、微服務的可用性 = 服務提供的每次調用都是可靠且可用的?

對於微服務的可用性,很多人的理解是,服務提供的每次調用都是可靠且可用的。這個觀點不太對。事實上,微服務保證其服務的整體可用性。通常情況下,如果服務 A 調用服務 B,如果調用了 10 秒,那麼後面的情況可能就會阻塞,間接地,導致了線程池撐爆,導致服務不可用。因此,我們就會採取超時機制來保障極短的時間內完成結果響應,儘可能不出現同步阻塞問題。
顛覆微服務認知:深入思考微服務的七個主流觀點
此外,如果服務 B 出現故障,所有調用依賴的服務都將出現阻塞,如果有大量的請求會導致線程資源會被消耗完畢,導致服務癱瘓。事實上,服務與服務之間的依賴性會導致級聯傳播,從而間接導致服務故障的“雪崩”效應,造成整個微服務系統不可用。爲了解決這個問題,熔斷機制就有了用武之地。

六、微服務中數據庫是相互獨立且透明?

微服務的一個主流觀點是,在每個服務都有自己的緩存和數據庫,並且緩存和數據庫是相互獨立且透明的。因此,共享緩存與共享數據庫是不對的。那如果服務 A 需要獲取服務 B 的數據怎麼辦?一般的做法是,服務 B 提供一個獲取該數據的 API 接口,而服務 A 通過調用該接口進行業務組裝。因此,微服務化之後,服務之間的數據交換都是通過接口來開展的,如果服務 A 越過服務 B 的業務邏輯之間訪問服務 B 的數據,其會破壞了微服務之間的數據獨立性。
顛覆微服務認知:深入思考微服務的七個主流觀點
此時,筆者需要潑一下冷水。凡事無絕對,有幾種特殊的場景可能需要共享數據。其一,那就是舊的服務過渡到新的服務的場景,新的服務複用舊的服務的數據庫從而到達功能與數據過渡的需求。其二,多個服務之間可能依賴於同一個數據源,例如報表的數據聚合。這種情況下,如果我們單純的依賴於 RPC 的接口調用很可能會導致偶發性的調用超時,從而導致故障發生的機率更大。那麼,解決這個問題的常用套路就是共享數據,要麼通過數據冗餘的方式進行數據同步,然後基於本地的服務進行邊界內的數據聚合;要麼通過抽離數倉方案進行數據的集中化 ETL,然後再對外通過加工好的數據。其三,更加現實的成本問題。事實上,更多的數據庫會帶來更多的經費成本。很多時候,我們也會從經費成本來考慮問題。我們選擇複用原來的數據庫表,等待業務價值明確之後,再考慮單獨獨立數據庫。

同時,共享數據技術方案可避免數據之間的上下文不明確的情況下代價高昂且重複的數據遷移,並可在需要時更輕鬆地調整服務粒度,然後在服務粒度穩定之後再進行數據遷移。因此,我們要在兩者之間尋求適當的平衡,儘可能遵守微服務的主流觀點,充分利用微服務帶來的好處。

七、組織保障微服務的實施?

微服務對與組織結構提出了新的要求,它建議將大團隊拆分成爲多個小團隊,而每個團隊各自運維開發和運維一個或多個服務,並且需要流程上持續交付、持續部署、DevOps。
顛覆微服務認知:深入思考微服務的七個主流觀點
不同的服務可能由不同的團隊開發與維護,實際場景下,微服務的便利性更多的在於團隊內部能夠產生閉環,換句話說,團隊內部可以易於開發與維護,便於溝通與協作,但是對於外部團隊就存在很大的溝通成本與協作成本。如圖所示,團隊 A 對於服務 C 的瞭解是一個黑盒,我們不知道它是單體服務還是微服務,它部署了幾臺服務器,需要依賴哪些下游服務,是否存在限流、熔斷和降級策略,以及如何接入。如果我們需要確認這些問題,通常情況下,都需要人工協作和確認。
顛覆微服務認知:深入思考微服務的七個主流觀點
當然,這個是組織分工帶來的不可避免的問題,那麼我們儘可能保證我們自己團隊內部的服務的內聚性,圍繞業務模塊進行劃分,保證微服務具有業務的獨立性與完整性,儘可能少的存在服務依賴,鏈式調用。這裏,又拋出了一個新的問題。微服務有多“微”?事實上,對於服務的拆分並非越小越好,甚至極端的案例是把一塊功能拆分成一個服務,這種做法是不對的。因此,拆分粒度應該保證微服務具有業務的獨立性與完整性,服務的拆分圍繞業務模塊進行拆分。如果單獨拆分成服務的業務價值/技術價值不明確,那麼就讓它耦合在這個單體系統中,在整個項目的生命週期裏已經足夠了。如果隨着業務的發展與需求,我們可以隨着調整系統源碼層次上模塊結構,並將其拆分成獨立的微服務。

有的時候,團隊對項目具有絕對的所有權,從而因爲團隊利益上的考慮而出現生產上的微服務是一個“半成品”。筆者相信這種情況並非個例,而是絕大多數的常態。現在,我們來看一個案例。團隊 A 考慮到功能的複用性而開發了一個“互動組件”,其中包括 “評論模塊”功能。此時,團隊 B 並不知情也開發了一個類似的“互動組件”。而團隊 C 也有這個需求,它知道團隊 A 有這個“互動組件”,希望可以複用,但是由於這個“互動組件”在設計的時候更多地考慮了團隊 A 的當前業務,沒有很好的複用性,例如不支持“評論蓋樓”功能,而由於團隊 A 出於當前其他項目的進度原因無法馬上提供支持,團隊 C 評估後決定花一週時間自己開發一個符合自己業務需求的“互動組件”。此時,各個項目團隊各自維護了一個“互動組件”。這個案例中,由於團隊之間的職責與邊界導致了服務的複用存在侷限性,甚至造成各自爲戰的局面,這種情況一般需要公司層面進行規劃和統籌。無獨有偶,團隊 A 和團隊 B 都在做工單系統,但是兩者需要融合,爲了保證兩個團隊的既有利益,他們並不是將原來的架構打破進行融合,而是在原有的基礎上確定領域邊界。

此外,假設外部一個 RPC 接口不太穩定,一般的做法就是去分析不穩定的原因,但是跨團隊合作的情況下,外部服務可能就是一個黑盒,並且團隊之間可能就是一堵隱形的牆,那麼溝通協作成本是非常大的,此時需要有人來全鏈路牽頭讓大家的利益達成一致。那麼,當下最高效的方式可能就是團隊內部通過其他手段來規避這個問題,例如冗餘緩存或者接口適配。因此,它也許就是當前組織結構和環境下業務價值的最大化的較優方案,我們需要適應當下,展望未來。說到接口適配,其實還有一個非常常見的微服務架構設計:適配器服務模式。通常情況下,外部服務給我們的消息體格式和我們所需要的不一致,此時,我們去推動他們改造顯得不太現實,那麼,爲了保障我們的業務邏輯不引入大量的業務適配邏輯,我們就會引入適配層 (適配器服務),它將外部服務的消息體適配成統一的標準格式,然後再向上暴露服務,例如退款適配、物流適配等。

因此,公司組織在很大程度上影響了服務邊界的確定。通常情況下,我們在自身團隊的邊界內做領域劃分,儘可能的滿足業務需求,雖然從技術層面來看這個是一個“半成品”,但是它也許就是當前環境下業務價值的最大化的較優方案。所謂分久必合,當大家看到它有足夠大的價值後,在考慮進一步融合也是一個不錯的主意。

寫在最後的心聲

最後,我們再來談談引入新的技術,給項目帶來技術紅利。一個新的技術需要考慮學習成本和維護成本,以及可用性保障和可運維性。例如,我在公司在運維的護航下,我可以輕鬆自如的使用各種技術等,但是,我不一定敢在另外一個公司使用 MongoDB,因爲我知道我並不是這方面的運維專家,如果出現問題,我可能沒辦法解決。那麼,引入一個新技術可能存在的技術風險。很多時候,我們要基於失敗設計,這恰恰是初級工程師和資深工程師之間的差距。例如,Redis 實現分佈式鎖,很多人都只想到來如何通過代碼實現這套邏輯,但是,如果 Redis 集羣中主服務掛了,直接切換到從服務,因爲是主從異步同步,而分佈式鎖講究的是一定是最新的鎖數據才管用,就是在一瞬間才起作用,這時候丟了分佈式鎖數據,你的業務就會造成重複請求,而分佈式鎖如果應用在了業務中,必須是非常重要的場景,尤其是金融和支付,所以單點版 Redis 分佈式鎖不是好方法,不能使用,如果要用,就得解決穩定性問題。(引用《高可用可伸縮微服務架構:基於Dubbo、Spring Cloud和Service Mesh》作者「程超」在羣裏分享的案例,特別精彩。)這裏,小小的偏題了下,回到正題,我們會經常發現新項目嘗試使用新技術,而老項目更加保守,因爲前者試錯成本更低。有趣的是,新公司對技術的發展更加敏銳,例如很多小公司在雲原生方面有諸多實踐與落地。此時,你可能大概明白了我表述的觀點:通常情況下,技術棧的使用背後是公司的運維保障,以及對技術深度的把控力。所以,我們需要對新技術有提前的儲備,以備隨時上戰場,但是絕大多數情況下,我們要保證利用現有的技術(工具)實現業務價值的最大化。

總結一下,本篇文章沉澱了很多我在工作以來的所見所聞和實戰思考,核心觀點並不是唱衰微服務,而是讓大家保持獨立思考,跳出純技術的視角去思考架構,去看待微服務,要保證利用現有的技術(工具)實現業務價值的最大化。

寫在末尾

【服務端思維】:我們一起聊聊服務端核心技術,探討一線互聯網的項目架構與實戰經驗。讓所有孤軍奮戰的研發人員都找到屬於自己的圈子,一起交流、探討。在這裏,我們可以認知升級,連接頂級的技術大牛,連接優秀的思維方式,連接解決問題的最短路徑,連接一切優秀的方法,打破認知的侷限。
更多精彩文章,盡在「服務端思維」!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章