1. 程式人生 > >打造一個企業級應用的微服務開發框架(上)---從服務註冊中心到服務管理中心

打造一個企業級應用的微服務開發框架(上)---從服務註冊中心到服務管理中心

【摘要】近年來越來越多的企業開始實踐微服務,本文分為上下兩篇介紹微服務框架ServiceComb如何幫助企業應用進行微服務化,實現快速交付,並可靠地執行在雲端。上篇介紹ServiceComb的服務管理中心設計。
近年來越來越多的企業開始實踐微服務,而微服務在企業應用落地的過程,面臨著微服務開發框架的選型,無論是自研還是選擇第三方框架都不得不考慮的問題包括:微服務框架是否具備高可靠性,任何時間不能中斷業務;微服務框架是否能夠實現高速通訊效能,保證業務從單體架構向微服務架構切換時,效能下降不會太多。本文從服務管理中心、通訊處理兩個模組來介紹華為開源微服務框架SeviceComb如何幫助企業應用快速具備高效能的通訊能力以及高可靠的服務管理能力。上篇先介紹微服務的服務管理中心。從服務註冊中心到服務管理中心
1     整體介紹圖1 ServiceCenter整體介紹ServiceCenter是一個具有微服務例項註冊/發現能力的微服務元件,提供一套標準的RESTful API對微服務元資料進行管理。ServiceComb的微服務註冊及動態發現能力也是依賴其實現的。除了以上描述的微服務動態發現外,ServiceCenter幫助應用具備以下能力:
  • 支援微服務例項隔離管理
  • 支援微服務黑白名單管理
  • 支援長連線**微服務例項狀態變化
  • 支援OpenAPI規範微服務契約管理
  • 支援微服務依賴關係管理
  • 提供Web portal展示微服務管理介面
  • 高可用的故障處理模型(自我保護機制)
  • 高效能介面和快取資料設計
