限流與熔斷初探

1 技術方案

1.1 限流/熔斷開關和閾值在ETCD中配置

在公共包go-common中封裝一個方法,在ETCD中設置限流/熔斷開啓/關閉的開關,將父類context傳遞進去,讀取環境變量,開關開啓則進行限流和熔斷(限流/熔斷閾值寫死,從環境變量中讀取)

優點:目標明確,工作量、技術實現可預知

缺點:需要人爲開啓/關閉限流、熔斷開關,限流/熔斷閾值固定,必須達到該閾值纔會出發,不夠靈活

1.2 根據打點統計結果決定限流/熔斷開啓時機(實時計算)

當同一個用戶對同一個模型或報表多次執行請求時(例如報表下載、查看線圖),請求次數超過閾值,自動開發限流;

當OLAP對同一個(模型/報表)請求多次返回查詢超時或者報錯時,這種返回異常超過設置的閾值,自動開啓熔斷,即在短時間內再次請求(該模型/報表)時,啓明星不再向OLAP查詢,而是直接報提示信息:OLAP查詢資源緊張/異常,等一定時間後(ETCD配置)再查詢的提示信息。

優點:視實時請求量、OLAP返回情況進行自動限流/熔斷,不需要人爲介入,比較靈活;

缺點:需要事先對各個接口打點,工作量大,情景複雜,實現難度高。

1.3 通過kong網關進行限流和熔斷

1.3.1 kong網關簡介

在微服務架構中,由於系統和服務的細分,導致系統結構變得非常複雜, 爲了跨平臺,爲了統一集中管理api,同時爲了不暴露後置服務。甚至有時候需要對請求進行一些安全、負載均衡、限流、熔斷、灰度等中間操作,基於此類種種的客觀需求一個類似綜合前置的系統就產生了,這就是API網關(API Gateway)。API網關作爲分散在各個業務系統微服務的API聚合點和統一接入點,外部請求通過訪問這個接入點,即可訪問內部所有的REST API服務。目前常用的微服務網關有kong、zuul、gateway。

1.3.2 優缺點分析

優點:可以利用現有框架進行功能擴展
缺點:需要接入grpc服務,前期工作量大
(1)grpc服務接入
Golang項目通過sidecar部署grpc-proxy和grpcserver(詳見:grpc-proxy(bridge)),實現grpc服務接入網關。

(2)grpc-proxy服務包括bridge服務以及proxy服務,其中bridge包括了服務註冊以及http請求橋接爲grpc請求,proxy包括了服務發現功能。

(3)golang可使用grpcx創建以及啓動服務,使用原生的grpc服務,需實現diagnose服務

1.4 實施節點安排

第一階段(年前):可以先實現方案1.1
第二階段(年後):等打點、緩存都加過之後,可以優化到方案1.2
第二階段(年後):go項目接入kong網關後,可以逐步優化到方案1.3

2 限流

2.1 限流(rate limit)技術方案

限流器,從字面上理解就是用來限制流量,有時候流量突增(可預期的比如“雙11”,不可預期的微博的熱門話題等),會將後端服務壓垮,甚至直接宕機,使用限流器能限制訪問後端的流量,起到一個保護作用,被限制的流量,可以根據具體的業務邏輯去處理,直接返回錯誤或者返回默認值等等。

2.2 限流算法

2.2.1 令牌桶算法

一個存放固定容量令牌的桶,按照固定速率往桶中添加令牌,請求是否被處理需要看桶中令牌是否足夠,當令牌數爲0時,則拒絕新的請求。

令牌桶算法限制的是平均流入速率,並允許一定程度的突發流量。

2.2.2 漏桶算法

一個固定容量的漏桶,按照固定速率流出請求,流入請求速率任意,當流入的請求數累積到漏桶容量時,則新的請求被拒絕。

漏桶算法限制的是平均流入速率,主要目的是平滑突發流入速率。

https://github.com/uber-go/ratelimit

