歡迎光臨
每天分享高質量文章

愛奇藝視頻後臺從”單兵作戰”到”團隊協作”的微服務實踐

系統越做越大,功能越加越多,我們是否有如下經歷:
  • 一次小的需求,評估由此產生的影響成本超過開發需求本身。

  • 系統幾經交接或升級,接口文件丟失或跟代碼嚴重不符。

  • 每天疲於排查線上問題和修複線上資料,沒有精力代碼優化。

  • 由於創建/開發/部署新服務的成本,不斷的將無關的功能添加到臃腫的服務。

  • 線上服務一個功能或者中間件的中斷,導致整個系統不能提供服務。

  • 每次新功能的技術選型總需要迎合現有系統的技術架構做讓步。

本文結合愛奇藝視頻後端開發團隊的微服務實踐,分享推進微服務化落地過程中遇到的問題及思考。分別從以下三個方面展開:


微服務化是什麼?

設計樣式中有單一職責,演算法理論中有分治思想,微服務化的思路大體一樣,將傳統大而全的單體應用,拆分成多個職責清晰、獨立部署的服務。微服務化帶來的好處是直觀的,更快的開發效率,更清晰的系統邊界,更好的擴展性和應對變化的能力;但是,隨之而來是大量重覆性工作和分佈式帶來的新的挑戰。所以微服務化概括起來要做兩件事,一是拆分服務,二是解決拆分服務帶來的重覆性問題和分佈式問題。
怎樣進行微服務化?

這部分介紹在微服務化落地過程中,我們遇到的問題及解決思路。主要從以下三個方面展開:
  • 如何拆分服務,服務拆分時,主要考慮的因素

  • 如何選擇微服務框架,微服務技術選型時,主要考慮的因素。

  • 我們選用的微服務框架/公共組件原理及解決的問題。

如何拆分服務?
微服務的拆分方式,沒有固定的樣式,粒度太粗,微服務化不徹底,粒度太細,開發運維成本高。微服務拆分不是一蹴而就的,而是隨著需求迭代逐漸演化的,微服務的拆分結果應該是系統邊界更清晰,需求迭代更快,開發效率更高,而不是相反,使問題複雜化。概括起來,可以從業務劃分,重要程度,代碼復用三個方面考慮是否要拆分服務。
業務拆分,微服務化的核心標的之一是業務邏輯內聚,系統邊界清晰,便於需求迭代、代碼重構和應對變化。所以,大部分時候,服務拆分基於業務劃分,如果兩個業務模塊拆分後,頻繁交互,相互依賴,接口定義複雜,則不適合拆分。
重要程度,服務重要程度不同,可用性要求,技術架構,保障級別都會不同。例如,同樣是修改視頻的操作,控制視頻上下線的播控服務和記錄視頻歷史修改的操作記錄系統,放到兩個獨立的服務里,會更合適。
代碼復用,對於相同的代碼邏輯,不同於公共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實戰培訓

Kubernetes應用實戰培訓將於2018年10月12日在深圳開課,3天時間帶你系統學習Kubernetes本次培訓包括:容器基礎、Docker基礎、Docker進階、Kubernetes架構及部署、Kubernetes常用物件、Kubernetes網絡、儲存、服務發現、Kubernetes的調度和服務質量保證、監控和日誌、Helm、專案實踐等,點擊下方圖片查看詳情。

赞(0)

分享創造快樂