原文地址:梁桂釗的博客數據庫
博客地址:blog.720ui.com編程
歡迎關注公衆號:「服務端思惟」。一羣同頻者,一塊兒成長,一塊兒精進,打破認知的侷限性。緩存
單體系統和微服務的區別在於,一個單體系統是一個大而全的功能集合,每一個服務器運行的是這個應用的完整服務。而微服務是獨立自治的功能模塊,它是生態系統中的一部分,和其餘微服務是共生關係。如今,業界對單體系統和微服務的廣泛觀點是:單體系統很是容易開發、測試、部署,可是單體系統面對的問題也不少,例如開發效率變低、維護成本增長、部署影響變大、可擴展性較差、技術選型成本高,而引入了微服務能夠實現每一個微服務易於開發與維護,便於溝通與協做,很適合小團隊敏捷開發與持續交付;每一個微服務職責單一,高內聚、低耦合。同時,每一個微服務可以獨立開發、獨立運行、獨立部署;每一個微服務之間是獨立的,若是某個服務部署或者宕機,只會影響到當前服務,而不會對整個業務系統產生影響;每一個微服務能夠隨着系統規模的不斷擴大,面對海量用戶和高併發,獨立作水平擴展與垂直擴展;每一個微服務可使用不一樣的編程語言以及不一樣的存儲技術,使得咱們更容易嘗試新的技術。此外,對單個服務進行業務重構,也不會面臨很大的業務負擔與技術債券。服務器
筆者對微服務系統的觀點是,咱們從單體系統向微服務系統改造的過程當中,須要認真思考什麼階段使用微服務。微服務不是銀彈,它對於設計和運維難度提出了更高的要求,同時也帶來了一些技術的複雜度。所以,咱們須要思考與解決分佈式的複雜性、數據的一致性、服務的管理與運維、服務的自動化部署等解決方案。事實上,微服務經過拆分單體系統使其成爲多個體積更小的服務來下降單個服務的複雜性,可是,咱們從總體來看,這種方式有形成了存在大量的服務,而服務之間的相互調用也會增多,從而致使整個系統架構變得更加複雜。網絡
咱們常常忽視業務價值和成本考量,而太過追求技術,那麼可能會致使咱們精心設計的分佈式架構嚴重影響咱們開發的速度和業務的快速迭代,而且隨着業務的不肯定性每每致使咱們的架構在半年到一年以內就已經不徹底適用,推倒重來。此外,若是業務沒有發展起來也會致使前期大量的服務器資源盲目的浪費了,這對於初創業務得不償失。所以,咱們在項目前期爲了保證快速增加業務,保證系統儘可能減小依賴且獨立完整,減低引入微服務架構後的技術複雜度,例如它對於運維難度提出了更高的要求,由於好的微服務架構須要穩定的基礎設施。隨着業務發展良好,系統規模會不斷擴大,它的擴展性、伸縮性、可用性和性能都限制了咱們的業務發展,此時,咱們懷着明確的業務思考和投入更多的資源再來考慮微服務改造。架構
微服務架構使用服務做爲模塊化的單元,那麼,咱們能夠在前期設計的時候經過 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》做者「程超」在羣裏分享的案例,特別精彩。)這裏,小小的偏題了下,回到正題,咱們會常常發現新項目嘗試使用新技術,而老項目更加保守,由於前者試錯成本更低。有趣的是,新公司對技術的發展更加敏銳,例如不少小公司在雲原生方面有諸多實踐與落地。此時,你可能大概明白了我表述的觀點:一般狀況下,技術棧的使用背後是公司的運維保障,以及對技術深度的把控力。因此,咱們須要對新技術有提早的儲備,以備隨時上戰場,可是絕大多數狀況下,咱們要保證利用現有的技術(工具)實現業務價值的最大化。
總結一下,本篇文章沉澱了不少我在工做以來的所見所聞和實戰思考,核心觀點並非唱衰微服務,而是讓你們保持獨立思考,跳出純技術的視角去思考架構,去看待微服務,要保證利用現有的技術(工具)實現業務價值的最大化。
【服務端思惟】:咱們一塊兒聊聊服務端核心技術,探討一線互聯網的項目架構與實戰經驗。讓全部孤軍奮戰的研發人員都找到屬於本身的圈子,一塊兒交流、探討。在這裏,咱們能夠認知升級,鏈接頂級的技術大牛,鏈接優秀的思惟方式,鏈接解決問題的最短路徑,鏈接一切優秀的方法,打破認知的侷限。
更多精彩文章,盡在「服務端思惟」!