https://github.com/juju/ratelimit

https://github.com/lyft/ratelimit

基於令牌桶算法和漏桶算法來實現的限速限流,Golang實現:https://github.com/yangwenmai/ratelimit

2.3 限流方式

2.3.1 應用級限流

  • 限流總併發/連接/請求數
  • 通過池化技術限流總資源數(連接池、線程池)
  • 限流某個接口的總併發/請求數
  • 限流某個接口的時間窗口請求數(某接口每秒的請求數)
  • 平滑限流某接口的請求數

2.3.2 分佈式限流

分佈式限流最關鍵的是將限流服務做成原子化,如可以使用Redis + Lua或Ngnix + Lua的方式實現這兩種技術可以實現高併發和高性能。

2.3.3 接入層限流

接入層通常指請求流量的入口,主要的目的有:負載均衡、非法請求過濾、請求聚合、緩存、降級、限流、A/B測試、服務質量監控等。

2.3.4 節流

有時想要在特定的時間窗口內對重複的相同事件最多隻處理一次,或者想要限制多個連續的相同事件最小執行時間間隔,那麼可以使用節流(throttle)是心啊,其防止多個相同事件連續重複執行。

3 降級與熔斷(circuit breaker)

在開發高併發系統時,可以通過使用緩存、限流、降級等方式保護系統,維護服務的穩定性和可用性。

當訪問量劇增、服務異常(如響應時間過長或者不響應)、非核心服務影響到核心系統的性能時,仍然需要保證關鍵服務是可用的,這是就需要使用服務降級方案。系統可以根據一些關鍵數據進行自動降級,也可以配置開關實現人工降級。

服務降級的目的是保證核心服務可用,即使是有損的。

3.1 降級分類

1.按照是否自動化分爲:自動降級、人工開關降級
2.按照功能分類:接口降級、服務降級

3.2 熔斷(circuit breaker)技術方案

和限流器對依賴服務的保護機制不一樣,熔斷器是當依賴的服務已經出現故障時,爲了保證自身服務的正常運行不再訪問依賴的服務,防止雪崩效應。

熔斷器有三種狀態:

  • 關閉狀態:服務正常,並維護一個失敗率統計,當失敗率達到閥值時,轉到開啓狀態;
  • 開啓狀態:服務異常,調用 fallback 函數,一段時間之後,進入半開啓狀態
  • 半開啓裝態:嘗試恢復服務,失敗率高於閥值,進入開啓狀態,低於閥值,進入關閉狀態
    github.com/afex/hystrix-go,提供了 go 熔斷器實現,使用也很方便。

(1)首先創建一個熔斷器:

使用hystrix-go創建熔斷器:

// 熔斷器
hystrix.ConfigureCommand(
    "addservice", // 熔斷器名字,可以用服務名稱命名,一個名字對應一個熔斷器,對應一份熔斷策略
    hystrix.CommandConfig{
        Timeout:                100,  // 超時時間 100ms
        MaxConcurrentRequests:  2,    // 最大併發數,超過併發返回錯誤
        RequestVolumeThreshold: 4,    // 請求數量的閥值,用這些數量的請求來計算閥值
        ErrorPercentThreshold:  25,   // 錯誤數量閥值,達到閥值,啓動熔斷,25%
        SleepWindow:            1000, // 熔斷嘗試恢復時間
    },
)

hystrix提供了阻塞和非阻塞兩種使用方式,完整代碼可以參考如下鏈接: https://github.com/hatlonely/hellogolang/blob/master/sample/addservice/cmd/client/main.go

(2)阻塞方式使用 Do 方法,返回一個 err

熔斷的阻塞方式:

