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