2     管理的需要作為微服務系統中非常重要的元件。當前可選來做微服務註冊中心的元件很多,例如Zookeeper、Consul、Eureka、ETCD。然而這些服務註冊中心元件的重點在於解決服務的註冊和發現,也就是一個動態路由的問題。而在一個大型的微服務架構的系統中,除了這些動態路由資訊外,還有一些微服務自身的元資料同樣需要進行管理,所以我們定義出微服務靜態元資料。通過ServiceCenter提供的介面可以很方便的檢索微服務資訊。除了微服務本身資訊屬於靜態元資料外,ServiceCenter還擴充套件了微服務依賴關係、黑白名單、Tag標籤和契約資訊等元資料,他們之間的關係如下圖所示:
圖2 微服務相關靜態資訊之間的關係這些微服務元資料不僅把例項資訊分組管理起來,同時也滿足了使用者對微服務管理的需要。比如說微服務的黑白名單,業界的服務註冊中心大多僅實現了consumer服務依賴的服務例項發現,但沒法對一些安全要求高的服務設定白名單訪問控制,ServiceCenter支援給這類provider服務設定黑白名單過濾規則,防止惡意服務發現並DDoS攻擊。3     分散式系統的高可用性保障微服務及支撐它執行所需要的系統,從廣義上來看,是一個典型的分散式系統。而分散式系統就一定不能逃脫CAP魔咒:Consistency(一致性), 在分散式系統的各點同時保持資料的一致。 Availability(可用性
), 每個請求都能接受到一個響應,無論響應成功或失敗。Partition tolerance(分割槽容錯性),當出現網路分割槽故障時系統的容錯能力。CA是每個系統都在努力追求的目標,但是P又是不能完全避免的。想同時滿足三者是不可能的任務。此後應運而生的BASE理論便成了現代化,特別是網際網路分散式系統設計的準則:BASE理論,Basically Available(基本可用)、Soft state(軟狀態)和Eventually consistent(最終一致性);ServcieCenter本身作為實現微服務架構中服務註冊發現的元件,對整體微服務系統起到至關重要的作用,並且其本身也是一個分散式的系統。因此後面我們通過對一個微服務系統中常見故障模式的剖析來介紹在CSE中對BASE的實現。
3.1    常見的故障模式常見的故障模式         這裡列出了ServiceComb架構中常見的故障以及對應的自保護機制,由於後端儲存使用ETCD,本身也有一定的故障恢復能力,具體可以訪問官網瞭解;現在簡單瞭解下ServiceComb SDK(簡稱:SDK)和ServiceCenter是如何實現高可用的。         SDK1.        與ServiceCenter網路分割槽:SDK提供例項快取機制,保證業務繼續可以用。2.        業務例項不可達:SDK提供客戶端負載均衡能力,快速將請求路由到可用的業務例項,同時也支援提供一系列的容錯策略,控制流量繼續流向異常的例項。         ServiceCenter1.        程序異常:ServiceCenter提供**127.0.0.1的本地HTTP健康檢查介面/health,當監控指令碼呼叫該介面時,ServiceCenter內部會進行etcd可用性檢查、當前管理資源容量超限檢查和平均延時等檢查。當以上某個指標發生異常時,介面將返回錯誤,便於監控系統及時感知。2.        環境約束:一般有CPU負載高、記憶體不足和磁碟空間不足等因素,ServiceCenter針對環境不穩定或資源不足的場景,除了自動觸發自保護機制以外,還有提供一些可定製策略儘量保證自身可服務在這類環境中。如:針對CPU負載高的環境,ServiceCenter支援配置請求讀寫超時,最大限度保證請求可以正常處理完畢,針對磁碟空間資源不夠場景,ServiceCenter自身提供定時日誌壓縮轉儲能力,儘量減少磁碟空間佔用。3.        併發操作:針對併發向ServiceCenter發寫請求場景,需要保證資料一致。為此ServiceCenter利用了etcd本身提供資料強一致性能力,保證每一次寫操作是原子操作;對於複雜的分散式業務場景,ServiceCenter內部還提供了分散式鎖元件,防止分散式併發寫資料不一致的問題。4.        網路分割槽:微服務依賴常見問題,一般是依賴的服務不可用和相互之間網路故障問題。針對前者,鬆耦合設計,ServiceComb SDK(簡稱:SDK)和ServiceCenter均有提供快取機制,保證執行態下,SDK到ServiceCenter、ServiceCenter到etcd各段間互不干擾;針對後者,ServiceCenter除了提供常見的客戶端重試外,還提供非同步心跳和自我保護機制。可見ServiceComb從設計上已經考慮了高可用,下面分別從SDK到ServiceCenter和ServiceCenter到etcd兩個層面來說明ServiceComb如何實現的。3.2    保護機制3.2.1   SDKServiceCenter1.        例項快取機制基於SDK開發的微服務,會在第一次消費Provider微服務時,會進行一次例項發現操作,此時內部會請求ServiceCenter拉取Provider當前存活的例項集合,並儲存到記憶體快取當中,後續消費請求就依據該快取例項集合,按照自定義的路由邏輯傳送到Provider的一個例項服務中。這樣處理的好處是,已經執行態的SDK程序,始終保留一份例項快取;雖然暫時無法感知例項變化及時重新整理快取,但當重新連上ServiceCenter後會觸發一快取重新整理,保證例項快取是最終有效的;在此過程中SDK保證了業務始終可用。                          圖4 微服務例項快取機制2.        心跳保活機制ServiceCenter的微服務例項設計上是存在老化時間的,SDK通過程序上報例項心跳的方式保活,當Provider端與ServiceCenter之間心跳上報無法保持時,例項自動老化,此時ServiceCenter會通知所有Consumer例項下線通知,這樣Consumer重新整理本地快取,後續請求就不會請求到該例項,也就微服務的實現動態發現能力。這樣做的好處是,解決了業務某個例項程序下線或異常時,依賴其業務的微服務例項,會在可容忍時間內,切換呼叫的目標例項,相關業務依然可用。                       圖5 微服務例項心跳保活機制3.2.2   ServiceCenteretcd1.        非同步快取機制在ServiceCenter內部,因為本身不儲存資料,如果設計上單純的僅作為一個Proxy服務轉發外部請求到etcd,這樣的設計可以說是不可靠的,原因是因為一旦後端服務出現故障或網路訪問故障,必將導致ServiceCenter服務不可用,從而引起客戶端例項資訊無法正確拉取和重新整理的問題。所以在設計之初,ServiceCenter引入了快取機制。非同步快取機制1)        啟動之初,ServiceCenter會與etcd建立長連線(watch),並實時**資源的變化。2)        每次watch前,為防止建立連線時間窗內發生資源變化,ServiceCenter無法**到這些事件,會進行一次全量list查詢資源操作。3)        執行過程中,List & watch所得到的資源變化會與本地快取比對,並重新整理本地快取。4)        微服務的例項發現或靜態資料查詢均使用本地快取優先的機制。非同步重新整理快取機制,可以讓ServiceCenter與etcd的快取同步是非同步的,微服務與ServiceCenter間的讀請求,基本上是不會因為etcd不可用而阻塞的;雖然在資源重新整理到ServiceCenter watch到事件這段時間內,會存在一定的對外呈現資源資料更新延時,但這是可容忍範圍內的,且最終呈現資料一致;這樣的設計即大大提升了ServiceCenter的吞吐量,同時保證了自身高可用。2.        非同步心跳機制中心化設計,另一個難題是,效能問題,基於心跳保活機制,隨著例項數量增加,ServiceCenter處理心跳的壓力就會越大;要優化心跳處理流程,首先了解下ServiceCenter怎麼實現例項心跳的。例項心跳,主要是為了讓ServiceCenter延長例項的下線時間,在ServiceCenter內部,利用了etcd的KeyValue的lease機制來計算例項是否過期。lease機制的運作方式是,需保證在定義的TTL時間內,傳送keepalive請求進行保持儲存的資料不被etcd刪除。基於這個機制,ServiceCenter會給每個例項資料設定下線時間,外部通過呼叫心跳接口出發ServiceCenter對etcd的keepalive操作,保持例項不下線。可以知道,這樣設計會導致上報心跳方因etcd延時大而連線阻塞,上報頻率越高,ServiceCenter等待處理連線佇列越長,最終導致ServiceCenter拒絕服務。圖7 非同步心跳機制在這裡ServiceCenter做了一點優化,非同步心跳機制。ServiceCenter會根據上報例項資訊中的心跳頻率和可容忍失敗次數來推算出最終例項下線時間,比如:某例項定義了30s一次心跳頻率,最大可容忍失敗是3次,在ServiceCenter側會將例項定義為120s後下線。另外,在例項上報第一次心跳之後,ServiceCenter會馬上產生一個長度為1的非同步佇列,進行請求etcd,後續上報的心跳請求都會在放進佇列後馬上返回,返回結果是最近一次請求etcd重新整理lease的結果,而如果佇列已有在執行的etcd請求,新加入的請求將會丟棄。這樣處理的好處是持續的例項心跳到ServiceCenter不會因etcd延時而阻塞,使得ServiceCenter可處理心跳的能力大幅度的提高;雖然在延時範圍內返回的心跳結果不是最新,但最終會更新到一致的結果。3.        自我保護機制前面提到的快取機制,保證了ServiceCenter在etcd出現網路分割槽故障時依然保持可讀狀態,ServiceCenter的自我保護(Self-preservation)機制保證了Provider端與ServiceCenter在出現網路分割槽故障時依然保持業務可用。現在可以假設這樣的場景:全網大部分的Provider與ServiceCenter之間網路由於某種原因出現分割槽,Provider心跳無法成功上報心跳。這樣的情況下,在ServiceCenter中會出現大量的Provider例項資訊老化下線訊息,ServiceCenter將Provider例項下線事件推送到全網大部分的Consumer端,最終導致一個結果,使用者業務癱瘓。可想而知對於ServiceCenter乃至於整個微服務框架是災難性的。為了解決這一問題,ServiceCenter需要有一個自我保護機制(Self-preservation):8  ServiceCenter的自我保護機制1)        ServiceCenter在一個時間窗內**到etcd有80%的例項下線事件,會立即啟動自我保護機制。2)        保護期間內,所有下線事件均儲存在待通知佇列中。3)        保護期間內,ServiceCenter收到佇列中例項上報註冊資訊則將其從佇列中移除,否則當例項租期到期,則推送例項下線通知事件到consumer服務。4)        佇列為空,則關閉自我保護機制。有了自我保護機制後,即使etcd儲存的資料全部丟失,這種極端場景下,SDK與ServiceCenter之間可在不影響業務的前提下,做到資料自動恢復。雖然這個恢復是有損的,但在這種災難場景下還能保持業務基本可用。