「在分佈式應用中,最多見的問題是什麼呢?」git
「一個分佈式應用部署上去後,還要關注什麼?」github
「這服務的遠程調用依賴彷佛有點多...」golang
在 《微服務的戰爭:級聯故障和雪崩》中有提到,在一個分佈式應用中,最多見,最有危險性之一的點就是級聯故障所形成的雪崩,而其對應的解決方案爲根據特定的規則/規律進行流量控制和熔斷降級,避免請求發生堆積,保護自身應用,也防止服務提供方進一步過載。面試
簡單來說就是,要控制訪問量的流量,要防各種調用的強/弱依賴,才能保護好應用程序的底線。api
要如何解決這個問題呢,能夠關注到問題的核心點是 「系統沒有任何的保護的狀況下」,所以核心就是讓系統,讓你的應用程序有流量控制的保護。通常含如下幾個方面:架構
以上的多種方式都可與內部的治理平臺打通,且流量控制、熔斷降級是不止面試應用程序的,就看資源埋點上如何設計、注入。常見有以下幾種角度:併發
在資源不變的狀況下,系統所能提供的處理能力是有限的。而系統所面對的請求所到來的時間和量級每每是隨機且不可控的。所以就會存在可能出現突發性流量,而在系統沒有任何的保護的狀況下,系統就會在數分鐘內沒法提供正常服務,常見過程爲先是出現調用延遲,接着持續出現飽和度上升,最終假死。框架
流量控制通常常見的有兩種方式,分別是:基於 QPS、基於併發隔離。分佈式
最經常使用的流量控制場景,就是基於 QPS 來作流控,在必定的時間窗口內按照特定的規則達到所設定的閾值則進行調控:微服務
在本文中藉助 sentinel-golang 來實現案例所需的訴求,代碼以下:
import ( ... sentinel "github.com/alibaba/sentinel-golang/api" "github.com/alibaba/sentinel-golang/core/base" "github.com/alibaba/sentinel-golang/core/flow" "github.com/alibaba/sentinel-golang/util" ) func main() { _ = sentinel.InitDefault() _, _ = flow.LoadRules([]*flow.Rule{ { Resource: "控制吃煎魚的速度", Threshold: 60, ControlBehavior: flow.Reject, }, }) ... e, b := sentinel.Entry("控制吃煎魚的速度", sentinel.WithTrafficType(base.Inbound)) if b != nil { // Blocked } else { // Passed e.Exit() } }
總的來說,上述規則結果就是 1s 內容許經過 60 個請求,超出的請求的處理策略爲直接拒絕(Reject)。
首先咱們初始化了 Sentinel 並定義資源(Resource)爲 「控制吃煎魚的速度」。其 Threshold 配置爲 3,也就是 QPS 的閾值爲 3,統計窗口未設置默認值爲 1s,ControlBehavior 控制的行爲爲直接拒絕。
而在知足閾值條件後,常見的處理策略還有勻速排隊(Throttling),勻速排隊方式會嚴格控制請求經過的間隔時間,也就是讓請求以均勻的速度經過。
基於資源訪問的併發協程數來控制對資源的訪問數量,主要是控制對資源訪問的最大協程數,避免由於資源的異常致使協程耗盡。
這類狀況,Go 語言在設計上經常可使用協程池來進行控制,但設計老是趕不上計劃的,且不一樣場景狀況可能不一樣,所以做爲一個平常功能也是很是有存在的必要性。
在分佈式應用中,隨着不斷地業務拆分,遠程調用逐漸變得愈來愈多。且在微服務盛興的狀況下,一個小業務拆出七八個服務的也常有。
此時就會出現一個經典的問題,那就是客戶端的一個普通調用,頗有可能就要通過好幾個服務,而一個服務又有可能遠程調用外部 HTTP、SQL、Redis、RPC 等,調用鏈會特別的長。
若其中一個調用流程出現了問題,且沒有進行調控,就會出現級聯故障,最終致使系統雪崩:
服務 D 所依賴的外部接口出現了故障,而他並無作任何的控制,所以擴散到了全部調用到他的服務,天然也就包含服務 B,所以最終出現系統雪崩。
這種最經典的是出如今默認 Go http client 調用沒有設置 Timeout,從而只要出現一次故障,就足矣讓記住這類 「坑」,畢竟崩的 」慢「,錯誤日誌還多。(via: 《微服務的戰爭:級聯故障和雪崩》)
爲了解決上述問題所帶來的災難,在分佈式應用中常須要對服務依賴進行熔斷降級。在存在問題時,暫時切斷內部調用,避免局部不穩定因素致使整個分佈式系統的雪崩。
而熔斷降級做爲保護服務自身的手段,一般是在客戶端進行規則配置和熔斷識別:
常見的有三種熔斷降級措施:慢調用比例策略、錯誤比例策略、錯誤計數策略。
在所設定的時間窗口內,慢調用的比例大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。
在所設定的時間窗口內,調用的訪問錯誤比例大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。
在所設定的時間窗口內,調用的訪問錯誤次數大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。
知道流量控制、熔斷降級的基本概念和功能後,在現實環境中應該如何結合項目進行使用呢。最多見的場景是可針對業務服務的 HTTP 路由進行流量控制,以 HTTP 路由做爲資源埋點,這樣子就能夠實現接口級的調控了。
還能夠加強其功能特性,針對參數也進行多重匹配。常會有這種限流訴求:針對 HTTP GET /eddycjy/info
且 language 爲 go 的狀況下進行限流。另外還能夠針對 HTTP 調用封裝統一方法,進行默認的熔斷注入,實現多重保障。
而結合系統負載、服務 QPS 等,能夠對限流熔斷的規則數據源進行實時調控,再結合 Watch 機制,就可以比較平滑的實現自適應限流熔斷的接入。
在分佈式應用中,限流熔斷是很是重要的一環,越早開始作越有益處。但須要注意的是,不一樣公司的業務模型多多少少有些不同,所針對的匹配維度多少有些不一樣,所以須要提早進行業務調研。
且在作業務的限流熔斷時,注意把度量指標的打點作上,這樣子後續就可以結合 Prometheus+Grafana+Alertmanager 作一系列的趨勢圖,熔斷告警,自動擴縮容等相關工做了,會是一個很好的助力。
分享 Go 語言、微服務架構和奇怪的系統設計,歡迎你們關注個人公衆號和我進行交流和溝通,二維碼:
最好的關係是互相成就,各位的點贊就是煎魚創做的最大動力,感謝支持。