本文由golang教學專欄為大家介紹關於Sentinel限流熔斷降級,內容包括限流和熔斷介紹, alibaba開源的Sentinel、安裝、實戰介紹,希望對需要的朋友有所幫助!
什麼是限流\ 熔斷\ 降級
#限流:在我們的後天系統中,如果那一天突然進入大量流量,我們服務原本最高只能處理同時2k 的請求,突然一
下就來了5k 的請求,這對伺服器的壓力是不是很要命,這很可能直接導致伺服器宕機,崩潰,導致原本2K 的處
理量都不能處理了,這時候我們需要限流,限流的作用就是保持訪問量到達伺服器最高的情況下,對多餘的請求
不做處理,相較之下,比伺服器直接掛掉是好很多的。例如在雙十一的時候,我們要下單就會看到類似」 請求繁
忙,請稍後重試!」。
熔斷: 相信大家對斷路器並不陌生,它就相當於一個開關,打開後可以阻止流量通過。例如保險絲,當電流過大
時,就會熔斷,以避免元件損壞。
服務熔斷是指調用方訪問服務時透過斷路器做代理進行訪問,斷路器會持續觀察服務返回的成功、失敗的狀態,
當失敗超過設定的閾值時斷路器打開,請求就不能真正地存取到服務了。
使用場景
服務故障或升級時,讓客戶端快速失敗
失敗處理邏輯容易定義
回應耗時較長,客戶端設定的read timeout 會比較長,防止客戶端大量重試請求導致的連線、執行緒資源無法釋放
* 降級*: 服務降級是從整個系統的負載情況出發和考慮的,對某些負載會比較高的情況,為了預防某些功能(業務
場景)出現負載過載或回應慢的情況,在其內部暫時捨棄一些非核心的介面和資料的請求,而直接傳回一個
提前準備好的fallback(退路)錯誤處理訊息。這樣,雖然提供的是一個有損的服務,但卻保證了整個系統的穩健
定性和可用性。
什麼是Sentinel
Sentinel 是阿里開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個維度來保障服務之間的穩定性。
官網:github.com/alibaba/Sentinel/wiki
2012 年,Sentinel 誕生於阿里巴巴,其主要目標是流量控制。 2013-2017 年,Sentinel 迅速發展,並成為阿里巴巴所有微型服務的基本組成部分。它已在 6000 多個應用程式中使用,涵蓋了幾乎所有核心電子商務場景。 2018 年,Sentinel 演變為一個開源專案。 2020 年,Sentinel Golang 發布。
特點:
豐富的應用場景:Sentinel 承接了阿里巴巴近10 年的雙十一大促流量的核心場景,例如秒殺(即
突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制、即時熔斷下游不可用應用等。
完整的即時監控 :Sentinel 同時提供即時的監控功能。您可以在控制台中看到接入應用的單一機器
器秒級數據,甚至 500 台以下規模的叢集的匯總運作情況。
* 生態廣廣泛*
Sentinel 的歷史
2012 年,Sentinel 誕生,主要功能為入口流量控制。
2013-2017 年,Sentinel 在阿里巴巴集團內部迅速發展,成為基礎技術模組,涵蓋了所有的核心場景。 Sentinel 也因此累積了大量的流量歸整場景以及生產實務。
2018 年,Sentinel 開源,並持續演進。
2019 年,Sentinel 朝著多語言擴展的方向不斷探索,推出C 原生版本,同時針對Service Mesh 場景也推出了Envoy 叢集流量控制支持,以解決Service Mesh 架構下多語言限流的問題。
2020 年,推出 Sentinel Go 版本,繼續朝著雲端原生方向演進。
2021 年,Sentinel 正在朝著 2.0 雲端原生高可用決策中心元件進行演進;同時推出了 Sentinel Rust 原生版本。同時我們也在 Rust 社群進行了 Envoy WASM extension 及 eBPF extension 等場景探索。
2022 年,Sentinel 品牌升級為流量治理,領域涵蓋流量路由/ 調度、流量染色、流量降級、過載保護/ 實例摘除等;同時社群將流量治理相關標準抽出到OpenSergo 標準中,Sentinel 作為流量治理標準實現。
Sentinel-go 的安裝
#Sentinel-go 開源位址:https://github.com/alibaba/sentinel-golang
官網文檔
安裝:go get github.com/alibaba/sentinel-golang/api
Go 限流實戰
qps 限流
package main import ( "fmt" "log" sentinel "github.com/alibaba/sentinel-golang/api" "github.com/alibaba/sentinel-golang/core/base" "github.com/alibaba/sentinel-golang/core/flow" ) func main() { //基于sentinel的qps限流 //必须初始化 err := sentinel.InitDefault() if err != nil { log.Fatalf("Unexpected error: %+v", err) } //配置限流规则:1秒内通过10次 _, err = flow.LoadRules([]*flow.Rule{ { Resource: "some_test", TokenCalculateStrategy: flow.Direct, ControlBehavior: flow.Reject, //超过直接拒绝 Threshold: 10, //请求次数 StatIntervalInMs: 1000, //允许时间内 }, }) if err != nil { log.Fatalf("Unexpected error: %+v", err) return } for i := 0; i < 12; i++ { e, b := sentinel.Entry("some_test", sentinel.WithTrafficType(base.Inbound)) if b != nil { fmt.Println("限流了") } else { fmt.Println("检查通过") e.Exit() } } }
列印結果:
检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 限流了 限流了
Thrnotting
package main import ( "fmt" "log" "time" sentinel "github.com/alibaba/sentinel-golang/api" "github.com/alibaba/sentinel-golang/core/base" "github.com/alibaba/sentinel-golang/core/flow" ) func main() { //基于sentinel的qps限流 //必须初始化 err := sentinel.InitDefault() if err != nil { log.Fatalf("Unexpected error: %+v", err) } //配置限流规则 _, err = flow.LoadRules([]*flow.Rule{ { Resource: "some_test", TokenCalculateStrategy: flow.Direct, ControlBehavior: flow.Throttling, //匀速通过 Threshold: 10, //请求次数 StatIntervalInMs: 1000, //允许时间内 }, }) if err != nil { log.Fatalf("Unexpected error: %+v", err) return } for i := 0; i < 12; i++ { e, b := sentinel.Entry("some_test", sentinel.WithTrafficType(base.Inbound)) if b != nil { fmt.Println("限流了") } else { fmt.Println("检查通过") e.Exit() } time.Sleep(time.Millisecond * 100) } }
检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过 检查通过
Warrm_up
package main import ( "fmt" "log" "math/rand" "time" sentinel "github.com/alibaba/sentinel-golang/api" "github.com/alibaba/sentinel-golang/core/base" "github.com/alibaba/sentinel-golang/core/flow" ) func main() { //先初始化sentinel err := sentinel.InitDefault() if err != nil { log.Fatalf("初始化sentinel 异常: %v", err) } var globalTotal int var passTotal int var blockTotal int ch := make(chan struct{}) //配置限流规则 _, err = flow.LoadRules([]*flow.Rule{ { Resource: "some-test", TokenCalculateStrategy: flow.WarmUp, //冷启动策略 ControlBehavior: flow.Reject, //直接拒绝 Threshold: 1000, WarmUpPeriodSec: 30, }, }) if err != nil { log.Fatalf("加载规则失败: %v", err) } //我会在每一秒统计一次,这一秒只能 你通过了多少,总共有多少, block了多少, 每一秒会产生很多的block for i := 0; i < 100; i++ { go func() { for { globalTotal++ e, b := sentinel.Entry("some-test", sentinel.WithTrafficType(base.Inbound)) if b != nil { //fmt.Println("限流了") blockTotal++ time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond) } else { passTotal++ time.Sleep(time.Duration(rand.Uint64()%10) * time.Millisecond) e.Exit() } } }() } go func() { var oldTotal int //过去1s总共有多少个 var oldPass int //过去1s总共pass多少个 var oldBlock int //过去1s总共block多少个 for { oneSecondTotal := globalTotal - oldTotal oldTotal = globalTotal oneSecondPass := passTotal - oldPass oldPass = passTotal oneSecondBlock := blockTotal - oldBlock oldBlock = blockTotal time.Sleep(time.Second) fmt.Printf("total:%d, pass:%d, block:%d\n", oneSecondTotal, oneSecondPass, oneSecondBlock) } }() <-ch }
列印結果:逐漸到達1k, 在1k 位置上下波動
total:11, pass:9, block:0 total:21966, pass:488, block:21420 total:21793, pass:339, block:21414 total:21699, pass:390, block:21255 total:21104, pass:393, block:20654 total:21363, pass:453, block:20831 total:21619, pass:491, block:21052 total:21986, pass:533, block:21415 total:21789, pass:594, block:21123 total:21561, pass:685, block:20820 total:21663, pass:873, block:20717 total:20904, pass:988, block:19831 total:21500, pass:996, block:20423 total:21769, pass:1014, block:20682 total:20893, pass:1019, block:19837 total:21561, pass:973, block:20524 total:21601, pass:1014, block:20517 total:21475, pass:993, block:20420 total:21457, pass:983, block:20418 total:21397, pass:1024, block:20320 total:21690, pass:996, block:20641 total:21526, pass:991, block:20457 total:21779, pass:1036, block:20677
Go 熔斷實戰
這裡我們介紹一個錯誤數量的,查看詳細熔斷機制
error_count package main import ( "errors" "fmt" "log" "math/rand" "time" sentinel "github.com/alibaba/sentinel-golang/api" "github.com/alibaba/sentinel-golang/core/circuitbreaker" "github.com/alibaba/sentinel-golang/core/config" "github.com/alibaba/sentinel-golang/logging" "github.com/alibaba/sentinel-golang/util" ) type stateChangeTestListener struct { } func (s *stateChangeTestListener) OnTransformToClosed(prev circuitbreaker.State, rule circuitbreaker.Rule) { fmt.Printf("rule.steategy: %+v, From %s to Closed, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis()) } func (s *stateChangeTestListener) OnTransformToOpen(prev circuitbreaker.State, rule circuitbreaker.Rule, snapshot interface{}) { fmt.Printf("rule.steategy: %+v, From %s to Open, snapshot: %d, time: %d\n", rule.Strategy, prev.String(), snapshot, util.CurrentTimeMillis()) } func (s *stateChangeTestListener) OnTransformToHalfOpen(prev circuitbreaker.State, rule circuitbreaker.Rule) { fmt.Printf("rule.steategy: %+v, From %s to Half-Open, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis()) } func main() { //基于连接数的降级模式 total := 0 totalPass := 0 totalBlock := 0 totalErr := 0 conf := config.NewDefaultConfig() // for testing, logging output to console conf.Sentinel.Log.Logger = logging.NewConsoleLogger() err := sentinel.InitWithConfig(conf) if err != nil { log.Fatal(err) } ch := make(chan struct{}) // Register a state change listener so that we could observer the state change of the internal circuit breaker. circuitbreaker.RegisterStateChangeListeners(&stateChangeTestListener{}) _, err = circuitbreaker.LoadRules([]*circuitbreaker.Rule{ // Statistic time span=10s, recoveryTimeout=3s, maxErrorCount=50 { Resource: "abc", Strategy: circuitbreaker.ErrorCount, RetryTimeoutMs: 3000, //3s只有尝试回复 MinRequestAmount: 10, //静默数 StatIntervalMs: 5000, Threshold: 50, }, }) if err != nil { log.Fatal(err) } logging.Info("[CircuitBreaker ErrorCount] Sentinel Go circuit breaking demo is running. You may see the pass/block metric in the metric log.") go func() { for { total++ e, b := sentinel.Entry("abc") if b != nil { // g1 blocked totalBlock++ fmt.Println("协程熔断了") time.Sleep(time.Duration(rand.Uint64()%20) * time.Millisecond) } else { totalPass++ if rand.Uint64()%20 > 9 { totalErr++ // Record current invocation as error. sentinel.TraceError(e, errors.New("biz error")) } // g1 passed time.Sleep(time.Duration(rand.Uint64()%20+10) * time.Millisecond) e.Exit() } } }() go func() { for { total++ e, b := sentinel.Entry("abc") if b != nil { // g2 blocked totalBlock++ time.Sleep(time.Duration(rand.Uint64()%20) * time.Millisecond) } else { // g2 passed totalPass++ time.Sleep(time.Duration(rand.Uint64()%80) * time.Millisecond) e.Exit() } } }() go func() { for { time.Sleep(time.Second) fmt.Println(totalErr) } }() <-ch }
以上是什麼是限流和熔斷?詳解Sentinel限流熔斷降級的詳細內容。更多資訊請關注PHP中文網其他相關文章!