攜程微服務體系下的服務治理之道和優化實踐
一、背景
微服務架構在中大型互聯網公司中被廣泛應用,隨著業務的發展,應用數越來越多、調用關系也越來越復雜。中臺化后,交易系統要支持業務線多,系統復雜性高,原系統雖然能支撐業務量的持續增長,但在穩定性、吞吐力和資源利用率上面,還存在優化空間。
分享的目的
本文站在業務開發角度介紹開發在微服務架構下遇到的相關問題(微服務架構的優缺點這里不再贅述),以門票活動預訂流程查詢引擎為例,分享微服務治理的實戰經驗,希望能給遇到同樣問題的同學提供一些借鑒思路。
如下圖所示,藍色部分為本文的重點
圖1 微服務架構關注點
在微服務治理之前,我們先簡單了解一下微服務歷史和陷阱。
二、微服務簡史
微服務概念在2005年被提出,2011年用來代表架構風格。
定義:微服務是一種架構風格,一個大型復雜軟件應用由一個或多個微服務組成,每個微服務僅關注于完成一件任務。
2.1 微服務與SOA的關系
大家在使用SOA (Service-Oriented Architecture)的時候往往分不清和微服務有什么關系,總結如下:
- 微服務是SOA的實現方式
- 微服務是去掉ESB后的SOA
- 微服務是一種和SOA相似但是兩種不同的架構設計風格
?
?
?
圖2 微服務與SOA的關系
了解微服務與SOA關系后,再對比一下功能差異:
表1 微服務與SOA對比
2.2 攜程SOA從1.0到 2.0演進
圖3 攜程SOA演進
攜程微服務:攜程SOA2.0是微服務架構,推薦單機、單應用、單服務。
三、微服務的陷阱
微服務這個話術會將關注點錯誤的聚焦在“微”上,大家會誤以為服務越小越好,實際上大小并不是第一考慮因素。接下來我們來看看開發微服務應用的時候容易踩到的陷阱。
下圖可以看出,服務拆分越細,調用關系越復雜。
調用鏈路理論上有 n * (n-1) 條:
圖4 服務粒度越細調用關系越復雜
應用粒度拆分過細容易帶來以下幾個問題:
3.1 重復調用
調用路徑 C - >D ->E 和 C ->E, 對于E的一次請求,可能會被調用了多次。
圖5 一次請求中服務E被重復調用
3.2 循環依賴
一條鏈路出問題,導致其他鏈路故障。當服務B1或B2 性能變差時,最終導致鏈路A/B都會被影響,嚴重情況下導致宕機。
圖6 循環依賴
3.3 鏈路太長
服務層級過深,一次請求鏈路太長會導致性能下降,每層網絡延時和序列化反序列化時間都有性能損失,層級越深,下游性能越差。
鏈路太長,定位問題困難(效率低),當服務F出現故障時,下游A~E 應用 owner 需要排查原因。
圖7 鏈路越長,性能損失越大
以上這些問題,在日常開發中容易遇到,下面我們看看怎么解決這些問題。
四、微服務治理
從下圖中可以看到應用之間調用關系復雜,并且有嚴重的循環依賴問題。
圖8 應用調用關系圖(雙黃線表示循環依賴)
循環依賴是微服務里面容易忽視的問題,系統穩定的情況下不會出現問題,由于某些原因,當系統從穩定變成非穩定狀態時,循環依賴容易導致更嚴重的故障。我們先看1個生產案例:
案例:發布過程中下游超時,訂單下跌?
剛接入流量的機器因線程初始化、類加載鎖、JIT等會產生慢請求。
圖9 發布過程中的慢請求
當流量接入時,請求在剛拉入的機器中多次來回調用,因多次慢請求疊加,導致接口越來越慢,機器資源耗盡,一臺一臺被拖垮,最終整個服務不可用,產生雪崩(如下圖)。
圖10 發布過程中循環依賴導致應用雪崩
當然如果應用間循環依賴QPS很小,例如單機QPS在10以內,少量慢請求無法將資源耗盡,一般不導致故障,但是這種“壞味道”會給系統埋下隱患,嚴重的時候會演變為接口級的循環依賴,導致死循環,并且這種死循環可能在測試環境由于命中緩存沒有被發現,發布到生產后有些緩存穿透的請求就會導致循環調用,直到超時;如果單機QPS上百,產生的慢請求短時間內耗盡資源,阻塞后續請求,導致性能下降,產生故障。
故障恢復期間,由于調用關系復雜,分不清上下游關系,無法根據調用關系來限流,導致定位困難,恢復時間長。
上述案例主要是由循環依賴引起,像一顆炸彈,為系統埋下隱患。
除了循環依賴,還有下面幾類問題可以優化:
1)層級太深:?
- 透傳字段要改多個應用,需求迭代效率低
- 每層網絡延時、序列化和反序列化都有性能損失,導致終端體驗差
2)重復緩存:同一個DB不同應用重復構建緩存
3)流量大:?
- 重復調用,直接調用或者間接調用,末尾服務壓力大
- 離線任務峰值波動太大
4)未隔離:?核心、非核心流量未隔離
5)效能低:人均應用多/資源使用率低
針對上面的幾類問題,我們制定了微服務治理目標、原則和治理策略。
4.1 治理目標
1)穩定:故障隔離,提升系統穩定性
2)交付:獨立迭代、獨立擴展、快速交付
橫向拆分:減少耦合,獨立迭代。
縱向拆分:減少應用層級,提高開發效率,縮短交付周期。
3)重用:相同功能復用
不同系統重復功能復用,減少重復開發,提升一致性。
4.2 治理原則
1)避免跨團隊維護一套代碼。
2)服務粒度要與團隊規模匹配,人均應用數在3個以內。
根據歷史經驗,一個人在超過3個應用之間來回切換開發,開發效率會降低,日常處理告警繁瑣,業務和性能優化也無法聚焦。
3)應用分層:上一層可以依賴任意下一層級(不可反向依賴)。
4)層級深度:垂直域/小組內,應用層級控制在5層以內。
這里的“5層”是我們根據實際業務實際情況來定的。一個垂直域/小組內應用層級超過5層,一個需求上下游依賴太多,開發效率會降低。
4.3 構建原則
1)業務領域拆分:單一職責,業務建模(對人員要求高)
2)數據存儲:獨立的數據讀寫API
3)復用性:功能復用(比如基礎數據提供能力,提供給不同小組使用)
- 可靠性
- 核心與非核心隔離
??4)穩定規則與易變動規則隔離
5)快速失敗:設置合理的熔斷規則
6)異步通信:將與此次請求無關的操作/調用異步化
4.4 治理策略
1)去除循環依賴?
問題:服務B和服務C 循環依賴
策略?
- 應用分層與定位:第一步劃分應用層級(分層工具有傳統三層架構、泛領域分層等),將應用定位劃分到不同的層級。
- 確認依賴關系:每一層內如果有多個應用,確認上下游關系。這個根據業務場景來,根據父子關系,包含關系,依賴關系,確認每一層內的依賴關系和應用職責。
?
圖11 循環依賴治理
2)縮短調用鏈路?
問題:服務BCD 鏈路太長(垂直域/小組內)
策略?
- 領域細分:將粗粒度的應用按照業務領域垂直劃分,不同層級負責不同的職責,讓系統更獨立。
- 減少透傳:每個層級職責清晰,減少不必要的透傳,讓開發效率更高。?
圖12 縮短調用鏈路
3)復用性
問題:服務BCD 對相同數據重復緩存(存在一致性問題)
策略?
下沉基礎服務,提供基礎數據:將相同的功能下沉為基礎服務,例如:基礎數據服務提供緩存,翻譯等功能。避免不同的使用方重復緩存,重復接入翻譯。
圖13 重復功能下沉
效果:?下沉基礎數據服務,統一緩存,翻譯等功能,提供給不同的開發組使用。?
4)流量治理?
a) 重復調用?
問題:一次請求,服務C同一個接口被重復調用
策略?
功能內聚:將同一個功能對下游的依賴放到同一個服務內調用。由于系統自身迭代導致的不合理調用,可以按照上述方法優化。如果為了解耦將功能拆開,可以根據實際情況評估影響和收益。
圖14 功能內聚合并重復調用
效果:功能內聚,多次調用合并為一次調用。
b) 降低調用量?
問題:一個服務中,不同的接口功能拆分太細,下游使用的時候都需要調用多個接口組裝結果。例如:一次請求服務B的a、b、c、d、e接口都被調用,下游為實現一個功能,需要調用太多小接口。
策略?
- 合并服務B中同一領域功能:將相同的功能合并到一個接口,減少調用量。
- 一個接口提供模塊參數,按需調用:
- 支持按需使用,對不同業務場景非必須的功能,提供模塊參數,按需傳參。
- 對于獨立的前端頁面接口,對外透明,內部封裝對應場景需要的模塊參數,例如前端首屏請求。
圖15 請求合并
效果:聚合相同功能,合并小接口,多次調用合并為一次調用。
c) 流量隔離?
問題:非核心流量(例如:Job調度)大于用戶流量
策略?
流量隔離:一套代碼,隔離部署,將核心和非核心流量隔離。核心流量承載用戶請求,保證交易的穩定性,非核心流量承載離線任務調度和非核心場景調用。
圖16 流量隔離
效果:總成本不變,核心鏈路穩定性得到提升,非核心鏈路CPU使用率得到提升。
d) 離線調度流量消峰?
問題:單位時間內調度過于集中(Job)
策略?
合理的延長調度時間:適當延遲調度時間,降低每分鐘的調用峰值,讓每分鐘內調用量更加平穩。
圖17 離線調度流量消峰
效果:調度總時間在可接受范圍內,調度時間拉長,單位時間內調用總量降低,降低服務端峰值壓力。
問題:每秒內調度不均衡(Job),導致服務穩定性差或為了能承載請求需要冗余更多服務器資源。
圖18 客戶端調度QPS不均衡
策略?
客戶端削峰填谷:調度波動太大,會導致請求到了服務端被限流或者服務端擴縮容。對于調度不均衡的離線任務,我們在客戶端控制每秒內發送的請求量,讓每秒內請求更加平穩,任務調度總時間不變。
圖19 客戶端調度從不均衡變為均衡
效果:分鐘內總的調用量不變,服務端調用量從波動變為平穩。
5)降低人均應用數/提升CPU使用率?
問題?
- 人均應用過多,開發效率降低
- CPU使用率6%以下應用數占比超過50% 且總核數占比超過30%
?
策略?
- 短期:縮容,將單邊服務器數縮容到SRE標準最小配置。
- 長期:合并拆分過細的應用,參考歷史、現狀和將來的規劃,將拆分過細、CPU使用率長期小于6%的應用做合并。
圖20 應用合并
五、實施效果
1)循環依賴(應用分層,解除應用間循環依賴)
- 去掉65條循環依賴鏈路,消除雪崩的風險
- 超時類告警降低99%
- 排障效率提升至分鐘級別
?2)鏈路長(減少應用層級):調用鏈深度縮短 40%
3)復用性(下沉基礎數據服務,減少重復功能)
- 新增基礎數據服務,緩存統一,解決一致性問題
- 緩存容量減少60%
?4)流量治理(降低水位線)
- 重復調用:功能內聚,去除重復調用
- 調用量大:合并小接口、消除調用峰值;離線任務削峰填谷,降低峰值調用量
- 核心應用調用量減少73%,核心系統峰值降低50%
5)開發效率(解耦&減少中間層)
- 水平拆分獨立功能,減少耦合,獨立開發
- 垂直領域減少3層,開發效率提升
?6)查詢引擎性能提升65%,QPS從8w提升至24w
- 減少了系統不穩定導致的服務變慢
- 領域劃分,垂直優化系統,專注用戶端到底層的優化
?7)人均應用:人均應用數控制在2個以內
8)資源使用率(應用合并,提升CPU使用率)
- 40+個應用CPU使用率(加權平均)從18%提升至32%
- 治理前后查詢引擎鏈路對比:
圖21 門票活動查詢引擎微服務治理前后對比
六、總結
微服務架構下服務拆分越細,調用關系越復雜,層級越深,性能損耗越大,開發效率越低(垂直域/小組內),所以服務不是越小越好,而是“合適的大小”。
在構建微服務的時候,要根據業務體量、團隊規模、成本等因素綜合考慮,按照合理的原則,構建出適合的大小,以達到預期的目標。
服務治理是一個長期的過程,制定目標持續優化,讓系統更快更穩定,為業務賦能。