-
一次小的需求,評估由此產生的影響成本超過開發需求本身。
-
系統幾經交接或升級,介面檔案丟失或跟程式碼嚴重不符。
-
每天疲於排查線上問題和修複線上資料,沒有精力程式碼最佳化。
-
由於建立/開發/部署新服務的成本,不斷的將無關的功能新增到臃腫的服務。
-
線上服務一個功能或者中介軟體的中斷,導致整個系統不能提供服務。
-
每次新功能的技術選型總需要迎合現有系統的技術架構做讓步。
本文結合愛奇藝影片後端開發團隊的微服務實踐,分享推進微服務化落地過程中遇到的問題及思考。分別從以下三個方面展開:
設計樣式中有單一職責,演算法理論中有分治思想,微服務化的思路大體一樣,將傳統大而全的單體應用,拆分成多個職責清晰、獨立部署的服務。微服務化帶來的好處是直觀的,更快的開發效率,更清晰的系統邊界,更好的擴充套件性和應對變化的能力;但是,隨之而來是大量重覆性工作和分散式帶來的新的挑戰。所以微服務化概括起來要做兩件事,一是拆分服務,二是解決拆分服務帶來的重覆性問題和分散式問題。
這部分介紹在微服務化落地過程中,我們遇到的問題及解決思路。主要從以下三個方面展開:
微服務的拆分方式,沒有固定的樣式,粒度太粗,微服務化不徹底,粒度太細,開發運維成本高。微服務拆分不是一蹴而就的,而是隨著需求迭代逐漸演化的,微服務的拆分結果應該是系統邊界更清晰,需求迭代更快,開發效率更高,而不是相反,使問題複雜化。概括起來,可以從業務劃分,重要程度,程式碼復用三個方面考慮是否要拆分服務。
業務拆分,微服務化的核心標的之一是業務邏輯內聚,系統邊界清晰,便於需求迭代、程式碼重構和應對變化。所以,大部分時候,服務拆分基於業務劃分,如果兩個業務模組拆分後,頻繁互動,相互依賴,介面定義複雜,則不適合拆分。
重要程度,服務重要程度不同,可用性要求,技術架構,保障級別都會不同。例如,同樣是修改影片的操作,控制影片上下線的播控服務和記錄影片歷史修改的操作記錄系統,放到兩個獨立的服務裡,會更合適。
程式碼復用,對於相同的程式碼邏輯,不同於公共JAR包依賴,微服務是以獨立部署的方式實現程式碼復用,當公共邏輯發生改變時,只需要升級一個服務而不是所有依賴該JAR包的服務。例如,統一閘道器服務,作為所有服務的訪問入口,為微服務體系內所有服務提供統一鑒權和過濾邏輯。
拆分服務的直接副作用是服務個數變多,開發運維成本線性增加。另外,由單體應用內方法呼叫變為不同服務間跨網路訪問,給微服務化帶來了新的挑戰,比如,資料一致性保證,跨系統問題定位等。為此,我們需要引入一系列的框架、元件來管理服務,簡化開發,減少運維。實際技術選型過程中,我們主要考慮了以下幾個因素。
歷史包袱,微服務化不可迴避的問題是,遺留系統如何微服務化。鑒於我們大部分系統都是Spring MVC專案或者Spring Boot專案,所以,我們選用Spring Cloud作為微服務框架,對於遺留系統接入成本和開發人員學習成本都很低。另外,對於少量老的RPC服務,我們透過代理服務統一封裝,將其納入微服務體系內,降低服務呼叫成本。
核心訴求,有關微服務訪問協議,以Spring Cloud為代表的HTTP和以gRPC為代表的二進位制協議各有利弊,HTTP規範通用,使用成本低;二進位制協議效能更高,節省頻寬。我們結合自身的業務特點,相比對效能的嚴苛要求,更看重較高的開發效率和更快的需求迭代,所以,選擇更適合我們的Spring Cloud。
框架成熟度,Spring Cloud在業界也有很多成功企業案例,檔案豐富,社群活躍,而且提供了包括服務註冊與發現,負載均衡,宣告式介面呼叫,服務熔斷,鏈路跟蹤等元件的微服務化完整解決方案。相比之下,近兩年新興起的基於服務網格的微服務框架值得期待,但在成功落地案例和生態成熟度方面,還稍顯不足。
使用成本,技術選型另一個要考慮的因素是使用成本,包括使用新技術的學習成本,獨立部署服務的運維成本等。我們推進微服務化落地同時,公司服務雲的同學提供了大量公共服務元件。其中包括基於Zipkin的鏈路跟蹤系統Rover,基於Flume+ES+Kibana的日誌收集系統Venus,基於攜程Apollo的分散式配置中心等。為此,我們透過整合他們(而不是重覆造輪子)解決微服務化過程中的部分公共問題。
所以,我們最終選用的微服務架構以Spring Cloud為基礎,集成了服務雲包括配置中心,日誌收集,鏈路跟蹤,奇眼Metrics監控等在內的公共元件,最終部署在公司QAE(iQIYI App Engine,基於Docker的應用引擎)上。另外,為了提升開發效率和解決分散式系統的常見問題,我們還提供了一些常用元件的實現,具體包括分散式鎖,分散式限速,分散式排程等。
下麵簡單介紹我們微服務框架中的核心元件原理以及解決的問題。
註冊中心&負載均衡,服務提供方在註冊中心動態註冊和登出服務,服務呼叫方從註冊中心發現服務提供方實體串列,並透過負載均衡策略,從中選擇一個實體進行訪問。我們使用Eureka實現服務註冊和發現,使用Ribbon提供客戶端負載均衡和重試。透過選用區域感知的負載均衡策略,實現同機房優先訪問的跨機房高可用部署方案。
分散式配置中心,微服務透過接入公司配置中心實現配置的集中管理和動態掃清。集中管理一方面可以實現同一服務內,不同實體間的配置共享,另一方面,可以實現不同服務間公共配置的統一管理,比如註冊中心的訪問地址,框架相關的預設配置等。動態掃清實現服務不重啟的情況下,修改配置後,配置立即生效,比如根據實時流量,動態調整分散式限速和Hystrix執行緒池引數等。
統一閘道器服務,閘道器服務作為整個微服務體系的統一入口,提供動態路由配置,資源訪問控制和介面級限速。所有註冊到註冊中心的服務,都可以透過閘道器,提供對外一致的服務,體系內服務的升級和重構,對服務呼叫方透明。
介面檔案生成,為簡化服務提供方編寫介面檔案的工作,支援需求快速迭代和擁抱變化,我們透過整合Swagger用於介面檔案自動生成。開發人員只需定義介面和介面物件,就可以自動生成介面檔案並可以直接發起測試,同時支援透過新增註解進行引數校驗。
宣告式介面呼叫,微服務化的副作用之一是服務呼叫更加頻繁,為簡化服務呼叫,我們使用Feign支援宣告式介面呼叫。服務呼叫方只需宣告本地介面或者直接取用遠端服務定義的介面,無需編寫實現,就可以像呼叫本地方法一樣,呼叫遠端服務。
服務熔斷,微服務使用Hystrix實現鏈路熔斷和降級服務,使用Hystrix dashboard實時監控Hystrix監控項。所有對外發起呼叫的地方,都使用HystrixCommand進行包裝,防止因為單個服務故障導致其他服務級聯故障。我們透過自動內建或使用註解的方式,降低使用Hystrix的門檻。
容器化部署,容器化是微服務的最佳載體,也是雲原生應用的標配。我們使用Spring boot開發微服務,並部署到私有雲QAE容器中,簡化服務開發部署成本的同時,支援橫向彈性擴容。公共元件選型時,我們透過選擇雲原生元件(比如Prometheus)或者簡單改造適配(比如XXL-JOB),不破壞整個服務的雲原生特性。
持續整合/部署,微服務透過對接公司持續整合工具QCI支援一鍵構建和部署。提交程式碼到GitLab後,自動觸發構建打包,並上傳至QAE應用,節省持續整合時間。
日誌收集,日誌是我們排查故障和檢查程式執行狀態的最主要的手段。我們使用統一的日誌工具類格式化日誌輸出,無縫對接Venus日誌收集,並將日誌引流到專有ES叢集,最終在Kibana集中展示和統計分析。
鏈路跟蹤,鏈路跟蹤用於快速定位跨系統呼叫問題。我們透過整合Spring Cloud Sleuth和公司鏈路追蹤系統Rover,實現跨系統鏈路跟蹤。對於體系內最常用的2種互動方式,Http同步呼叫和rocketmq非同步訊息,自動內建鏈路跟蹤功能。
Metrics監控,Mettics監控用於瞭解服務執行情況和線上流量分佈,可以發現系統潛在問題,併為後續需求迭代和業務決策提供參考。我們引入奇眼/Kibana用作基於日誌的Metrics監控統計,同時基於Prometheus實時監控報警也在落地實踐中。
健康檢查&報警,Metrics監控是基於日誌的,如果應用本身有問題,沒有產生日誌或日誌收集本身有問題(斷流或延遲),基於日誌的監控、統計、報警都會失效。為此,我們針對使用服務雲提供的奇眼指標探測,定時檢查服務health端點,服務不可用時,第一時間報警通知。
分散式一致性,服務拆分帶來的分散式事務複雜性是微服務化最大副作用之一。業界有很多解決方案,比如2PC,TCC,訊息事務等,我們結合業務特點,選用基於訊息的最終一致性方案,簡單有效,只需各個事務參與方保證業務冪等。
分散式限速&分散式鎖&分散式排程,我們開發了基於Redis的分散式限速元件,用於在服務入口和資源受限的場景保護我們的系統。基於ZooKeeper的分散式鎖,用於解決分散式場景下相同資源的訪問衝突題。引入XXL-JOB,用於解決分佈系統中的定時排程問題。
微服務化的標的之一是提升效率和降低成本。微服務化過程中,不同的服務,業務上雖然是相互獨立的,但是具有很多相同的橫切性關註點。為此我們在微服務的全生命週期的各個階段,引入多個公共元件來解決這些共性問題。比如,建立服務時,我們使用腳手架,一鍵生成專案原型;開發服務時,大量使用Spring Boot的自動配置和起步依賴簡化開發;提供服務時,使用Swagger自動生成介面檔案;呼叫服務時,使用Feign的宣告式介面呼叫……
分散式系統中,服務呼叫是最常見的操作。因為兩方面的原因,服務呼叫失敗的情況在所難免:一是當服務生產者狀態由可用變為不可用,由於各種原因,服務消費者並不能立刻感知到;二是由於各種原因,例如網路抖動,JVM GC,資源受限等導致的訪問超時。也就是說,我們不可能保證服務呼叫百分百成功。服務呼叫失敗後,簡單有效的補償方案是,客戶端增加重試。另一方面,服務呼叫方訪問超時,服務提供方處理未必是失敗的,為了避免生產者多次處理同一請求產生錯誤資料,服務提供方必須要做到業務冪等。
我們在進行系統設計和編碼時,必須意識到,任何資源都是有限的。比如資料庫連線數量,執行緒數量,介面QPS限速,如果存在多個使用方共享資源的情況,就會出現一個使用方耗盡資源導致其他使用方無資源可用。對於常規的資源隔離,業界有好多最佳實踐,比如Hystrix隔離,執行緒池,連線池使用等,對於系統中使用的其他資源,為避免因為共用資源而相互影響,最好也使用獨立的資源。比如大到獨立的儲存,中介軟體,小到獨立的佇列等。
微服務化的團隊中,比較直接的職責劃分方式是,按照服務進行劃分,每個人對服務的全生命週期負責,從服務構建,開發測試,打包部署,到運維監控。開發人員編碼階段就應該為後期運維監控做必要的日誌埋點,系統上線後,開發人員也應該關註線上執行情況和資料分佈,並以此作為後期系統最佳化和需求迭代參考,促進DevOps形成閉環。
隨著微服務化推進,每個人可能負責幾個甚至更多微服務,因為各種原因,單次服務呼叫失敗,甚至短時間內個別服務不可用不可避免,我們不應該每天疲於修複由於服務不可用而出現的資料不一致。提高服務的可用性以及實現故障恢復後系統自修複,總是值得的。為此,我們從儲存到中介軟體,從提供服務到呼叫服務,從資源隔離到跨機房部署,多個維度進行了高可用方案選型和設計。
微服務化過程,是服務拆分和消除服務拆分副作用的過程,為此我們引入大量公共服務和元件,用於解決分散式系統共性問題。服務拆分是隨著業務發展逐步進行的,微服務框架是根據實際需要逐步演化的,公共元件也需要持續完善補充進來。但是,無論怎樣變化,提高開發效率,提高系統可用性,減少運維成本的原則不會變。後續我們會對微服務相關框架、技術、方法論(比如服務網格,雲原生,領域驅動設計等)保持關註,適時引入新的技術元件解決實際問題,進一步形成DevOps完整閉環。
Kubernetes應用實戰培訓將於2018年10月12日在深圳開課,3天時間帶你係統學習Kubernetes。本次培訓包括:容器基礎、Docker基礎、Docker進階、Kubernetes架構及部署、Kubernetes常用物件、Kubernetes網路、儲存、服務發現、Kubernetes的排程和服務質量保證、監控和日誌、Helm、專案實踐等,點選下方圖片檢視詳情。
長按二維碼向我轉賬
受蘋果公司新規定影響,微信 iOS 版的贊賞功能被關閉,可透過二維碼轉賬支援公眾號。
微信掃一掃
使用小程式