您好,登錄后才能下訂單哦!
怎么提高微服務架構的可用性,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
業界通常用多少個9來衡量系統的可用性,如99.99%表示一年中有1小時左右的不可用時間。任何一個服務的可用性都不會是100%,意味著在服務運行時間里還是有可能發生故障。當把功能集中且運行在同一個應用中的單體架構拆分成多個相互獨立的微服務架構后,雖然可以降低一損俱損的全局性故障風險,但由于微服務之間存在大量的依賴關系, 隨著微服務個數的增多,依賴關系也將會變得越來越復雜,而且每個微服務都有可能發生故障,如果不能做好相互依賴的隔離,避免故障的連鎖反應,結果可能比單體更糟糕。
假設有100個微服務,并且每個微服務只會發生1種故障,那么總共會有 2100 種不同的故障場景,而每個微服務自身可能不止1種故障。當某個微服務發生故障時,如何確保不會導致其他依賴的微服務不可用, 如何確保系統自動降級把發生故障的微服務排除出去,如何確保故障不會擴展到整個系統? 那么如何有效確保微服務架構的可用性將會成為挑戰。
設定一個用戶請求依賴5個微服務的協作完成(pod為K8S容器框架中的定義,為一組相同功能的容器)。
在一開始每個依賴的Service都是正常的,現假設有一個Service異常了,這時可能會有三種情況:
這個請求成功,假設因網絡異常或宕機導致Service C某個節點不可用,但有高可用節點取代了這個失敗節點,這時Service C不受影響,依然可用。
這個請求是成功的,假設是Service D故障,而這個Service不是關鍵性的,運行失敗也可以繼續進行,比如注冊用戶需要調用一個服務發送注冊成功的郵件給用戶,如果發郵件的這個Service不可用,但不會影響用戶的注冊,所以用戶注冊還是會成功,郵件可以等服務恢復后再發送,只有時間上的延遲。這時Service A不受影響,依然可用,如下圖所示:
這個請求失敗,比如異常的節點是Service E,而Service E是代碼級邏輯異常,所有高可用節點都不可用,這時需要將Service E進行依賴隔離,否則ServiceA可能會受到ServiceE的影響而不可用。需要做一些措施確保Service A不會受影響,依然可用。
可以從以下幾個策略可以提高微服務架構的可用性:
1) 失效轉移
提高服務的高可用性,最基本的原則就是消除單點,通過負載均衡技術構建集群,所有的集群節點都是無狀態且完全對等的。如上面講的第1種情況。當一個節點異常時,負載均衡服務器會把用戶發送的訪問請求發送到可用的節點上。對用戶來說,某個節點異常是無感的,用戶請求會透明的轉移到了可用的節點上執行。
2) 異步調用
避免一個服務失敗導致整個應用請求失敗很重要的是使用異步調用。如上面講的第2種情況。如果采用的是同步調用,當郵件服務異常時,會導致其他兩個服務也無法執行,最終導致用戶注冊失敗。如果采用異步調用,Service A把用戶注冊信息發送給消息隊列后立即返回用戶注冊成功的響應,雖然郵件服務不能用,但是寫數據庫的服務,權限開通等服務都能正常執行。所以即使郵件不能發送成功,也不會影響其他服務的執行,用戶注冊可順利完成。
3) 依賴隔離
用戶請求發送給Service A,Service A分配線程資源通過網絡遠程調用其他的Service,假設調用Service E發生異常時,Service A中對Service E調用的線程就可能會響應慢或僵死,而線程是系統的資源,如果短時間內得不到釋放,在高并發的情況下資源就會被耗盡,結果會導致Service A也不可用,雖然其他的服務依然可用。
Service A的資源是有限的,比如Service A啟動時分配了400個線程,當400個線程都因調用Service E時異常不能及時正常的釋放,如線程死鎖,響應時間慢,導致 400個線程全部僵死在調用Service E上,這里Service A就沒有空閑的線程來接收新的用戶請求,這時就會導致Service A掛起或僵死。所以避免Service A被依賴的服務拖垮就是要確保Service A的線程資源不會被調用的依賴服務耗盡,在《Release It!》一書中總結了非常重要的兩條方法: 設置超時和使用斷路器。
設置超時
在應用中設置服務調用的超時時間后,一旦線程的執行時間超過了所設置的時間,就拋出異常信息,自動斷開連接,這樣服務的線程就不會都長時間僵死在調用異常的服務上,導致沒有空閑線程接收新的用戶請求,可以避免Service A因為調用Server E 異常而被拖垮,自身不可用。所以通過網絡調用外部依賴服務時,都必須設置超時。
使用斷路器
斷路器大家都不陌生,家里電表在電流過載或者短路時就會跳閘,如果不跳閘,電路就不斷開,電線就會升溫,造成火災。有了斷路器之后,電流過載時就會自動跳閘斷開電路,避免引起更大的災難。在程序中也是如此,當知道服務調用某個依賴服務有大量超時的時候,再讓新的請求去訪問也只會超時,并不能得到想到的結果,還會消耗現有資源,增加負載,導致服務不可用。
這個時候使用斷路器就能避免這種資源浪費,在自身服務和依賴服務之間放一個斷路器,通過斷路器的監控實時統計訪問的狀態,當訪問超時或者失敗達到某個閾值的時候(如50%請求超時,或者連續20次請失敗),就打開斷路器,那么后續的請求就直接返回失敗,而不是一個長時間的等待,再根據一個時間間隔(如30秒)或請求超時的情況(如0%的超時)嘗試關閉斷路器(或者更換保險絲),看依賴是否恢復服務了。
一個服務依賴多個服務時,如果其中一個非核心的依賴不可用,通過設置超時和使用斷路器,可以確保Service A在調用異常的Service E并不會導致自身的異常,在大部分情況下服務還能健康運轉,可以很好的做到依賴隔離。
4)設置限流
在服務訪問的高峰期可能因為大量的并發導致性能下降,嚴重時將會有大量的請求排隊,可能會導致服務宕機。為了保證應用的可用性,可拒絕低優先級的調用,讓高優先級的請求成功,避免所有調用都失敗的情況,并且為每個依賴服務提供一個小的線程池,如果線程池已滿調用將被立即拒絕,默認不采用排隊,可以加速失敗判定時間。這樣的結果是有些用戶可以訪問,而有些用戶失敗,但失敗的用戶重新訪問又可以是正常的訪問。這樣能確保服務的可用性,而不是完全不可用。
雖然有了上面的一些可提高系統可用性的措施,但系統是復雜的,一個簡單的修復都有可能造成不可想像的后果,且系統又是動態的,有些系統可能一天都發布幾次,幾十次。在這種情況下 故障依然是不可避免的。比起半夜深睡或正在享受節假日的美好時光時系統故障來當救火隊員,會做更多的措施來提高系統的可用性。比如在某些企業里會定期舉行生產環境的故障應急演練。
過去都是在業務低峰時進行人為故障測試高可用方案是否生效,包括主機,網絡,應用,存儲等每一層架構都進行演練,而現在也逐漸的在正常的生產時間進行故障應急演練,檢查系統的高可用性。問題在于可能在演練時能夠立即恢復,但真實故障發生時還是會出現長時間故障得不到恢復的情況。一個是演練是按照已知的場景制定的方案實施,二是演練的范圍基本是高可用節點的切換或災備系統的切換,第三個問題是這個演練是人為操作,需要全員的參與,并不會頻繁的舉行。但系統是動態的,這次是高可用的,不代表下周,或下個月還是高可用的。
當單體架構變成微服務架構后,應用層的演練就會變得復雜,就像前面提到的,如果每個服務只有一個故障可能都會有 2100種不同。因此需要有一種自動的故障測試方式來回避微服務化后演練實施的可操作性。
Netflix公司提出了一種自動故障測試的方案來提高微服務架構的可用性。這個測試方案也是在生產環境中進行,而故障測試的最終目的,是為了當真的有故障發生時,生產環境不會停止服務,并且整套系統可以在沒有人為干預的情況下,非常優雅地通過降級將發生故障的部分組件排除出去。他們認為如果只在測試環境中測試,而真實生產環境的業務壓力,業務場景,環境配置、網絡性能和硬件性能都沒有測試過,當故障在生產環境中真實發生時發現緩解問題的方案可能會失效。而且這個測試只在工作時間運行,這樣工程師可以得到告警并及時響應。
Netflix通過Peter Alvaro在論文《路徑驅動的故障注入(Lineage-driven fault injection)》中提到了一套名為“Molly”的算法和自身的故障注入測試FIT(failure injection testing)實現了這套安全地自動化故障注入測試。Molly是從一套系統的無故障狀態出發,然后試圖去回答說“系統是如何達到目前這種無故障的狀態的?”簡單舉例介紹一下這個算法的原理。先利用自身的追蹤系統繪制一個樹來表示每個請求經過的所有的微服務。
(A or R or P or B)
在最開始,上圖中的四個節點都是必須的,且正常的。然后從這個正確輸出反推,隨機選擇一個節點注入故障,找到并構建支持其正確性的邏輯鏈條圖 。當節點注入故障后,這時可能有三種情況:
這個請求失敗,我們已經找到一個節點會故障,從而我們可以刪除未來的實驗中包含這個故障。
這個請示是成功的-但這個失敗的節點不是關鍵性的
這個請求成功,有高可用節點取代了這個失敗
在這個例子中,首先在Ratings中注入失敗,但請求是成功的。說明Rating失敗并不會影響服務的使用,那就先把這個節點排除,重新繪制請求樹。
(A or P or B) and (A or P or B or R)
這時可以看到,請求可以通過(A or P or B)的方式實現,也可以通過 (A or P or B or R)的方式實現。接下來再在Playlist中注入故障,這時請求還是成功的,因為請求轉發到備用節點上執行,這里將會有一個新的節點可以訪問。
(A or PF or B) and (A or P or B) and (A or P or B or R)
這時可以更新公式,說明可以通過(A or PF or B) and (A or P or B) and (A or P or B or R)三種方式請求服務。然后通過這樣不停的測試直到遍歷完所有正確輸出,沒有失敗的節點可以找到。
Molly沒有規定怎么搜索空間,所以實現時會估算所有的方案,然后隨機選擇最小的方案的集合。比如,最后的方案可能是[{A}, {PF}, {B}, {P,PF}, {R,A}, {R,B} …]。先選擇所有的單節點注入失敗,再選擇所的有雙節點的組合注入失敗,依此類推。
這個測試的目的是在影響大量成員前找到和修復故障,在生產環境上進行故障測試時,不能接受引起大量的問題節點。為了避免這個風險,只能在指定的范圍構建測試,指定的范圍包含兩個關鍵的概念:故障范圍(failure scope)和注入點(injection points)。故障范圍指的是,把一次故障測試可能產生的影響,限制在一個可控的范圍內,這個范圍可以小到某個特定的用戶或者設備,也可以大到所有用戶的1%。而注入點指的是系統內計劃會發生故障的組件,比如RPC層,緩存層,或者持久層。
故障模擬測試從FIT服務把故障模擬元數據注入到Zuul(緣邊網關服務)開始,如果請求符合故障范圍(failure scope)則注入失敗。這個故障可能是延遲服務調用,或達到持久層失敗。每個被接觸到的注入點(injection points)檢查這個請求的上下文是否為指定要被注入故障的組件,如果是,在這個注入點模擬故障。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。