微服務(wù)架構(gòu)深度解析與最佳實踐
時間:2022-08-11 14:42:01 | 來源:網(wǎng)站運營
時間:2022-08-11 14:42:01 來源:網(wǎng)站運營
微服務(wù)架構(gòu)的概念,現(xiàn)在對于大家應(yīng)該都不陌生,無論使用 Apache Dubbo、還是 Spring Cloud,都可以去嘗試微服務(wù),把復(fù)雜而龐大的業(yè)務(wù)系統(tǒng)拆分成一些更小粒度且獨立部署的 Rest 服務(wù)。但是這個過程,具體應(yīng)該怎么做?現(xiàn)有的條件下到底要不要做微服務(wù)?服務(wù)拆分成什么粒度才是合適的?遺留的老系統(tǒng)需要如何考慮重構(gòu)改造?有哪些坑需要我們注意?系統(tǒng)怎么在分布式服務(wù)下實現(xiàn)數(shù)據(jù)的一致性和服務(wù)的高可用可伸縮?拆分的過程中系統(tǒng)數(shù)量增多,測試、部署、運維、監(jiān)控,又應(yīng)該如何處理?
(個人微信號:Robynn-D , 歡迎交流)
本文將從這些問題的深度分析出發(fā),闡述微服務(wù)架構(gòu)落地的一些設(shè)計原則和利弊取舍,結(jié)合微服務(wù)架構(gòu)過程的很多最佳實踐經(jīng)驗,希望給讀者帶來一定的啟發(fā)和思考,避免在實際應(yīng)用過程中走彎路,能夠多快好省的落地實現(xiàn)微服務(wù)架構(gòu)。內(nèi)容涉及:
- 微服務(wù)架構(gòu)的發(fā)展過程簡介
- 微服務(wù)架構(gòu)的特點與常見特性
- 微服務(wù)架構(gòu)的常見技術(shù)與簡單示例
- 微服務(wù)架構(gòu)存在的一些問題
- 如何合理拆分微服務(wù)
- 遺留系統(tǒng)應(yīng)該如何改造
- 怎么考慮拆分后的數(shù)據(jù)一致性
- 系統(tǒng)和服務(wù)的高可用可伸縮如何實現(xiàn)
- 拆分過程的測試和部署如何處理
- 拆分后的運維和監(jiān)控如何處理
第一部分:微服務(wù)深度解析
微服務(wù)架構(gòu)的發(fā)展過程簡介
微服務(wù)架構(gòu)發(fā)展的五個關(guān)鍵時間節(jié)點
- “微服務(wù)”這個概念最早是在 2011 年 5 月威尼斯的一個軟件架構(gòu)會議上討論并提出的,用于描述一些作為通用架構(gòu)風(fēng)格的設(shè)計原則。
- 2012 年 3 月在波蘭克拉科夫舉行的 33rd Degree Conference 大會上,Thoughtworks 首席咨詢師 James Lewis 做了題為《Microservices - Java, the Unix Way》的演講(http://2012.33degree.org/talk/show/67),這次演講里 James 討論了微服務(wù)的一些原則和特征,例如單一服務(wù)職責(zé)、康威定律、自動擴展、DDD 等等。
- “微服務(wù)架構(gòu)”則是由 Fred George 在 2012 年的一次技術(shù)大會上所提出(http://oredev.org/oredev2012/2012/sessions/micro-service-architecture.html),在大會的演講中他講解了如何分拆服務(wù)以及如何利用 MQ 來進行服務(wù)間的解耦,這就是最早的微服務(wù)架構(gòu)雛形。
- 而后由 Martin Fowler 發(fā)揚光大并且在 2014 年發(fā)表了一篇著名的微服務(wù)文章(https://martinfowler.com/articles/microservices.html),這篇文章深入全面的講解了什么是微服務(wù)架構(gòu)。隨后,微服務(wù)架構(gòu)逐漸成為一種非常流行的架構(gòu)模式,一大批的技術(shù)框架和文章涌現(xiàn)出來,越來越多的公司借鑒和使用微服務(wù)架構(gòu)相關(guān)的技術(shù)。
- 2016 年 4 月,Lightbend 公司的創(chuàng)始人兼 CTO、Akka 的作者 Jonas Bonér,發(fā)布了一本小冊子《響應(yīng)式微服務(wù)架構(gòu)》,討論了基于響應(yīng)式原理的微服務(wù)架構(gòu),以及如何將其用于構(gòu)建可擴展,可應(yīng)對故障的隔離服務(wù),并與其他服務(wù)結(jié)合以形成一個緊密的整體。
特別值得一提的是,在微服務(wù)架構(gòu)發(fā)展的過程中,還有兩位技術(shù)大牛的影響不可忽視:
- Chris Richardson:《POJOS IN ACTION》與《微服務(wù)架構(gòu)的設(shè)計模式》的作者,也是著名開源項目 cloudfoundry 和 eventuate 的創(chuàng)始人,他做了大量的微服務(wù)架構(gòu)相關(guān)的方法和實踐的探索,這里有其大量的演講材料和視頻:https://chrisrichardson.cn/resource/。
- Sam Newman,Martin Fowler 和 James Lewis 的 Thoughtworks 前同事,《Building Microservices》和《Monolith To Microservices》這兩本的作者,前一本中文版叫《微服務(wù)設(shè)計》,同時也是 2014 年著名的推特論戰(zhàn)《單體應(yīng)用 vs 微服務(wù)應(yīng)用》的主角之一(另外兩人分別是 Netflix 的 Adrian Cockcroft 和 Etsy 的 John Allspaw ,具體參見 https://www.csdn.net/article/2014-08-06/2821078)。
微服務(wù)架構(gòu)的發(fā)展趨勢
軟件架構(gòu)的發(fā)展趨勢,簡單的說可以分為這幾個階段(詳細(xì)介紹可以參見我的上一個文章《軟件架構(gòu)發(fā)展歷程分享》或者《高可用可伸縮微服務(wù)架構(gòu):基于 Dubbo、Spring Cloud 和 ServiceMesh》一書):
- 單體架構(gòu):最簡單的架構(gòu)風(fēng)格,所有的代碼在一起,部署到單個進程,例如打包成一個 war 或者 jar,就是通常說的“大泥球”。
- 垂直架構(gòu):隨著業(yè)務(wù)的發(fā)展,系統(tǒng)變得復(fù)雜,通過結(jié)構(gòu)化思考,大家發(fā)現(xiàn)對于大規(guī)模協(xié)同開發(fā),有效的控制手段就是對系統(tǒng)進行抽象和分層,例如場景的 MVC 方式。
- 面向服務(wù)架構(gòu)(SOA):面向服務(wù)架構(gòu)從建設(shè)企業(yè) IT 生態(tài)系統(tǒng)的角度思考架構(gòu)問題,關(guān)注點是各個系統(tǒng)提供可以集成的業(yè)務(wù)服務(wù)能力,而且通常由 ESB 之類的集中化技術(shù)實現(xiàn)。
- 微服務(wù)架構(gòu)(MSA):微服務(wù)架構(gòu)風(fēng)格,將系統(tǒng)設(shè)計為一組低耦合的微服務(wù),每個服務(wù)獨立的部署運行,服務(wù)間一般采用 REST 等方式通信,同時采用自動化的測試運維等技術(shù)降低服務(wù)拆分后的復(fù)雜度。
我們可以從 Google 的趨勢圖看到,從 2014 年起,微服務(wù)架構(gòu)架構(gòu)的熱度就直線上升,成為最熱門的技術(shù)之一。從國內(nèi)的情況來看,一方面;另一方面,一直到現(xiàn)在,每年的 QCon 大會都會有微服務(wù)架構(gòu)版本,跟大家分享最新的微服務(wù)架構(gòu)知識和實踐經(jīng)驗。
什么是微服務(wù)架構(gòu)
說了這么多,那么到底什么是微服務(wù)和微服務(wù)架構(gòu)呢?
按 Martin Fowler 和 James Lewis 的定義,微服務(wù)架構(gòu)風(fēng)格是通過實現(xiàn)一系列微小的服務(wù)的方式,開發(fā)一個獨立應(yīng)用系統(tǒng)的方法,每個服務(wù)運行在自己的進程內(nèi),通過輕量級的機制與其他服務(wù)通信,通常是 HTTP 資源 API 的方式。這些服務(wù)都是圍繞業(yè)務(wù)能力來構(gòu)建,通過全自動部署工具來實現(xiàn)獨立部署。這些服務(wù),其可以使用不同的編程語言和不同的數(shù)據(jù)存儲技術(shù),并保持最小化集中管理。
具體包含如下特征:
- 組件以服務(wù)形式來提供:微服務(wù)也是面向服務(wù)的,提倡用可以獨立部署的服務(wù)來作為組裝業(yè)務(wù)能力的組件,而不是類庫的形式。這樣需要明確定義組件間的接口和通信協(xié)議。微服務(wù)架構(gòu)的一個目的是通過合理的邊界劃分和演化機制來減少變更帶來的影響。
- 圍繞業(yè)務(wù)功能進行組織:根據(jù)康威定律,“設(shè)計系統(tǒng)的組織,其產(chǎn)生的設(shè)計等同于組織之內(nèi)、組織之間的溝通結(jié)構(gòu)”。微服務(wù)更傾向于圍繞業(yè)務(wù)功能對服務(wù)結(jié)構(gòu)進行劃分、拆解。這樣的服務(wù),是針對業(yè)務(wù)領(lǐng)域有著關(guān)完整實現(xiàn)的軟件,它包含使用接口、持久存儲、以及對象的交互。因此團隊?wèi)?yīng)該是跨職能的,包含完整的開發(fā)技術(shù):用戶體驗、數(shù)據(jù)庫、以及項目管理。
- 產(chǎn)品不是項目:傳統(tǒng)的開發(fā)模式,我們一般稱為項目心態(tài),就是以乙方心態(tài)給甲方做項目。一旦項目開發(fā)完成,軟件將移交給維護/實施部門,或者是乙方交給甲方,然后開發(fā)團隊就可以解散掉了。而微服務(wù)要求開發(fā)團隊對軟件產(chǎn)品的整個生命周期負(fù)責(zé)。這要求開發(fā)者每天都關(guān)注軟件產(chǎn)品的運行情況,并與用戶聯(lián)系的更緊密,這也就是我們說的產(chǎn)品心態(tài)。產(chǎn)品心態(tài)下的軟件質(zhì)量要明顯好于項目心態(tài)。Amazon 的理念就是“You build, you run it”,這也正是 DevOps 的文化理念。
- 強化終端及弱化通道:微服務(wù)的應(yīng)用致力松耦合和高內(nèi)聚,所以更傾向于 REST,而不是復(fù)雜的協(xié)議(如 WS 或者 BPEL 或者集中式框架),或者采用輕量級消息總線(如 RabbitMQ 或 ZeroMQ 等)來發(fā)布消息。
- 分散治理:這是跟傳統(tǒng)的集中式管理很大區(qū)別的地方。微服務(wù)把單體式框架中的組件,拆分成不同的服務(wù),在構(gòu)建它們時將會有更多的選擇。分散治理也意味著責(zé)任的下放,每個團隊對自己的業(yè)務(wù)服務(wù)負(fù)責(zé),如果你維護一個 7x24 小時的不間斷服務(wù),那么你就必須 24 小時隨時 OnCall,哪怕晚上 3 點起來處理問題,就是 AWS 的模式。
- 分散數(shù)據(jù)管理:當(dāng)單體式的應(yīng)用使用單一邏輯數(shù)據(jù)庫對數(shù)據(jù)持久化時,企業(yè)通常選擇在應(yīng)用的范圍內(nèi)使用一個數(shù)據(jù)庫。微服務(wù)讓每個服務(wù)管理自己的數(shù)據(jù)庫:無論是相同數(shù)據(jù)庫的不同實例,或者是不同的數(shù)據(jù)庫系統(tǒng)。這一點,我們后面會詳細(xì)說明。
- 基礎(chǔ)設(shè)施自動化:云計算,特別是 AWS、Azure、Aliyun 等的發(fā)展,減少了構(gòu)建、發(fā)布、運維微服務(wù)的復(fù)雜性。微服務(wù)的團隊更加依賴于基礎(chǔ)設(shè)施的自動化。其實不管是運維,測試也是一樣。
- 容錯性設(shè)計:任務(wù)服務(wù)都可能因為供應(yīng)商或者底層硬件或網(wǎng)絡(luò)的不可靠而故障。微服務(wù)應(yīng)為每個應(yīng)用的服務(wù)及數(shù)據(jù)中心提供日常故障檢測和恢復(fù),收集各項系統(tǒng)狀態(tài)指標(biāo)和業(yè)務(wù)指標(biāo)、日志信息進行監(jiān)控,并提供預(yù)警報警能力。面向失敗的設(shè)計,后面也會再討論。
- 改進設(shè)計:組件的關(guān)鍵特性是可替代性和可升級性,由于設(shè)計會不斷更改,微服務(wù)所提供的服務(wù)應(yīng)該能夠替換或者報廢,而不是要長久的發(fā)展的,每種設(shè)計也一樣都有自己的階段使命和生命周期。這樣如果客戶需要兼容性,版本控制是一種好的手段。
Chris Richardson 則簡化了這種說法,認(rèn)為微服務(wù)是一種架構(gòu)風(fēng)格,通過一組服務(wù)的方式構(gòu)造系統(tǒng),同時需要滿足如下條件:
- 高可維護性和測試性
- 松耦合性
- 可獨立部署
- 圍繞業(yè)務(wù)能力進行組織
- 一個小團隊對其完全負(fù)責(zé) 微服務(wù)架構(gòu)能夠快速、頻繁和可靠地發(fā)布大型的復(fù)雜應(yīng)用系統(tǒng),也能夠使得組織可以進化出自己的技術(shù)棧。
Peter Lawyer 說,微服務(wù)很多地方特別像是 Mike Gancarz 總結(jié)的 Unix 哲學(xué):
- 小即是美(Small is beautiful)
- 讓程序只做好一件事(Make each program do one thing well)
- 盡可能早地建立原型(Build a prototype as soon as possible)
- 可移植性比效率更重要(Choose portability over efficiency)
- 盡可能地榨取軟件的全部價值 Use software leverage to your advantage.
微服務(wù)架構(gòu)就是 Unix 哲學(xué)在分布式系統(tǒng)里的應(yīng)用。微服務(wù)架構(gòu)的哲學(xué)本質(zhì)上等于 Unix 哲學(xué)里的“讓程序只做好一件事”:
- 服務(wù)都很小,細(xì)粒度地執(zhí)行單個功能。
- 組織文化應(yīng)擁抱自動化部署和測試。這減輕了管理和運維的負(fù)擔(dān)。
- 文化和設(shè)計原則應(yīng)擁抱失敗和錯誤,類似于抗脆弱系統(tǒng)。
- 每個服務(wù)都是彈性的,回彈性的,可組合的,最小化的和完整的(彈性和回彈性參見響應(yīng)式微服務(wù))。
從這里我們可以得出一個結(jié)論,一個微服務(wù)架構(gòu)的系統(tǒng)需要滿足一系列的條件和原則,而不僅僅是說使用了某個微服務(wù)的技術(shù)框架就是微服務(wù)架構(gòu)。微服務(wù)這個詞目前也過于流行以致于有些泛濫了。很多技術(shù)組件或者框架,例如可以用來暴露服務(wù),可以用來自動化部署,可以用來管理配置等等,它們都可以用來作為微服務(wù)架構(gòu)設(shè)計時的某個組成部分。但是單獨用一個,并不代表我們實現(xiàn)了微服務(wù)設(shè)計,而只能說明我們借鑒了一些微服務(wù)的思想。另一方面,我們在做微服務(wù)的時候,也不用把市面上所有的微服務(wù)組件都拿來用。就像是我們寫個業(yè)務(wù)系統(tǒng)不會用到 JDK 的所有 API,畫一幅畫之前我們買了一盒 24 支的水彩筆,實際上我們可能做一幅作品最終只用到了 5-6 個顏色的水彩筆。
微服務(wù)架構(gòu)的特點、優(yōu)勢和常見技術(shù)
微服務(wù)的四個特點和六個能力
現(xiàn)在讓我們分析一下上一節(jié)里的各個技術(shù)大牛們闡述的技術(shù)觀點,從設(shè)計開發(fā)、系統(tǒng)部署、測試運維和服務(wù)治理四個主要方面來考慮微服務(wù)架構(gòu)的特點,那么這四個方面就可以總結(jié)為下圖:
微服務(wù)架構(gòu)首先是一個分布式的架構(gòu),其次我們要暴露和提供業(yè)務(wù)服務(wù)能力,然后我們需要考慮圍繞這些業(yè)務(wù)能力的各種非功能性的能力。這些分散在各處的服務(wù)本身需要被管理起來,并且對服務(wù)的調(diào)用方透明,這樣就有了服務(wù)的注冊發(fā)現(xiàn)的功能需求。
同樣地,每個服務(wù)可能部署了多臺機器多個實例,所以,我們需要有路由和尋址的能力,做負(fù)載均衡,提升系統(tǒng)的擴展能力。有了這么多對外提供的不同服務(wù)接口,我們一樣需要有一種機制對他們進行統(tǒng)一的接入控制,并把一些非業(yè)務(wù)的策略做到這個接入層,比如權(quán)限相關(guān)的,這就是服務(wù)網(wǎng)關(guān)。同時我們發(fā)現(xiàn)隨著業(yè)務(wù)的發(fā)展和一些特定的運營活動,比如秒殺大促,流量會出現(xiàn)十倍以上的激增,這時候我們就需要考慮系統(tǒng)容量,服務(wù)間的強弱依賴關(guān)系,做服務(wù)降級、熔斷,系統(tǒng)過載保護等措施。
以上這些由于微服務(wù)帶來的復(fù)雜性,導(dǎo)致了應(yīng)用配置、業(yè)務(wù)配置,都被散落到各處,所以分布式配置中心的需求也出現(xiàn)了。最后,系統(tǒng)分散部署以后,所有的調(diào)用都跨了進程,我們還需要有能在線上做鏈路跟蹤,性能監(jiān)控的一套技術(shù),來協(xié)助我們時刻了解系統(tǒng)內(nèi)部的狀態(tài)和指標(biāo),讓我們能夠隨時對系統(tǒng)進行分析和干預(yù)。這六種能力總結(jié)如下圖:
微服務(wù)的優(yōu)勢
為什么我們要從單體系統(tǒng)走向微服務(wù)架構(gòu)呢?我們先來看一個圖。
這個圖 x 軸是系統(tǒng)復(fù)雜度,y 軸是開發(fā)的生產(chǎn)力,我們可以看到:
- 在系統(tǒng)復(fù)雜度很低的時候,單體的生產(chǎn)力要高一點,這是因為拆分微服務(wù),管理這些服務(wù)的成本增加了
- 當(dāng)復(fù)雜度開始增加,單體的生產(chǎn)力快速的降低,而微服務(wù)則不太受影響,這是因為復(fù)雜度大了,單體牽一發(fā)而動全身,各種耦合和相互影響太多
- 隨著復(fù)雜度越來越高,微服務(wù)的低耦合開始減低了生產(chǎn)力的衰變,而單體架構(gòu)的生產(chǎn)力則會降到一個非常低的水平
也就是說微服務(wù)應(yīng)用在復(fù)雜度低的情況下,生產(chǎn)力反而比單體系統(tǒng)低。在復(fù)雜度高的地方,情況恰恰相反。
隨著復(fù)雜度升高,單體架構(gòu)的生產(chǎn)力快速下降,而微服務(wù)相對平穩(wěn),所以,復(fù)雜度越高的業(yè)務(wù)系統(tǒng),越適合使用微服務(wù)架構(gòu)。
搞清楚了微服務(wù)架構(gòu)與單體架構(gòu)的生產(chǎn)力的區(qū)別以后,我們再來看看微服務(wù)有哪些優(yōu)勢,我總結(jié)了一下有以下幾點:
- 服務(wù)簡單:因為微小,所以簡單,從一個大泥球,變成了很多個小而美的顆粒,每個小顆粒職責(zé)單一,邊界明確,可以通過簡單組裝完成大的功能,自然就比之前的大泥球好處理得多。
- 靈活擴展:單體的情況下,只能通過增加機器,再部署多套單體系統(tǒng)做成集群,前面加負(fù)載均衡來擴展;微服務(wù)以后,我們會發(fā)現(xiàn)用戶服務(wù)壓力不大不用擴展,訂單服務(wù)壓力大的時候多部署兩臺就可以了,這樣我們就把擴展的方式從全部優(yōu)化到局部。
- 便于維護:如果一個系統(tǒng)有 100 個業(yè)務(wù)功能,依賴關(guān)系相互耦合到一起,那么這就是維護的惡夢,這樣的系統(tǒng)沒有任何免疫力,修改任何一個功能,都可能會導(dǎo)致整個系統(tǒng)崩潰。微服務(wù)就簡單得多了,每個服務(wù)自己出現(xiàn)問題,其他服務(wù)不會直接受到影響。同時維護具體某個服務(wù)的人員,也不需要一上來就學(xué)習(xí)全部的業(yè)務(wù)知識,比如用戶服務(wù)模塊的維護人員,只需要先學(xué)習(xí)用戶服務(wù)的業(yè)務(wù)就可以上手工作了。
- 獨立演進:變成微服務(wù)以后,各個獨立系統(tǒng)的內(nèi)部設(shè)計和實現(xiàn)細(xì)節(jié)都被隔離開,相互之間不受影響,只要服務(wù)間的接口不變,大家就可以各自獨立的發(fā)展自己的系統(tǒng),完成升級、重構(gòu)、功能增強等等。
- 混合開發(fā):各服務(wù)獨立開發(fā)的另外一個好處就是,各個獨立系統(tǒng)可以使用自己的技術(shù)棧和研發(fā)模式,包括開發(fā)語言和工具、數(shù)據(jù)庫存儲和中間件等技術(shù),這樣有助于試驗和引入更先進和創(chuàng)新的技術(shù),從一些個邊緣服務(wù)開始嘗試,技術(shù)、工具、中間件、研發(fā)模式,孵化成熟以后,可以大范圍推廣,實現(xiàn)技術(shù)和研發(fā)能力的持續(xù)更新?lián)Q代,讓研發(fā)組織保持長期的優(yōu)勢和活力,充分獲得技術(shù)發(fā)展的紅利。
- 持續(xù)交付:微服務(wù)比單體系統(tǒng)簡單明確,這樣就便于我們利用自動化測試和自動化部署來加速功能的迭代,配合 CI/CD 等基礎(chǔ)設(shè)施,實現(xiàn)業(yè)務(wù)功能的持續(xù)交付,保障研發(fā)能夠緊跟業(yè)務(wù)發(fā)展變化的節(jié)奏。
常見的微服務(wù)技術(shù)框架
具體怎么做微服務(wù)呢?我們先看看大家最常簡單見到的微服務(wù)的圖:
(個人微信號:Robynn-D , 歡迎交流)
在互聯(lián)網(wǎng)出行業(yè)務(wù)中,用戶通過 API 網(wǎng)關(guān)訪問系統(tǒng)的乘客、司機、出行三個 REST 服務(wù),這三個服務(wù)再通過 REST 訪問計費、支付和通知三個服務(wù)??雌饋砗芎唵?,也好理解,但是實際的業(yè)務(wù)系統(tǒng)里,設(shè)計和實現(xiàn)一般會是這樣:
- 服務(wù)框架:我們可以選擇用 Spring Cloud 或者 Apache Dubbo,包括新興的 Spring Cloud Alibaba,還有華為貢獻的 Apache ServiceComb,螞蟻金服的 SOFAStack ,Oracle 的 Helidon,Redhat 的 Quarkus,基于 Scala 語言和 Akka 的 Lagom,基于 Grails 語言的 Micronaut,基于 Python 語言的 Nameko,基于 Golang 語言的 go-micro,支持多語言混編的響應(yīng)式微服務(wù)框架 Vert.X,騰訊開源的 Tars,百度開源的 Apache BRPC(孵化中),微博的簡化版 Dubbo 框架 Motan 等等。
- 配置中心:Apollo,Nacos,disconf,Spring Cloud Config,或者 Etcd、ZK、Redis 自己封裝
- 服務(wù)注冊發(fā)現(xiàn):Eureka,Consul,或者 Etcd、ZK、Redis 自己封裝
- 服務(wù)網(wǎng)關(guān):Zuul/Zuul2,Spring Cloud Gateway,nginx 系列的 Open Resty 和 Kong,基于 Golang 的 fagongzi Gateway 等
- 容錯限流:Hystrix,Sentinel,Resilience4j,或者直接用 Kong 自帶的插件
- 消息處理:Kafka、RabbitMQ、RocketMQ,以及 Pulsar,可以使用 Sping Messaging 或者 Spring Cloud Stream 來簡化處理
- 鏈路監(jiān)控與日志:開源的鏈路技術(shù)有 CAT、Pinpoint、Skywalking、Zipkin、Jaeger 等,也可以考慮用商業(yè)的 APM(比如聽云 APM、OneAPM、App Dynamic 等),日志可以用 ELK
- 認(rèn)證與授權(quán):比如要支持 OAuth2、LDAP,傳統(tǒng)的自定義 BRAC 等,可以選擇 Spring Security 或者 Apache Shiro 等
Sping Cloud 與 Apache Dubbo、Spring Cloud Alibaba
Spring Cloud 是一個較為成熟的微服務(wù)框架,定位為開發(fā)人員提供工具,以快速構(gòu)建分布式系統(tǒng)中的某些常見模式(例如,配置管理,服務(wù)發(fā)現(xiàn),斷路器,智能路由,微代理,控制總線,一次性令牌,全局鎖,Leader 選舉,分布式會話,群集狀態(tài))。其技術(shù)棧大致如下:
可以看到很多組件都是由 Netflix 貢獻的。
而 Apache Dubbo 定位是一款高性能、輕量級的開源 Java RPC 框架,它提供了三大核心能力:面向接口的遠程方法調(diào)用,智能容錯和負(fù)載均衡,以及服務(wù)自動注冊和發(fā)現(xiàn)。所以,我們可以看到 Dubbo 提供的能力只是 Spring Cloud 的一部分子集。
同時 Dubbo 項目重新維護以后,捐獻給了 Apache,項目的導(dǎo)師就是 Spring Cloud 的核心人員。自這時候起 Dubbo 項目就開始在適合 Spring Cloud 體系,結(jié)果就是 Alibaba 也基于自己的一系列開源組件和技術(shù),實現(xiàn)了 Spring Cloud Alibaba,并順利從 Spring Cloud 孵化器畢業(yè)。詳見:
https://spring.io/projects/spring-cloud-alibabaSpring Cloud Alibaba 致力于提供微服務(wù)開發(fā)的一站式解決方案。此項目包含開發(fā)分布式應(yīng)用微服務(wù)的必需組件,方便開發(fā)者通過 Spring Cloud 編程模型輕松使用這些組件來開發(fā)分布式應(yīng)用服務(wù)。主要功能和開源技術(shù)棧:
- 服務(wù)限流降級(Sentinel):默認(rèn)支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降級功能的接入,可以在運行時通過控制臺實時修改限流降級規(guī)則,還支持查看限流降級 Metrics 監(jiān)控。
- 服務(wù)注冊與發(fā)現(xiàn)(Nacos):適配 Spring Cloud 服務(wù)注冊與發(fā)現(xiàn)標(biāo)準(zhǔn),默認(rèn)集成了 Ribbon 的支持。
- 分布式配置管理(Nacos):支持分布式系統(tǒng)中的外部化配置,配置更改時自動刷新。
- 消息驅(qū)動能力(Apache RocketMQ):基于 Spring Cloud Stream 為微服務(wù)應(yīng)用構(gòu)建消息驅(qū)動能力。
- 分布式事務(wù)(Seata):使用 @GlobalTransactional 注解, 高效并且對業(yè)務(wù)零侵入地解決分布式事務(wù)問題。。
可以看到,基本上功能都比較完備了。
第二部分:微服務(wù)最佳實踐
微服務(wù)架構(gòu)不是銀彈
《管理的常識》一書里說,管理的核心是不斷的解決(推進工作過程中出現(xiàn)的各種)問題。同樣地,我認(rèn)為架構(gòu)的核心則是不斷的解決(系統(tǒng)設(shè)計實現(xiàn)與演化過程中的各種)問題。
Fred Brooks 在《人月神話》里說,“沒有銀彈”,現(xiàn)在依然成立,微服務(wù)也并不是只有優(yōu)點,沒有副作用,把系統(tǒng)拆分了了很多部分,每一個部分簡單了,但是整體的關(guān)系變復(fù)雜了。前面介紹了那么多微服務(wù)的特點和優(yōu)勢,以及相關(guān)技術(shù),我們再來分析一下微服務(wù)存在的問題,進而討論什么場景適合使用微服務(wù),什么場景不適合,以及最佳的實踐方式。
微服務(wù)架構(gòu)首先是一個分布式系統(tǒng)架構(gòu)。分布式系統(tǒng)的發(fā)展由來已久,但是近年來產(chǎn)生了理論和實現(xiàn)的大爆發(fā)。
究其原因:分布式系統(tǒng)的發(fā)展得益于廉價 pc 硬件使得堆機器成為可能,而單機的成本/容量是非線性的,所以分布式的核心是線性的水平擴展整個集群的功能,以及帶來的協(xié)調(diào)機制,管理復(fù)雜度,數(shù)據(jù)和狀態(tài)一致性,容錯和故障恢復(fù),容量與彈性伸縮,這些通用性的基礎(chǔ)能力建設(shè)。
簡單的翻譯一下:單個機器堆資源,升級 CPU 內(nèi)存加大一倍,想要增加一倍的處理能力且成本不超過一倍,不僅很難、而且不現(xiàn)實了。這樣,我們就需要思考怎么通過進一步對于系統(tǒng)里不同功能的部分,拆解開,單獨擴展這些能水平擴展的部分,從而在控制成本的前提下,提升整個系統(tǒng)的處理能力。
另外,我們現(xiàn)在都知道,設(shè)計系統(tǒng)如果對可靠性,可用性有非常高的要求,那么需要先假設(shè)上下游都是不可靠的,依賴的基礎(chǔ)設(shè)施和網(wǎng)絡(luò)也是不可靠的,這樣就考慮分布式后的分片、復(fù)制、故障轉(zhuǎn)移、災(zāi)難恢復(fù)等,分布式系統(tǒng)下,我們可以對系統(tǒng)的不同性質(zhì)的節(jié)點、不同可靠性可用性要求的組件,做針對性的單獨處理。
很多系統(tǒng)看起來是分布式的,其實是一個大單機。 很多系統(tǒng)看起來是微服務(wù)的,其實是一個大單體。
就像人月神話里說的,往已經(jīng)延期的項目,加人手,可能會導(dǎo)致更延期。問題不是出現(xiàn)在應(yīng)不應(yīng)該加人,或者應(yīng)不應(yīng)該使用分布式上。而是實施的人,沒搞清楚關(guān)鍵。比如系統(tǒng)拆成分布式或微服務(wù),然后某個關(guān)鍵地方依然卡住了業(yè)務(wù)流程,可能整體還不如單機的,擴展性失效,多加機器還不如單機。
為什么?
因為如果希望用多個機器來擴展業(yè)務(wù),最后發(fā)現(xiàn)各個機器上運行的程序沒有劃清楚邊界職責(zé),多個機器合起來并不比單個機器有更大處理能力,這就是一個大單機。
同樣的,如果我們拆分了很多更小獨立的服務(wù),但是一個業(yè)務(wù)請求進來,還是在一些地方被卡住,導(dǎo)致有一些瓶頸使得整個拆分后并不比之前有整體的改進,那么其實是費勁的做了一個大單體系統(tǒng)。
所以合理的拆分微服務(wù),使得我們能夠更好的擴展系統(tǒng)是關(guān)鍵,同時如果業(yè)務(wù)簡單,流量不大,不擴展也可以很好的應(yīng)對,系統(tǒng)對于可用性和一致性要求也不是特別高,那么分布式和微服務(wù),也不是必須的。
微服務(wù)系統(tǒng)適合的場景
以下幾類系統(tǒng),比較適合使用微服務(wù)架構(gòu),或者使用微服務(wù)架構(gòu)改造:
- 大型的前后分離的復(fù)雜業(yè)務(wù)系統(tǒng)后端,業(yè)務(wù)越復(fù)雜,越需要我們合理的設(shè)計和劃分,長期的維護成本會非常高,歷史包袱也很重,這個問題后面會詳談。
- 變化發(fā)展特別快的創(chuàng)新業(yè)務(wù)系統(tǒng),業(yè)務(wù)快,就意味著天天要“擁抱變化”,每一塊的業(yè)務(wù)研發(fā)都要被業(yè)務(wù)方壓的喘不過氣,不做拆分、自動化,一方面研發(fā)資源永遠不夠用,活永遠干不完,另一方面不停的在給系統(tǒng)打補丁,越來質(zhì)量越低,出錯的可能性也越來越大。
- 規(guī)劃中的新大型業(yè)務(wù)系統(tǒng),如果有能力一開始就應(yīng)該考慮做微服務(wù),而不是先做單體,還是發(fā)展到一定的階段再做改造,改造一定是傷筋動骨的大手術(shù),雖然我們可以采取一些策略做得更平滑,但是成本還是比較高的。
- 敏捷自驅(qū)的小研發(fā)團隊,擁抱新技術(shù),可以直接用微服務(wù)做系統(tǒng),不但的通過快速迭代、持續(xù)交付,經(jīng)過一定時間的嘗試和調(diào)整,形成自己的微服務(wù)實踐經(jīng)驗,這樣在團隊擴大時可以把好的經(jīng)驗復(fù)制到更大的團隊。
反過來,以下幾類系統(tǒng),不太適合一開始就做微服務(wù),
- 小團隊,技術(shù)基礎(chǔ)較薄弱,創(chuàng)業(yè)初期或者團隊新做的快速原型,這個時候做微服務(wù)的收益明顯比單體要低,快速把原型做出來怎么方便怎么來,用團隊最熟悉的技術(shù)棧。
- 流量不高,壓力小,業(yè)務(wù)變化也不大,單體能簡單搞定的,就可以先不考慮微服務(wù),不考慮分布式,因為分布式和微服務(wù)帶來的好處,可能還不足以抵消復(fù)雜性增加帶來的成本。
- 對延遲很敏感的低延遲高并發(fā)系統(tǒng),低延遲的秘訣就是離 io 能多遠就多遠,離 cpu 能多近就多近,分布式和微服務(wù),導(dǎo)致增加了網(wǎng)絡(luò)跳數(shù),延遲就沒法降低。
- 技術(shù)導(dǎo)向性的系統(tǒng),技術(shù)產(chǎn)品,這類產(chǎn)品跟業(yè)務(wù)系統(tǒng)不同,常規(guī)的業(yè)務(wù)系統(tǒng)研發(fā)方法不是太適合。
微服務(wù)帶來的一些問題
Chris Richardson 在
http://blog.daocloud.io/microservices-1/提到了微服務(wù)的幾個不足:
- 服務(wù)大小:『微服務(wù)』強調(diào)了服務(wù)大小,有人強調(diào)服務(wù)要大一點,也有人愿意采用小服務(wù)。需要注意這只是一種選擇,微服務(wù)的目的是有效的拆分應(yīng)用,實現(xiàn)敏捷開發(fā)和部署。
- 進程通訊:微服務(wù)應(yīng)用是分布式系統(tǒng),由此會帶來分布式固有的復(fù)雜性。開發(fā)者需要在 RPC 或者消息傳遞之間選擇并完成進程間通訊機制。相對于單體式應(yīng)用中通過語言層級的方法或者進程調(diào)用,微服務(wù)下這種技術(shù)顯得更復(fù)雜一些,需要考慮 RPC 的超時、重試、失效策略,或者消息服務(wù)不可用,堆積堵塞等問題。
- 數(shù)據(jù)庫拆分:數(shù)據(jù)庫事務(wù)對于單體式應(yīng)用來說很容易,因為只有一個數(shù)據(jù)庫。在微服務(wù)架構(gòu)應(yīng)用中,需要更新不同服務(wù)所使用的不同的數(shù)據(jù)庫。使用分布式事務(wù)并不一定是好的選擇,不僅僅是因為 CAP 理論,還因為很多中間件并不支持這一需求。最終你不得不使用一個最終一致性的方法,從而對開發(fā)者提出了更高的要求和挑戰(zhàn)。
- 測試復(fù)雜度:比如采用流行的 Spring Boot 架構(gòu),對一個單體式 web 應(yīng)用,測試它的 REST API,是很容易的事情。反過來,同樣的一個業(yè)務(wù)場景,需要測試啟動和它有關(guān)的所有服務(wù),這些服務(wù)在不同的進程,所以變得尤為復(fù)雜。
- 服務(wù)依賴關(guān)系:微服務(wù)架構(gòu)模式應(yīng)用的改變將會波及多個服務(wù)。比如,假設(shè)你在完成一個案例,需要修改服務(wù) A、B、C,而 A 依賴 B,B 依賴 C。在單體式應(yīng)用中,你只需要改變相關(guān)模塊,整合變化,部署就好了。對比之下,微服務(wù)架構(gòu)模式就需要考慮相關(guān)改變對不同服務(wù)的影響。比如,你需要更新服務(wù) C,然后是 B,最后才是 A,幸運的是,許多改變一般只影響一個服務(wù),而需要協(xié)調(diào)多服務(wù)的改變很少。特別是,如果要處理的服務(wù)間依賴是一個網(wǎng)狀關(guān)系,那么很可能導(dǎo)致,我們修改 A 時,考慮到了會影響 BCDEF 這幾個服務(wù),但是漏掉了影響系統(tǒng) K,導(dǎo)致上線以后發(fā)現(xiàn) K 的部分功能無法使用了。
- 部署復(fù)雜度:部署一個微服務(wù)應(yīng)用也很復(fù)雜,一個分布式應(yīng)用只需要簡單在負(fù)載均衡服務(wù)后面部署各自的服務(wù)器就好了。每個應(yīng)用實例是需要配置諸如數(shù)據(jù)庫和消息中間件等基礎(chǔ)服務(wù)。而一個微服務(wù)應(yīng)用一般由大批服務(wù)構(gòu)成。例如,根據(jù) Adrian Cockcroft,Hailo 有 160 個不同服務(wù)構(gòu)成,NetFlix 有大約 600 個服務(wù)。每個服務(wù)都有多個實例。這就造成許多需要配置、部署、擴展和監(jiān)控的部分,除此之外,你還需要完成一個服務(wù)發(fā)現(xiàn)機制,以用來發(fā)現(xiàn)與它通訊服務(wù)的地址(包括服務(wù)器地址和端口)。傳統(tǒng)的解決問題辦法不能用于解決這么復(fù)雜的問題。接續(xù)而來,成功部署一個微服務(wù)應(yīng)用需要開發(fā)者有足夠的成熟部署方法,并高度自動化。
Peter Lawyer 有在
https://vanilla-java.github.io/2016/03/22/Micro-services-for-performance.html 中提出,微服務(wù)的幾個問題:
- 服務(wù)形成信息障礙。
- 引入了額外的復(fù)雜性和新問題,例如網(wǎng)絡(luò)等待時間,消息格式,負(fù)載平衡和容錯。忽略其中之一屬于“分布式計算的謬誤”。
- 測試和部署更加復(fù)雜。
- 整體應(yīng)用程序的復(fù)雜性僅轉(zhuǎn)移到網(wǎng)絡(luò)中,但仍然存在。
- 細(xì)粒度的微服務(wù)已被批評為一種反模式。
其實可以看到,大家的想法和分析都比較一致,微服務(wù)架構(gòu)帶來的固有復(fù)雜性和人為復(fù)雜性如何管理:
- 如何合理拆分微服務(wù),粒度如何控制:對業(yè)務(wù)按粒度和邊界拆解的問題,這決定了我們的服務(wù)是不是合理,開發(fā)和維護是不是方便。核心思路是深入了解業(yè)務(wù)。
- 遺留系統(tǒng)應(yīng)該如何改造,從哪兒下手,如何推動:改造遺留系統(tǒng)一般來說比重新做一個新系統(tǒng)更復(fù)雜,如何平滑的、最大代價的將老系統(tǒng)改造成微服務(wù),也是一個巨大挑戰(zhàn)。
- 拆分后的性能應(yīng)該如何保障:性能有兩個指標(biāo),關(guān)鍵是區(qū)分出來如何處理不同特性的數(shù)據(jù),關(guān)注不同的指標(biāo),做好優(yōu)化和選型,針對具體細(xì)節(jié)具體對待。
- 怎么考慮拆分后的數(shù)據(jù)一致性,分布式事務(wù)的選取和取舍:多個服務(wù)間的業(yè)務(wù)數(shù)據(jù)一致性的問題,事務(wù)是個需要重點考慮的問題,主要是考慮強一致性的分布式事務(wù)還是可以使用最終一致的弱一致性。對于一般的業(yè)務(wù),可能使用補償沖正類的做法,在業(yè)務(wù)允許的一定時間內(nèi)數(shù)據(jù)達到一致狀態(tài)即可,比如 30s,或者 10 分鐘。
- 系統(tǒng)和服務(wù)的高可用可伸縮如何實現(xiàn):高可用意味著系統(tǒng)穩(wěn)定健壯,可伸縮意味著彈性,資源有效使用,如何做到無狀態(tài)、不共享,是實現(xiàn)可高用可伸縮的關(guān)鍵。
- 拆分過程的測試和部署如何處理,怎么提升管理能力,降低風(fēng)險:跨系統(tǒng)的協(xié)作問題、測試的問題,這對于技術(shù)能力和研發(fā)成熟度較低的團隊,會帶來很大麻煩。核心思路定義好業(yè)務(wù)邊界、系統(tǒng)間接口與數(shù)據(jù)標(biāo)準(zhǔn),提升自動化測試水平。微服務(wù)架構(gòu)由于拆分粒度較細(xì),測試是個大問題。在保持每塊職責(zé)單一的同時,關(guān)鍵是保證接口穩(wěn)定,做好自動化測試(特別是 UT 和接口的自動化)和持續(xù)集成,盡量少全流程的人工回歸測試。部署單元太多導(dǎo)致維護成本上升的問題,要管理的點多了,問題自然復(fù)雜了,核心思路是自動化部署與運維。
- 拆分后的運維和監(jiān)控如何處理:怎么應(yīng)對系統(tǒng)的故障,關(guān)鍵是保障核心技術(shù)的指標(biāo),以及業(yè)務(wù)指標(biāo),做好預(yù)警報警,積累問題、尋找根因,控制和杜絕低級失誤。
七個關(guān)鍵問題的應(yīng)對策略
如何合理拆分微服務(wù)
當(dāng)一個系統(tǒng)服務(wù)化的時候,就會面臨一個問題:如何進行服務(wù)的劃分?怎么確定服務(wù)的粒度?有沒有一些可以參考的業(yè)界通用規(guī)則?
實際上服務(wù)劃分的本質(zhì)是對系統(tǒng)進行架構(gòu)設(shè)計,服務(wù)的劃分粒度沒有絕對的過大或過小之說,不同階段的側(cè)重點和思考的角度也不盡相同。創(chuàng)業(yè)初期的團隊,過分的追求微服務(wù),為了“微”而微,反而會導(dǎo)致業(yè)務(wù)邏輯過于分散,技術(shù)架構(gòu)過于復(fù)雜,團隊基礎(chǔ)設(shè)施搭建能力弱,進而導(dǎo)致忽略了快速迭代交付產(chǎn)品的重要性,可能錯失了市場機會。所以,關(guān)于服務(wù)的劃分不是對錯的選擇題,而是需要綜合考慮各種外界的因素,所作出的一個最適合的決策,這些外界因素通常包括業(yè)務(wù)、技術(shù)債、開發(fā)、運維、測試這
五個方面:
- 業(yè)務(wù)所處領(lǐng)域的市場性質(zhì):對市場比較敏感的項目,創(chuàng)業(yè)初期粒度應(yīng)該盡量劃分的粗一些,先提供充足的彈藥去占領(lǐng)市場,然后再去考慮對系統(tǒng)進行重構(gòu)和優(yōu)化;
- 與原有系統(tǒng)之間的關(guān)系:對于歷史遺留的系統(tǒng),需要做好新舊系統(tǒng)之間的邊界劃分,避免過于激進、過大幅度的改造,應(yīng)該采取小步快跑的方式,有節(jié)奏的對老系統(tǒng)進行服務(wù)化改造;
- 開發(fā)團隊的成熟度:服務(wù)化帶來的技術(shù)風(fēng)險應(yīng)該提前進行評估,要考慮團隊的承受度,用合適的人做適合的事,考慮團隊需要有包括敏捷,包括 Devops,包括基礎(chǔ)設(shè)施,運維和測試的自動化等基礎(chǔ)能力;
- 基礎(chǔ)設(shè)施的搭建能力:在進行細(xì)粒度的服務(wù)劃分時,要考慮團隊是否有足夠的能力來支撐大量服務(wù)實例運行的運維復(fù)雜度,是否可以做好分布式的日志追蹤和服務(wù)的監(jiān)控;
- 測試團隊的測試執(zhí)行效率:過于細(xì)粒度的服務(wù)劃分,如果測試團隊不能通過自動化測試、自動回歸、壓力測試、極限測試等手段來提高測試執(zhí)行效率,必然會帶來測試工作量的大幅度上升,進而影響整個項目的上線周期;
如果沒有特別強烈的大規(guī)模水平擴展需求,拆分就沒有必要,反而把問題搞復(fù)雜了。進行服務(wù)化的拆分時,通常會先按照業(yè)務(wù)子系統(tǒng)先進行一次劃分,根據(jù)業(yè)務(wù)邏輯和數(shù)據(jù)的關(guān)系劃分為若干個子系統(tǒng),然后再考慮子系統(tǒng)內(nèi)部是否可以再次進行拆分。至于拆分的基本原則,我推薦:
- 高內(nèi)聚低耦合:這個已經(jīng)提了很多了,簡單說一下,就是要把強相關(guān)的部分,總是會一起改動的部分,聚合到一起,相關(guān)性不大的部分拆開,可以參考 DDD 中的一些辦法。
- 粗粒度服務(wù):服務(wù)的粒度要稍微的抽象和粗粒度一些,因為服務(wù)是基于業(yè)務(wù)場景的抽象和設(shè)計,不能做成是直接把數(shù)據(jù)庫的增刪改查暴露出來成接口和方法,而是應(yīng)該隱藏這些細(xì)節(jié),考慮清楚從業(yè)務(wù)和客戶角度來看,哪些步驟和過程,是必須封裝起來的,細(xì)節(jié)隱藏掉,然后對外提供的就是粗粒度的服務(wù),而在單體系統(tǒng)的時候,我們可以直接調(diào)用這些細(xì)節(jié),無需過多考慮。
遺留系統(tǒng)應(yīng)該如何改造
對舊系統(tǒng)進行改造,可以分為幾個步驟:拆分前準(zhǔn)備階段,設(shè)計拆分改造方案,實施拆分計劃。下面是我的一些經(jīng)驗之談。
1)拆分之前先梳理系統(tǒng)關(guān)系和接口
其中準(zhǔn)備階段主要是梳理清楚了依賴關(guān)系和接口,就可以思考如何來拆,第一刀切在哪兒里,即能達到快速把一個復(fù)雜單體系統(tǒng)變成兩個更小系統(tǒng)的目標(biāo),又能對系統(tǒng)的現(xiàn)有業(yè)務(wù)影響最小。要盡量避免構(gòu)建出一個分布式的單體應(yīng)用,一個包含了一大堆互相之間緊耦合的服務(wù),卻又必須部署在一起的所謂分布式系統(tǒng)。沒分析清楚就強行拆,可能就一不小心剪斷了大動脈,立馬搞出來一個 A 類大故障,后患無窮。
2)不同階段拆分要點不同,每個階段的關(guān)注點要聚焦
拆分本身可以分成三個階段,核心業(yè)務(wù)和非業(yè)務(wù)部分的拆分、核心業(yè)務(wù)的調(diào)整設(shè)計、核心業(yè)務(wù)內(nèi)部的拆分。這三個階段,第一階段將核心業(yè)務(wù)瘦身,把非核心的部分切開,減少需要處理的系統(tǒng)大?。坏诙A段。重新按照微服務(wù)設(shè)計核心業(yè)務(wù)部分;第三階段把核心業(yè)務(wù)部分重構(gòu)設(shè)計落地。拆分的方式也有三個:代碼拆分、部署拆分、數(shù)據(jù)拆分。代碼直接體現(xiàn)了依賴關(guān)系,拆完就可以單獨打包部署。但是有時候,我們可以通過控制一些提供服務(wù)的開關(guān),使用同一份代碼和打包的程序,部署多組進程,每組提供不同的服務(wù),這就是部署拆分,比如同一份代碼,我們部署了 3 組機器,A 組 5 臺提供訂單服務(wù),B 組 2 臺提供用戶服務(wù),C 組 2 臺提供任務(wù)調(diào)度處理任務(wù)。數(shù)據(jù)拆分最復(fù)雜,涉及到代碼的調(diào)整,SQL 和事務(wù)的分析和重構(gòu),數(shù)據(jù)庫表的拆分甚至數(shù)據(jù)遷移,數(shù)據(jù)結(jié)構(gòu)的調(diào)整和數(shù)據(jù)遷移則一般意味著需要停機維護。這三個方式,可以在適當(dāng)?shù)臈l件下選擇先做哪個操作合適。 另外,每個階段需要聚焦到一兩個具體的目標(biāo),否則目標(biāo)太多反而很難把一件事兒做通透。例如某個系統(tǒng)的微服務(wù)拆分,制定了如下的幾個目標(biāo):
- 性能指標(biāo)(吞吐和延遲):核心交易吞吐提升一倍以上(TPS:1000->10000),A 業(yè)務(wù)延遲降低一半(Latency:250ms->125ms),B 業(yè)務(wù)延遲降低一半(Latency:70ms->35ms)。
- 穩(wěn)定性指標(biāo)(可用性,故障恢復(fù)時間):可用性>=99.99%,A 類故障恢復(fù)時間<=15 分鐘,季度次數(shù)<=1 次。
- 質(zhì)量指標(biāo):編寫完善的產(chǎn)品需求文檔、設(shè)計文檔、部署運維文檔,核心交易部分代碼 90%以上單測覆蓋率和 100%的自動化測試用例和場景覆蓋,實現(xiàn)可持續(xù)的性能測試基準(zhǔn)環(huán)境和長期持續(xù)性能優(yōu)化機制。
- 擴展性指標(biāo):完成代碼、部署、運行時和數(shù)據(jù)多個維度的合理拆分,對于核心系統(tǒng)重構(gòu)后的各塊業(yè)務(wù)和交易模塊、以及對應(yīng)的各個數(shù)據(jù)存儲,都可以隨時通過增加機器資源實現(xiàn)伸縮擴展。
- 可維護性指標(biāo):建立全面完善的監(jiān)控指標(biāo)、特別是全鏈路的實時性能指標(biāo)數(shù)據(jù),覆蓋所有關(guān)鍵業(yè)務(wù)和狀態(tài),縮短監(jiān)控報警響應(yīng)處置時間,配合運維團隊實現(xiàn)容量規(guī)劃和管理,出現(xiàn)問題時可以在一分鐘內(nèi)拉起系統(tǒng)或者回滾到上一個可用版本(啟動時間<=1 分鐘)。
- 易用性指標(biāo),通過重構(gòu)實現(xiàn)新的 API 接口既合理又簡單,極大的滿足各個層面用戶的使用和需要,客戶滿意度持續(xù)上升。
- 業(yè)務(wù)支持指標(biāo):對于新的業(yè)務(wù)需求功能開發(fā),在保障質(zhì)量的前提下,開發(fā)效率提升一倍,開發(fā)資源和周期降低一半。
- 核心人員指標(biāo):培養(yǎng) 10 名以上熟悉核心交易業(yè)務(wù)和新系統(tǒng)的一線技術(shù)人員,形成結(jié)構(gòu)合理的核心研發(fā)人才梯隊。
結(jié)果可想而知了,目前太多了,反而沒有目標(biāo)。最后第一階段只選擇了穩(wěn)定性作為最重要的指標(biāo),先穩(wěn)住系統(tǒng),然后再在后面的階段里選擇其他指標(biāo),逐步實現(xiàn)各個目標(biāo)。
3)快速迭代,找到突破口,持續(xù)產(chǎn)出
大家都知道,敏捷開發(fā)之所以流行,就是因為小步快跑,快速迭代,實現(xiàn)對業(yè)務(wù)變化和新需求的第一時間響應(yīng),這對快速發(fā)展變化的外部市場,以及 KPI 壓力非常大的業(yè)務(wù)部門非常重要。研發(fā)團隊在系統(tǒng)改造過程也可以通過快速的階段性產(chǎn)出,來證明團隊的技術(shù)能力和推進水平,增進互相的背靠背信任關(guān)系,為長期的順暢合作打下堅實基礎(chǔ)。這方面,很多研發(fā)團隊都想試圖憋大招,搞個大項目,反而慢慢失去各個利益方的耐心,最終把合作關(guān)系搞僵,吃了大虧。
4)大膽假設(shè),小心求證,穩(wěn)步上線
凡事不破不立,拆分改造過程,我們每一次改動的地方,可能有多個不同的方案和路徑,具體選擇哪一個最合適,這需要我們放開思路,大膽假設(shè),充分吸收各方面的意見和想法,然后小心謹(jǐn)慎的去測試,甚至在線上做驗證,保障萬無一失后,最后上線。
5)保障質(zhì)量,不斷重構(gòu)和改善現(xiàn)有設(shè)計和代碼
所有的事物都有產(chǎn)生,發(fā)展,衰退和消亡的過程。長期來看,軟件系統(tǒng)的代碼質(zhì)量肯定是會一直下降的,就像是人的身體健康,到了一定的程度,就會難以為繼,需要重構(gòu)或者重做。而不斷的重構(gòu),改善現(xiàn)有的設(shè)計集合代碼,就像是一直在保養(yǎng)身體,可以減緩衰老,保證健康,增加壽命。
6)取得領(lǐng)導(dǎo)和業(yè)務(wù)方的支持,過程和決策透明化
拆分改造看起來,沒有給系統(tǒng)帶來明確的可見收益,比如沒有明顯改進了用戶體驗,也沒有給系統(tǒng)新增了一個業(yè)務(wù)功能,但是卻涉及到多方參與,付出勞動,這就必然會帶來很大的阻力,怎么辦呢?還是從《管理的常識》一書里,我看到了一個很有道理的話:”如果無法推動問題背后的人解決問題,那說明對問題挖掘的還不夠深“?,F(xiàn)代化的工作教會我們,雙贏/多贏是協(xié)作的唯一辦法,也是可以持續(xù)的辦法。搞清楚怎么才能推動各個合作方的支持,怎么才能讓領(lǐng)導(dǎo)同意,如果我們現(xiàn)在提的意見,他們不同意,那么他們關(guān)心的點是什么,怎么把他們關(guān)心的點,納入到這個工作范圍里來,從而實現(xiàn)大家可以達成一致來合作。同時需要注意的是,信息一定要透明,決策要公開,讓大家都直接參與到這個過程,從而明確目標(biāo),一致前行。
總結(jié)成 48 字箴言的“微服務(wù)拆分核心價值觀”:
- 功能剝離、數(shù)據(jù)解耦
- 自然演進、逐步拆分
- 小步快跑、快速迭代
- 灰度發(fā)布、謹(jǐn)慎試錯
- 提質(zhì)量線、還技術(shù)債
- 各方一致,過程透明
理想中的系統(tǒng)拆分改造效果(實際上一般最后都雞飛狗跳):
關(guān)于微服務(wù)對性能的影響
大家可以先思考 2 個問題:延遲(latency)和吞吐量(throughout)有什么關(guān)系? 延遲是響應(yīng)時間么?
先說一下延遲和響應(yīng)時間,延遲是對于服務(wù)本身來說的,響應(yīng)時間是相當(dāng)于調(diào)用者來說的(更多的內(nèi)容可以參考《數(shù)據(jù)密集型應(yīng)用系統(tǒng)設(shè)計》一書):
- 延遲(latency) = 請求響應(yīng)出入系統(tǒng)的時間
- 響應(yīng)時間(ResponseTime)= 客戶端請求開始,一直到收到響應(yīng)的時間 = 延遲 + 網(wǎng)絡(luò)耗時
理想狀態(tài)下,延遲越低,吞吐越高,當(dāng)然這是對單機單線程而言的,在分布式下就不成立了,舉個反例:
比如從密云水庫,拉一個水管到國貿(mào),水流到國貿(mào),需要 1 小時;如果再拉一個水管到順義,20 分鐘就可以。如果你在國貿(mào)用水龍頭接水,你可以單位時間接到非常多的水,這個數(shù)量跟你在過國貿(mào)還是順義,沒有關(guān)系,只跟水庫單位時間輸入的水量/水壓有關(guān)系。但是如果你在水管里放一個小球,它從密云到國貿(mào)的時間是到順義的時間的三倍,這樣對于到國貿(mào)的這個水管系統(tǒng),延遲很高,但是系統(tǒng)的吞吐量跟到順義的是一樣的。
同理,如果一個單體系統(tǒng),被拆分成了 10 個服務(wù),假如一個業(yè)務(wù)處理流程要經(jīng)過 5 個服務(wù),這 5 個服務(wù)只要是每個吞吐量(TPS/QPS)不低于原先的單體,那么整個微服務(wù)系統(tǒng)的吞吐量是不變的。
相反地,我們通過服務(wù)變小,關(guān)系變簡單,數(shù)據(jù)庫簡化,事務(wù)變小等等,如果 5 個系統(tǒng)的吞吐都比原來的系統(tǒng)打,那么改造后的系統(tǒng),整體的吞吐也比之前要高。那么這個過程的副作用是什么呢?
簡單的說,就是延遲變高了,原來都是本地調(diào)用,現(xiàn)在變成了 5 次遠程調(diào)用,假設(shè)每次調(diào)用的網(wǎng)絡(luò)延遲在 1-10 毫秒(物理機房+萬兆網(wǎng)卡可以很低,云環(huán)境下比較高),那么延遲就會比之前增加增加 5-50 毫秒,而且前提是分布式下的請求,使用異步非阻塞的流式或消息處理方式,同步阻塞會更高,而且影響吞吐量。好在低延遲的系統(tǒng)要求比較少見,對于一般的業(yè)務(wù)系統(tǒng)來說,可以水平擴展的能力比延遲增加幾毫秒要重要的多。
比如我們在淘寶或者京東,買個衣服,交易步驟的處理,在秒級都是可以接受的,如果是機票、酒店、電影票之類的,分鐘級以上都是可以接受的。
再舉一個現(xiàn)實的例子,某個公司從 2016 年起,就在做微服務(wù)改造,研發(fā)團隊規(guī)模不大,業(yè)務(wù)發(fā)展很快,基礎(chǔ)設(shè)施沒有跟上,自動化測試、部署都沒有。同時這個公司的主要核心業(yè)務(wù)是一個低延遲高并發(fā)的交易系統(tǒng),微服務(wù)拆分導(dǎo)致系統(tǒng)的延遲進一步增大,客戶滿意度下降。很快研發(fā)團隊就發(fā)現(xiàn)了拆分成了多個小系統(tǒng)以后,比單體更難以維護,繼而采取了措施,把部分微服務(wù)進行合并,提高可維護性和控制延遲水平。
對于分布式微服務(wù)系統(tǒng)的低延遲設(shè)計,更多信息可以參考 Peter Lawyer 的博客:
https://vanilla-java.github.io/。
怎么考慮拆分后的數(shù)據(jù)一致性
微服務(wù)提倡每一個服務(wù)都使用自己的數(shù)據(jù)庫存儲,這就涉及到老系統(tǒng)的數(shù)據(jù)庫拆分改造。一般的數(shù)據(jù)庫拆分,我們可以把不同業(yè)務(wù)服務(wù)涉及的表拆分到不同的庫中去,這樣如果之前的事務(wù)中操作的兩個表如果被拆分到了不同的數(shù)據(jù)庫,就會涉及到分布式事務(wù)。下面先解釋一下分布式事務(wù)。
我們知道 ACID(原子性 Atomicity、一致性 Consistency、隔離性 Isolation、持久性 Durability)定義了單個數(shù)據(jù)庫操作的事務(wù)性,這樣我們就能放心的使用數(shù)據(jù)庫,而不用擔(dān)心數(shù)據(jù)的一致性,操作的原子性等等。由于數(shù)據(jù)庫同時可以并發(fā)的給多個應(yīng)用、多個會話線程使用,這樣就涉及到了鎖,隔離級別和數(shù)據(jù)可見性等一系列工作,好在關(guān)系數(shù)據(jù)庫都已經(jīng)幫我們解決了這些問題。
但是在 SOA、分布式服務(wù)化和微服務(wù)架構(gòu)的大背景下,數(shù)據(jù)拆分到多個不同的庫已經(jīng)是常態(tài),這種改造或者設(shè)計中,同一個業(yè)務(wù)處理涉及到的關(guān)聯(lián)數(shù)據(jù)生命周期可能要貫穿到多個不同的數(shù)據(jù)庫,如果沒有事務(wù)保證,那么數(shù)據(jù)的一致性或者正確性就會收到破壞,賬就可能會錯亂了,平臺或者客戶就會產(chǎn)生損失了。如何保證數(shù)據(jù)的事務(wù)性,則是一個非常有意思的話題。傳統(tǒng)的數(shù)據(jù)庫和消息系統(tǒng)一般都是支持 XA 分布式事務(wù),通過一個 TM 事務(wù)管理器協(xié)調(diào)各個 RM 資管管理器,每個 RM 管理自己的本地事務(wù),通過兩階段提交 2PC 來保障一致。
由于 CAP 的不可能三角約束下,我們大部分時候選擇了從 ACID 到 BASE(Basically Available 基本可用, Soft-state 柔性狀態(tài), Eventually consistent 最終一致),這樣分布式事務(wù)我們一般也從 XA 變成了 TCC(Try-Commit-Cancel),把分布式事務(wù)的控制權(quán)從底層資源層(比如數(shù)據(jù)庫)挪到了業(yè)務(wù)實現(xiàn)層,從而通過釋放數(shù)據(jù)庫層的鎖,來提升性能和靈活性。具體情況可以參閱下面的兩個文章。
- 分布式事務(wù)的原理綜述,講的非常詳細(xì): https://mp.weixin.qq.com/s/syPKHckm6uZ3TgJYfRnw
- 分布式事務(wù)解決方案與適用場景分析,結(jié)合實際,詳細(xì)說明了 TCC 的原理和用法: https://mp.weixin.qq.com/s/Okvgn5beGy5aJypfu6mKcg
我的好朋友長源和張亮,也分別寫過一個分布式事務(wù)的系列:
- 長源-漫談分布式事務(wù):https://zhuanlan.zhihu.com/distributedDatabase
- 張亮-分布式事務(wù):https://shardingsphere.apache.org/document/current/cn/features/transaction/
如果直接操作數(shù)據(jù)庫或者支持 XA/JTA 的 MQ,可以使用 XA 事務(wù)。其他情況,可以使用開源的分布式事務(wù)中間件。開源的分布式事務(wù)中間件有 Apache ServiceComb Pack,Seata/Fescar,Apache ShardingSphere 等。
(個人微信號:Robynn-D , 歡迎交流)
但是實際上,對大部分業(yè)務(wù)來說,性能遠大于分布式事務(wù)帶來的強一致性要求。更多時候,我們可以先犧牲掉強一致性,甚至是準(zhǔn)實時一致性,先讓多個不同節(jié)點的服務(wù),都自己單獨把業(yè)務(wù)執(zhí)行掉。然后再通過定時任務(wù)去檢查是否一致的狀態(tài),如果不一致,保證可以從某個存儲拿到原來的數(shù)據(jù),重新執(zhí)行即可。這時候其實是做了補償?shù)牟僮?,補償會帶來數(shù)據(jù)重復(fù)處理的情況,就是檢查的時候沒有執(zhí)行,但是去補償操作的時候,可能已經(jīng)在執(zhí)行了。
特別是我們使用異步的 MQ 之類的方式做業(yè)務(wù)處理和補償,消息也可能由于 MQ 的機制而重復(fù)。這時候我們只需要加上業(yè)務(wù)處理的冪等操作即可,比如訂單處理,我們可以使用 Redis 或者 RoaringBitmap,把最近的 10000 個訂單 id 或者 1 小時以內(nèi)的訂單 id,都放進去,每次訂單處理之前,先看一下 id 是不是已經(jīng)在里面了,如果是說明重復(fù)了,就不處理。
總之,最好的處理辦法就是直接犧牲掉強一致性和準(zhǔn)實時一致性,不用事務(wù),既簡單,又快速。
系統(tǒng)和服務(wù)的高可用可伸縮如何實現(xiàn)
1) 擴展立方體
既然分布式的核心是水平擴展系統(tǒng),那么我們先來看看“擴展立方體”,如上圖所示,擴展有三個維度:
- x 軸-水平復(fù)制:復(fù)制系統(tǒng),簡單說就是集群,把整個系統(tǒng)作為一個整體,重新部署幾套,前面加上集群管理器或者負(fù)載均衡器;
- y 軸-功能解耦:拆分業(yè)務(wù),按業(yè)務(wù)把不同的部分切割開,這樣可以使用服務(wù)化的方式,把不同服務(wù)獨立部署,然后各個服務(wù)可以自己多實例部署來擴展;
- z 軸-數(shù)據(jù)分區(qū):切分?jǐn)?shù)據(jù),把相同的數(shù)據(jù)按照不同的屬性切割開來擴展系統(tǒng),比如都是用戶,VIP 用戶比普通用戶對平臺交易的價值高,我們就可以把 VIP 用戶相關(guān)的數(shù)據(jù)單獨切分出來,單獨提供資源處理,這樣可以在 VIP 用戶訪問量很大(比如 VIP 用戶都是高頻的量化交易程序)時,單獨給 VIP 用戶增加機器資源,擴展 VIP 用戶的交易訂單處理能力。
我們可以通過上面三個維度的擴展性,靈活的應(yīng)用到自己的場景里去。高水平擴展性帶來我們隨時給系統(tǒng)增加機器資源的能力。假設(shè) A 系統(tǒng)的可用性為 99%,忽略掉負(fù)載均衡器的可用性,那么每多一個 A 服務(wù)節(jié)點,就意味著我們的可用性再增加 2 個 9,2 個節(jié)點是 99.99%,3 個節(jié)點就是 99.9999%的可用性。(這也說明了為什么要拆分?jǐn)?shù)據(jù)庫,數(shù)據(jù)庫跟系統(tǒng)是串行的,只使用一個數(shù)據(jù)庫,就意味著數(shù)據(jù)庫宕機導(dǎo)致所有的服務(wù)節(jié)點都不可用了。)
2) 無狀態(tài)系統(tǒng)
同時為了做到盡量高的擴展性,我們需要盡量讓每個服務(wù)是無狀態(tài)的、不共享狀態(tài)和數(shù)據(jù),這樣的話每個服務(wù)才能隨時增加機器。
3) 版本機制與特性開關(guān)
當(dāng)我們新上線一個功能,或者升級一個現(xiàn)有功能的時候,可能會產(chǎn)生不兼容,或者對客戶有未預(yù)期的影響,這個時候考慮提供版本化的接口,或者使用特型開關(guān),在一定的時間窗口內(nèi)實現(xiàn)對客戶的兼容和友好用戶體驗。
4) 容錯機制
分布式環(huán)境下,我們默認(rèn)上下游和基礎(chǔ)設(shè)施都是不可靠的,那么怎么在不可靠的假設(shè)基礎(chǔ)上實現(xiàn)可靠性?這就要求我們考慮容錯性,實現(xiàn)面向負(fù)載和面向失敗的設(shè)計,考慮系統(tǒng)的限流、服務(wù)熔斷和降級、系統(tǒng)過載保護等。
5)盡早發(fā)現(xiàn)問題
程墨 Morgan 在
https://www.zhihu.com/question/21325941/answer/173370966 里提到的 case 一樣,不要畫蛇添足,做一些不必要的操作,“要讓服務(wù)穩(wěn)定而高可用,靠的可不是一臺服務(wù)器,應(yīng)該用多服務(wù)的方式來應(yīng)對”,如果系統(tǒng)可能會發(fā)生問題,就讓問題早點暴露,而不是讓系統(tǒng)帶病運行,最終問題大爆發(fā),可能錯過了最佳的處理時機。我也深有體會,特別是交易類的系統(tǒng),如果帶著一些不一致的 bug 運行一段時間,可能會讓大量的交易賬目出錯,再來修補問題,更正數(shù)據(jù),代價非常大,甚至要賠償給用戶上百萬美元。
6)根因分析、積累故障處理經(jīng)驗
歷史告訴我們,錯誤如果不徹底分析原因解決,一定會再次發(fā)生。所以,把所有犯過的錯,出過的故障,積累起來,每次都做根因分析,一層層的復(fù)盤,找到本質(zhì)原因,制定改進行動項,才能解決這一類問題。經(jīng)過一段時間的積累,再分析這一段時間的故障情況,就知道我們的研發(fā)團隊哪一塊的能力有缺失,應(yīng)該去增強和補充這一塊的能力。通過積累處理經(jīng)驗,提升系統(tǒng)的可用性和穩(wěn)定性。
上面總結(jié)了一些高可用可伸縮的要點,下面的小節(jié)會講到自動伸縮擴容。
拆分過程的測試和部署如何處理
通過前面的分析,我們了解到測試、部署和運維,在微服務(wù)環(huán)境下會變得復(fù)雜。試想,原來只需要測試一個系統(tǒng),現(xiàn)在要測試一堆系統(tǒng),原來要發(fā)布一個應(yīng)用,現(xiàn)在要發(fā)布一堆應(yīng)用。原來線上排查問題,只需要從一個日志文件看日志信息,一個數(shù)據(jù)庫找數(shù)據(jù),現(xiàn)在都不知道去哪兒找數(shù)據(jù),因為第一時間不知道業(yè)務(wù)處理在哪個環(huán)節(jié)出錯了,需要先搞清楚一個跨多個系統(tǒng)的調(diào)用處理過程,在哪個環(huán)節(jié)出了錯,如果是顯式的錯誤,有日志還好點,要是沒有報錯,而是數(shù)據(jù)錯了,那簡直就是排查問題的噩夢。
特別是如果兩個不同的服務(wù)系統(tǒng),分別是兩個小組維護,一有問題就可能會產(chǎn)生相互推諉扯皮,A 讓 B 先去排查是不是 B 的問題,B 讓 A 去排查是不是 A 的問題,出現(xiàn)兩個和尚沒水吃的尷尬境地。一個解決問題的辦法就是,自動化,降低人為因素的影響,也消滅服務(wù)拆分帶來的這種重復(fù)勞動的復(fù)雜性,提升測試、部署、運維效率。
1) 自動化測試
建立全功能覆蓋的測試 case,并實現(xiàn)自動化,變更時全量自動回歸。集成 Sonar 等工具,檢查代碼風(fēng)格、單測覆蓋率和成功率等,控制代碼質(zhì)量。我們一般要求核心業(yè)務(wù)代碼,覆蓋率 100%;重要業(yè)務(wù)代碼,覆蓋率 90%;一般的后端業(yè)務(wù)代碼,覆蓋率 80%;其他代碼覆蓋率 60%。遺留代碼,維護時把本次修改設(shè)計到的代碼,覆蓋率提升到 60%。代碼風(fēng)格可以參考阿里巴巴或是 Google 的 Code Style 編碼規(guī)范定制適合自己團隊的標(biāo)準(zhǔn)。
2)自動化部署
借助與 Jenkins、Nexus、Ansible,Docker、K8S 等工具,實現(xiàn)多個應(yīng)用的自動打包,編排,以及自動化部署,構(gòu)建微服務(wù)項目的部署流水線。特別是基于 K8S,我們可以實現(xiàn)微服務(wù)的服務(wù)自愈和自動彈性伸縮,在服務(wù)失敗后重新拉起,在負(fù)載高或者低時動態(tài)控制容器數(shù)量。
3)自動化運維
通過標(biāo)準(zhǔn)規(guī)范,配置管理工具,資源交付工具等手段的配合,逐步實現(xiàn)基礎(chǔ)架構(gòu)、應(yīng)用、IT 服務(wù)和業(yè)務(wù)運營的自動化,實現(xiàn)日常運維處理和運維流程的自動化,降低風(fēng)險、提高效率,促進組織能力和成熟度提升。
拆分后的運維和監(jiān)控如何處理
監(jiān)控與運維是生產(chǎn)環(huán)境運行系統(tǒng)的日常工作,就像是人體的免疫細(xì)胞一樣,保障著整個系統(tǒng)的健康運行,業(yè)務(wù)的正常運轉(zhuǎn),下面我們從 5 個方面說明一些微服務(wù)下的健監(jiān)控和運維工作要點。
1) 系統(tǒng)監(jiān)控
系統(tǒng)監(jiān)控是最基礎(chǔ)的監(jiān)控指標(biāo),是我們了解系統(tǒng)內(nèi)部運行情況的直接手段。我們要對所有重要的狀態(tài)進行度量和監(jiān)控,全面實時的掌握系統(tǒng)健康狀態(tài)。
2) 業(yè)務(wù)監(jiān)控
業(yè)務(wù)監(jiān)控意味著我們要從用戶的角度看來待系統(tǒng)的監(jiān)控指標(biāo),而不僅僅是技術(shù)角度,這樣我們就能發(fā)現(xiàn)和分析業(yè)務(wù)指標(biāo)的突然變化是什么原因造成的,跟系統(tǒng)本身有沒有關(guān)系,有沒有需要我們改進提升的地方,可以更好的支撐業(yè)務(wù)增長,影響和穩(wěn)定業(yè)務(wù)指標(biāo)。時??赡軙霈F(xiàn),業(yè)務(wù)方說客戶都在抱怨系統(tǒng)不穩(wěn)定,卡了,延遲高了,但是我們從系統(tǒng)監(jiān)控上看,系統(tǒng)的指標(biāo)沒有大的變化,那么一定是我們設(shè)定的監(jiān)控指標(biāo)不夠,沒有覆蓋到業(yè)務(wù)的全流程。反過來,也說我們和業(yè)務(wù)方、和客戶思考問題的角度有差異,為了更好的服務(wù)客戶,我們需要調(diào)整自己的視角,從用戶角度出發(fā)思考問題。
3) 容量規(guī)劃
只了解系統(tǒng)的過去和現(xiàn)狀是不夠的,因為隨時可能會有突發(fā)的流量襲來,導(dǎo)致系統(tǒng)被沖擊,可能會超出系統(tǒng)處理能力導(dǎo)致延遲飆升,直至系統(tǒng)宕機崩潰。所以我們需要在平時做好容量規(guī)劃,通過持續(xù)的壓測,了解到系統(tǒng)的極限處理能力,針對瓶頸資源持續(xù)做優(yōu)化,提升處理能力,做好容量預(yù)案,隨時有激增流量的擴容方案,超出處理能力的限流和熔斷、系統(tǒng)過載保護方案,保障系統(tǒng)的穩(wěn)定運行。
4) 報警預(yù)警
做好了業(yè)務(wù)和系統(tǒng)指標(biāo)監(jiān)控,也做了容量規(guī)劃,那么我們還需要通過這些指標(biāo)和容量策略,在合適的時機對系統(tǒng)進行干預(yù),把系統(tǒng)風(fēng)險提前消滅掉。所以,我們需要根據(jù)經(jīng)驗定義預(yù)警報警的閾值,根據(jù)容量水位進行擴容縮容的后續(xù)處理動作。特別報警預(yù)警的實時性,是我們應(yīng)對線上突發(fā)異常的一個重要指標(biāo),例如對于 web 和 app 用戶,如果我們在系統(tǒng)突發(fā)異常的 10 秒內(nèi)收到預(yù)警,然后又花了 20 秒把系統(tǒng)重啟,恢復(fù)了服務(wù)能力,那么用戶可能會覺得剛才 30 秒是不是網(wǎng)絡(luò)卡了一下,不會產(chǎn)生大規(guī)模的客訴。相反地,假如我們 2 分鐘收到報警,又花了 10 分鐘才處理完,這時候基本上大批客戶都會感知到了這次故障,稱為一個有大量客訴的事故了。
5) 故障處理
從我們的實際經(jīng)驗來看,導(dǎo)致系統(tǒng)出現(xiàn)非預(yù)期的不可用性故障,主要有三類原因:
- 人為的操作失誤導(dǎo)致的宕機類不可用,沒有標(biāo)準(zhǔn)的操作流程或者操作者沒遵守流程;
- 遺漏的功能或性能相關(guān)的 bug 問題引起的不可用,我們的測試覆蓋不足,或者對系統(tǒng)間的影響關(guān)系判斷不準(zhǔn)確,導(dǎo)致 Corner-Case 有遺漏;
- 不可預(yù)知的突發(fā)條件或狀況引起故障的不可用,比如我們使用了 AWS,突然某個時間段 AWS 日本某個可用區(qū)的網(wǎng)絡(luò)突然發(fā)生了大規(guī)模超時,某個 RDS 的底層硬盤突然損壞等;
這三類問題的應(yīng)對策略是分別如下
- 操作失誤的應(yīng)對策略:制定標(biāo)準(zhǔn)操作流程,并根據(jù)實際情況不斷更新和調(diào)整,貫徹培訓(xùn),嚴(yán)格執(zhí)行,用流程來防止人為的不規(guī)范;
- 功能問題的應(yīng)對策略:建立全功能覆蓋的測試 case,并不斷擴充 Corner-Case,逐步實現(xiàn)自動化,跟 CI/CD 集成,每次修改后都能及時的回歸所有已知的 case,不留死角。完成系統(tǒng)和服務(wù)依賴關(guān)系分析,梳理和合理改造影響范圍。建立可跟蹤的性能測試基線標(biāo)準(zhǔn)和環(huán)境,每次重構(gòu)或者設(shè)計調(diào)整,都通過基線環(huán)境進行性能驗證,不把性能問題帶到線上。
- 突發(fā)故障的應(yīng)對策略:突發(fā)問題是我們真正面臨的問題,一般來說不可控,超出預(yù)期,難以通過我們的努力直接解決。
所以,如何在不可靠的基礎(chǔ)上實現(xiàn)應(yīng)對策略,除了上面提到的容錯和面向失敗設(shè)計以外,我的經(jīng)驗就是需要在基礎(chǔ)設(shè)施和業(yè)務(wù)服務(wù)之間,考慮再加一些中間層,特別是使用一些業(yè)內(nèi)知名的成熟中間件,利用它們的主從、多副本、分片等機制,通過軟件的高可用實現(xiàn)對硬件和網(wǎng)絡(luò)底層問題的隔離,進而給上層的業(yè)務(wù)服務(wù)層提供高可用的基座。
簡單說,就是把系統(tǒng)的穩(wěn)定性風(fēng)險,從我們的基礎(chǔ)設(shè)施或者原來在業(yè)務(wù)代碼實現(xiàn)里寫的各種策略,轉(zhuǎn)移到了中間件上。另一方面,需要考慮突發(fā)故障后的系統(tǒng)快速恢復(fù)策略,如果我們能在用戶可容忍的感知時間內(nèi)把系統(tǒng)恢復(fù)到故障前的狀態(tài),則大部分情況下這次突發(fā)故障的影響就會非常小。
所以,系統(tǒng)的啟動時間,數(shù)據(jù)預(yù)熱和配置加載時間,我們需要考慮減低平均故障處理時間(MTTR),縮減到一個可控的很小范圍內(nèi),比如系統(tǒng)在 10 秒內(nèi)啟動,30 秒內(nèi)完成預(yù)熱加載,這樣系統(tǒng)發(fā)生問題時,我們不在線排查問題,迅速無腦重啟即可恢復(fù)業(yè)務(wù)。
故障處理的第一原則是,先解決問題,然后再去考慮分析原因,復(fù)盤過程,總結(jié)經(jīng)驗教訓(xùn),最后才是考慮要不要追責(zé)。特別強調(diào)的是,如果一個線上的發(fā)布或者變更操作,有可能造成客戶的感知事件,最好就先跟客戶進行一個可以預(yù)期造成業(yè)務(wù)影響的溝通,給客戶同步一下操作的時間,目的,持續(xù)時間,可能造成的影響,讓客戶可以從容的安排和調(diào)整自己的業(yè)務(wù),保證不受影響或者降低損失(如果停機會給客戶造成損失的話)。如果技術(shù)團隊對這個操作沒有十足的把握,最好考慮在一個可接受的時間窗口內(nèi)停機處理。對于發(fā)布造成的故障,我們一直有個說法:
如果發(fā)布可能導(dǎo)致宕機這件事是提前告知了客戶,那么真的發(fā)生了宕機就是一個故事。相反,如果可能導(dǎo)致宕機這事兒沒有提前告知客戶,那么操作過程導(dǎo)致宕機,就是一個事故。
最佳實踐的總結(jié)
林林總總說了這么多的微服務(wù)架構(gòu)相關(guān)的知識也好,經(jīng)驗也罷,不一定適合每個希望做微服務(wù)系統(tǒng)的技術(shù)人員的實際需求?!暗罒o常道,法無常法,君子審時度勢,自可得而法”。實際項目里需要做哪些工作,采取哪些策略,先后運用哪些步驟,都需要因地制宜,借鑒各種“他山之石”,綜合考慮。
微服務(wù)架構(gòu)的最佳實踐,其實就是把微服務(wù)架構(gòu)的條條框框都思考一遍,這一條到底解決了什么問題,適用于什么場景,對我現(xiàn)在的工作有沒有幫助,考慮清楚了以后徹底的忘掉這些“有為法”。然后用自己的思考成果去解決工作中遇到的各類問題,就算是真正學(xué)會了微服務(wù)。問題永遠存在,解決掉遇到的問題,是推動技術(shù)發(fā)展,促進組織進步,提升個人技術(shù)能力的不二法門。
過去的幾十年里,技術(shù)發(fā)展的越來越豐富,體系越來越龐大,業(yè)務(wù)系統(tǒng)越來越復(fù)雜,正像是《人月神話》中形容的那樣:
“史前史中,沒有別的場景比巨獸們在焦油坑中垂死掙扎的場面更令人震撼。上帝見證著恐龍、猛犸象、劍齒虎在焦油中掙扎。它們掙扎得越猛烈,焦油糾纏得就越緊,沒有任何猛獸足夠強壯或具有足夠的技巧,能夠掙脫束縛,它們最后都沉到了坑底。”
如何才能逃離“焦油坑”,目前看來使用“微服務(wù)架構(gòu)”的指導(dǎo)思想,去解決復(fù)雜業(yè)務(wù)系統(tǒng)研發(fā)中的各類問題,是一個明確的解決辦法。沒有什么比去解決實際問題、讓系統(tǒng)變好更重要。系統(tǒng)變好了,我們的技術(shù)也變好了,公司的業(yè)務(wù)也支持的更好了,業(yè)務(wù)好了則行業(yè)也發(fā)展的更好了,行業(yè)好了就會讓整個國家和民族都更好了。所以,解決問題是關(guān)鍵。
萬維鋼老師在《精英日課》里說“這個世界上的問題,分為三類”:
第一類是目標(biāo)明確、路徑明確的,比如你上高中的時候,你知道把幾門功課都學(xué)好,就能上個好大學(xué),這就是一個單純問題,但是可以看到,單純問題也可能是非常難的。錢能直接解決的問題,一般也是這類問題。
第二類是兩難問題,選擇就意味著放棄,比如兩個女孩喜歡你,一個有錢,一個漂亮,選了有錢的就不漂亮,選了漂亮的就沒有錢。路徑很明確,但你面臨的痛苦不是因為問題很難,而是不管你怎么處理,都需要付出失去的代價,放棄潛在的另一些可能,會在將來造成實際的損失。你跟小張結(jié)了婚,10 年后天天吵架,后悔沒有取曉麗,紅玫瑰與白玫瑰。
第三類問題,我們叫棘手問題,這類問題,即可能沒有特別明確的目標(biāo),而且也沒有任何人能告訴你一個明確的路徑,你現(xiàn)在做了什么努力,可能也短時間看不到效果,做好了不一定有很大成果,做不好肯定要背鍋。大家可能都知道這個問題在,就是沒有人去解決或者能去解決。最關(guān)鍵是,從外部的一些人看來,這些問題,很像是一個單純問題。這特么就扯淡了。
而現(xiàn)在看來,微服務(wù)架構(gòu)的這些知識和實踐,包括最近幾年大家在響應(yīng)式微服務(wù)架構(gòu)(Reactive MicroServices Architecture)、服務(wù)網(wǎng)格(Service Mesh)做得一些工作,恰恰就是給怎么去處理“復(fù)雜業(yè)務(wù)系統(tǒng)研發(fā)”這個棘手問題,帶來了一個比較明確的路徑,指引著大家逐漸的把這個棘手問題變成一個單純問題,然后用各種新的思想和武器去解決它。
最后給大家分享一個今天看到的微服務(wù)和中臺的段子:
Q:大師大師,微服務(wù)拆多了怎么辦?
A:那就再合起來啊。
Q:那太沒面子了啊。
A:你就說你已經(jīng)跨越了微服務(wù)初級階段,在做中臺了。
(個人微信號:Robynn-D , 歡迎交流)