err := hystrix.Do("addservice", func() error {
    // 正常業務邏輯,一般是訪問其他資源
    var err error
    // 設置總體超時時間 10 ms 超時
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res, err = client.Add(
        ctx, req,
        // 這裏可以再次設置重試次數,重試時間,重試返回碼
        grpc_retry.WithMax(3),
        grpc_retry.WithPerRetryTimeout(time.Duration(5)*time.Millisecond),
        grpc_retry.WithCodes(codes.DeadlineExceeded),
    )
    return err
}, func(err error) error {
    // 失敗處理邏輯,訪問其他資源失敗時,或者處於熔斷開啓狀態時,會調用這段邏輯
    // 可以簡單構造一個response返回,也可以有一定的策略,比如訪問備份資源
    // 也可以直接返回 err,這樣不用和遠端失敗的資源通信,防止雪崩
    // 這裏因爲我們的場景太簡單,所以我們可以在本地在作一個加法就可以了
    fmt.Println(err)
    res = &addservice.AddResponse{V: req.A + req.B}
    return nil
})

(3)非阻塞方式使用 Go 方法,返回一個 error 的 channel,建議在有多個資源需要併發訪問的場景下使用

熔斷的非阻塞方式:

err1 := hystrix.Go("addservice", func() error {
    var err error
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res1, err = client.Add(ctx, req)
    if err == nil {
        success <- struct{}{}
    }
    return err
}, nil)
 
// 有 fallback 處理
err2 := hystrix.Go("addservice", func() error {
    var err error
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50*time.Millisecond))
    defer cancel()
    res2, err = client.Add(ctx, req)
    if err == nil {
        success <- struct{}{}
    }
    return err
}, func(err error) error {
    fmt.Println(err)
    res2 = &addservice.AddResponse{V: req.A + req.B}
    success <- struct{}{}
    return nil
})
 
for i := 0; i < 2; i++ {
    select {
    case <-success:
        fmt.Println("success", i)
    case err := <-err1:
        fmt.Println("err1:", err)
    case err := <-err2:
        // 這個分支永遠不會走到,因爲熔斷機制裏面永遠不會返回錯誤
        fmt.Println("err2:", err)
    }
}

go-kit微服務實踐,http restful api、日誌功能、限流、服務監控、服務發現與註冊、api網關、服務鏈路跟蹤、服務熔斷、jwt身份認證:https://github.com/vincecfl/go-kit-one

https://github.com/rubyist/circuitbreaker

當程序進行遠程調用時,通常使用熔斷器(機制)/斷路器circuit breaker。遠程調用通常會在超時之前掛起一段時間。如果應用程序發出大量這些請求,許多資源可能會被擁堵在一起,等待這些超時發生。熔斷器通過包裝這些遠程調用,並在發生定義數量的故障或超時後跳閘。當熔斷器被絆倒時,任何之後的調用都將避免進行遠程調用並將錯誤返回給調用者。同時,熔斷器將定期允許再次嘗試某些調用請求,如果這些調用成功,將關閉訪問鏈路。

參考

微服務組件之限流器與熔斷器:https://studygolang.com/articles/13254

Golang實現請求限流的幾種辦法:https://blog.csdn.net/micl200110041/article/details/82013032

測試代碼: https://github.com/hatlonely/hellogolang/blob/master/sample/addservice/cmd/client/main.go

Circuit Breaker Pattern: https://docs.microsoft.com/en-us/previous-versions/msp-n-p/dn589784(v%3dpandp.10)

hystrix-go: https://github.com/afex/hystrix-go/

限流:漏桶算法和令牌桶算法:http://maiyang.github.io/%E6%8A%80%E6%9C%AF/%E7%AE%97%E6%B3%95/2017/05/28/rate-limit-algorithm

維基百科:Token_bucket:https://en.wikipedia.org/wiki/Token_bucket

維基百科:Leaky_bucket:https://en.wikipedia.org/wiki/Leaky_bucket

接口限流實踐:http://www.cnblogs.com/LBSer/p/4083131.html

流量調整和限流技術:http://colobu.com/2014/11/13/rate-limiting/
微服務最強開源流量網關Kong:https://www.jianshu.com/p/934d18ff2efd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章