業務壓力一大就宕機?一文帶你搞懂限流熔斷!

「在分佈式應用中,最多見的問題是什麼呢?」git

「一個分佈式應用部署上去後,還要關注什麼?」github

image

「這服務的遠程調用依賴彷佛有點多...」golang

前言

《微服務的戰爭:級聯故障和雪崩》中有提到,在一個分佈式應用中,最多見,最有危險性之一的點就是級聯故障所形成的雪崩,而其對應的解決方案爲根據特定的規則/規律進行流量控制和熔斷降級,避免請求發生堆積,保護自身應用,也防止服務提供方進一步過載。面試

image

簡單來說就是,要控制訪問量的流量,要防各種調用的強/弱依賴,才能保護好應用程序的底線。api

訴求,指望

  1. 訴求:做爲一個業務,確定是但願自家的應用程序,可以整年無休,最低限度也要有個 4 個 9,一出突發性大流量,在資源貧乏的窗口期,就立刻可以自動恢復。
  2. 指望:萬丈高樓平地起,咱們須要對應用程序進行流量控制、熔斷降級。確保在特定的規則下,系統可以進行容錯,只處理本身力所能及的請求。如有更一步訴求,再自動擴縮容,提升系統資源上限。

解決方案

要如何解決這個問題呢,能夠關注到問題的核心點是 「系統沒有任何的保護的狀況下」,所以核心就是讓系統,讓你的應用程序有流量控制的保護。通常含如下幾個方面:架構

  • 來自端控制:在業務/流量網關處內置流量控制、熔斷降級的外部插件,起到端控制的效果。
  • 來自集羣/節點控制:在統一框架中內建流量控制、熔斷降級的處理邏輯,起到集羣/節點控制的效果。
  • 來自 Mesh 控制:經過 ServiceMesh 來實現流量控制、熔斷降級。侵入性小,能帶來多種控制模式,但有利有弊。

以上的多種方式都可與內部的治理平臺打通,且流量控制、熔斷降級是不止面試應用程序的,就看資源埋點上如何設計、注入。常見有以下幾種角度:併發

  • 資源的調用關係:例如遠程調用,像是面向 HTTP、SQL、Redis、RPC 等調用均,另外針對文件句柄控制也能夠。
  • 運行指標:例如 QPS、線程池、系統負載等。

流量控制

在資源不變的狀況下,系統所能提供的處理能力是有限的。而系統所面對的請求所到來的時間和量級每每是隨機且不可控的。所以就會存在可能出現突發性流量,而在系統沒有任何的保護的狀況下,系統就會在數分鐘內沒法提供正常服務,常見過程爲先是出現調用延遲,接着持續出現飽和度上升,最終假死。框架

image

流量控制通常常見的有兩種方式,分別是:基於 QPS、基於併發隔離。分佈式

基於 QPS

最經常使用的流量控制場景,就是基於 QPS 來作流控,在必定的時間窗口內按照特定的規則達到所設定的閾值則進行調控:微服務

image

案例

在本文中藉助 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),勻速排隊方式會嚴格控制請求經過的間隔時間,也就是讓請求以均勻的速度經過。

基於併發隔離

基於資源訪問的併發協程數來控制對資源的訪問數量,主要是控制對資源訪問的最大協程數,避免由於資源的異常致使協程耗盡。

image

這類狀況,Go 語言在設計上經常可使用協程池來進行控制,但設計老是趕不上計劃的,且不一樣場景狀況可能不一樣,所以做爲一個平常功能也是很是有存在的必要性。

熔斷降級

在分佈式應用中,隨着不斷地業務拆分,遠程調用逐漸變得愈來愈多。且在微服務盛興的狀況下,一個小業務拆出七八個服務的也常有。

此時就會出現一個經典的問題,那就是客戶端的一個普通調用,頗有可能就要通過好幾個服務,而一個服務又有可能遠程調用外部 HTTP、SQL、Redis、RPC 等,調用鏈會特別的長。

若其中一個調用流程出現了問題,且沒有進行調控,就會出現級聯故障,最終致使系統雪崩:

image

服務 D 所依賴的外部接口出現了故障,而他並無作任何的控制,所以擴散到了全部調用到他的服務,天然也就包含服務 B,所以最終出現系統雪崩。

這種最經典的是出如今默認 Go http client 調用沒有設置 Timeout,從而只要出現一次故障,就足矣讓記住這類 「坑」,畢竟崩的 」慢「,錯誤日誌還多。(via: 《微服務的戰爭:級聯故障和雪崩》)

目的和措施

爲了解決上述問題所帶來的災難,在分佈式應用中常須要對服務依賴進行熔斷降級。在存在問題時,暫時切斷內部調用,避免局部不穩定因素致使整個分佈式系統的雪崩。

而熔斷降級做爲保護服務自身的手段,一般是在客戶端進行規則配置和熔斷識別:

image

常見的有三種熔斷降級措施:慢調用比例策略、錯誤比例策略、錯誤計數策略。

慢調用比例

在所設定的時間窗口內,慢調用的比例大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。

錯誤比例

在所設定的時間窗口內,調用的訪問錯誤比例大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。

錯誤計數

在所設定的時間窗口內,調用的訪問錯誤次數大於所設置的閾值,則對接下來訪問的請求進行自動熔斷。

實踐案例

知道流量控制、熔斷降級的基本概念和功能後,在現實環境中應該如何結合項目進行使用呢。最多見的場景是可針對業務服務的 HTTP 路由進行流量控制,以 HTTP 路由做爲資源埋點,這樣子就能夠實現接口級的調控了。

image

還能夠加強其功能特性,針對參數也進行多重匹配。常會有這種限流訴求:針對 HTTP GET /eddycjy/info 且 language 爲 go 的狀況下進行限流。另外還能夠針對 HTTP 調用封裝統一方法,進行默認的熔斷注入,實現多重保障。

而結合系統負載、服務 QPS 等,能夠對限流熔斷的規則數據源進行實時調控,再結合 Watch 機制,就可以比較平滑的實現自適應限流熔斷的接入。

總結

在分佈式應用中,限流熔斷是很是重要的一環,越早開始作越有益處。但須要注意的是,不一樣公司的業務模型多多少少有些不同,所針對的匹配維度多少有些不一樣,所以須要提早進行業務調研。

且在作業務的限流熔斷時,注意把度量指標的打點作上,這樣子後續就可以結合 Prometheus+Grafana+Alertmanager 作一系列的趨勢圖,熔斷告警,自動擴縮容等相關工做了,會是一個很好的助力。

個人公衆號

分享 Go 語言、微服務架構和奇怪的系統設計,歡迎你們關注個人公衆號和我進行交流和溝通,二維碼:

image

最好的關係是互相成就,各位的點贊就是煎魚創做的最大動力,感謝支持。

相關文章
相關標籤/搜索