随着微服务架构的流行,服务与服务之间交互的稳定性越来越重要。
当生产服务挂掉的时候,我们首先想到的是:激增流量、被其它服务拖垮、异常没处理。总之就是缺乏高可用防护和容错机制,尤其是针对流量的防护。
Sentinel是面向分布式服务架构的轻量级流量控制组件,以流量为切入点,从流量控制、熔断降级、自适应的系统保护等多个维度来保证系统的稳定性。

客户端和服务端进行通信时,两方的速率并不一定相等,如果客户端某一时间内发来的请求过多,会导致服务端处理不过来。
这时,服务端只能把处理不过来的数据放到缓存区里。如果缓存区满了,客户端还在发送数据,服务端没法存放只能把收到的数据包丢掉,而大量的丢包造成大量的网络资源浪费。
所以,需要控制客户端的发送速率,让发送和接收处于一定的平衡。这种控制手段,也就是流量控制。
在股市交易中的熔断是,当行情波动过大,终止交易15分钟,如果开盘后继续大幅波动,触发熔断机制,当天终止交易。
而程序中的熔断,是因为某个服务故障或者异常引起的,类似于股市熔断,当触达某个异常条件,直接熔断整个服务,而不是一直等到这个服务的响应。也是为了防止连锁反应造成雪崩,而采用的一种保护措施。
一般在接口响应速度很慢、超时的时候,触发熔断。
当服务器压力剧增的时候(系统压力过载),根据当前业务情况及流量,对一些服务和页面进行有策略的降级。以此来缓解服务器资源的的压力,保证核心业务的正常运行。“弃车保帅”的思想。
自动降级:1)通过配置的请求超时时间;2)不稳定API调用次数达到一定数量;3)所调用的远程服务出现故障
人工降级:1)秒杀场景;2)双十一大促场景
对系统维度进行保护,当系统负载很高时,如果仍持续让请求进入,可能会导致系统崩溃,无法响应。
即使是集群环境,当把压力过载的服务机器上的流量转发到别的机器上,如果别的机器也处于过载状态,则有可能把这台机器直接搞崩溃,最后导致整个集群不可用。
Sentinel提供的保护机制,让系统的入口流量和系统的负载达到一个平衡,进而保证系统在能力范围之内处理最多的请求。
Hystrix主要是以隔离和熔断为主的容错机制,超时或被熔断的调用将会快速失败,并可以提供 fallback 机制。用线程池隔离的方式,对资源进行完全的隔离,但是会引起大量的线程切换开销。性能并不是很高。
Sentinel侧重于多样化的流量控制,熔断降级,系统负载保护。是通过对并发线程数和服务的响应时间,来对流量进行控制和熔断降级,因此也就没有线程切换上的开销了,也不用预留线程池资源。性能更好。
xxxxxxxxxx611package main2
3import (4 "fmt"5 "log"6 "net/http"7
8 sentinel "github.com/alibaba/sentinel-golang/api"9 "github.com/alibaba/sentinel-golang/core/base"10 "github.com/alibaba/sentinel-golang/core/config"11 "github.com/alibaba/sentinel-golang/core/flow"12 "github.com/alibaba/sentinel-golang/logging"13)14
15func init() {16 // 初始化 sentinel17 conf := config.NewDefaultConfig()18 conf.Sentinel.Log.Logger = logging.NewConsoleLogger()19 err := sentinel.InitWithConfig(conf)20 if err != nil {21 log.Fatal(err)22 }23
24 // 创建限流规则:资源名 testFlowQPSRule 阈值 2 统计间隔 1000毫秒 ==》 每秒最多两次请求25 _, err = flow.LoadRules([]*flow.Rule{26 {27 Resource: "testFlowQPSRule", // 埋点资源名28 TokenCalculateStrategy: flow.Direct, // 计算策略:Direct-目录,WarmUp-预热流控29 ControlBehavior: flow.Reject, // 控制行为:Reject-拒绝,Throttling-匀速限流30 Threshold: 2, // 阀值31 StatIntervalInMs: 1000, // 统计间隔32 },33 })34 if err != nil {35 log.Fatalf("Unexpected error: %+v", err)36 return37 }38}39
40func main() {41 server := http.Server{42 Addr: "127.0.0.1:8080",43 }44
45 http.HandleFunc("/test_flow_rule", testFlowQPSRule)46 server.ListenAndServe()47}48
49// 测试流控规则50func testFlowQPSRule(w http.ResponseWriter, r *http.Request) {51 resourceName := "testFlowQPSRule"52
53 // 埋点(流控规则方式)54 e, b := sentinel.Entry(resourceName, sentinel.WithTrafficType(base.Inbound))55 if b != nil {56 fmt.Fprintf(w, "限流")57 } else {58 fmt.Fprintf(w, "不限流")59 e.Exit()60 }61}xxxxxxxxxx631package main2
3import (4 "fmt"5 "log"6 "net/http"7
8 sentinel "github.com/alibaba/sentinel-golang/api"9 "github.com/alibaba/sentinel-golang/core/base"10 "github.com/alibaba/sentinel-golang/core/config"11 "github.com/alibaba/sentinel-golang/core/flow"12 "github.com/alibaba/sentinel-golang/logging"13)14
15func init() {16 // 初始化 sentinel17 conf := config.NewDefaultConfig()18 conf.Sentinel.Log.Logger = logging.NewConsoleLogger()19 err := sentinel.InitWithConfig(conf)20 if err != nil {21 log.Fatal(err)22 }23
24 // 创建限流规则:资源名 testFlowWarmUpRule 阈值 2 统计间隔 1000毫秒 ==》 每秒最多两次请求25 _, err = flow.LoadRules([]*flow.Rule{26 {27 Resource: "testFlowWarmUpRule", // 埋点资源名28 TokenCalculateStrategy: flow.WarmUp, // 计算策略:Direct-目录,WarmUp-预热流控29 ControlBehavior: flow.Reject, // 控制行为:Reject-拒绝,Throttling-匀速限流30 Threshold: 10, // 阀值31 WarmUpPeriodSec: 3, // 预热周期(秒)32 WarmUpColdFactor: 3, // 预热冷因子33 StatIntervalInMs: 1000, // 统计间隔34 },35 })36 if err != nil {37 log.Fatalf("Unexpected error: %+v", err)38 return39 }40}41
42func main() {43 server := http.Server{44 Addr: "127.0.0.1:8080",45 }46
47 http.HandleFunc("/test_flow_rule", testFlowWarmUpRule)48 server.ListenAndServe()49}50
51// 测试流控规则52func testFlowWarmUpRule(w http.ResponseWriter, r *http.Request) {53 resourceName := "testFlowWarmUpRule"54
55 // 埋点(流控规则方式)56 e, b := sentinel.Entry(resourceName, sentinel.WithTrafficType(base.Inbound))57 if b != nil {58 fmt.Fprintf(w, "限流")59 } else {60 fmt.Fprintf(w, "不限流")61 e.Exit()62 }63}xxxxxxxxxx731package main2
3import (4 "fmt"5 "log"6 "math/rand"7 "net/http"8 "time"9
10 sentinel "github.com/alibaba/sentinel-golang/api"11 "github.com/alibaba/sentinel-golang/core/config"12 "github.com/alibaba/sentinel-golang/core/isolation"13 "github.com/alibaba/sentinel-golang/logging"14)15
16func init() {17 // 初始化 sentinel18 conf := config.NewDefaultConfig()19 conf.Sentinel.Log.Logger = logging.NewConsoleLogger()20 err := sentinel.InitWithConfig(conf)21 if err != nil {22 logging.Error(err, "fail")23 return24 }25 logging.ResetGlobalLoggerLevel(logging.DebugLevel)26
27 // 创建并发规则:资源名 testConcurrencyLimitationRule 并发请求数528 _, err = isolation.LoadRules([]*isolation.Rule{29 {30 Resource: "testConcurrencyLimitationRule", // 埋点资源名31 MetricType: isolation.Concurrency, // Concurrency-并发32 Threshold: 5, // 阈值,数值代表并发数33 },34 })35 if err != nil {36 log.Fatalf("Unexpected error: %+v", err)37 return38 }39}40
41func main() {42 server := http.Server{43 Addr: "127.0.0.1:8080",44 }45
46 http.HandleFunc("/test_concurrency_limitation_rule", testConcurrencyLimitationRule)47 server.ListenAndServe()48}49
50// 测试并发规则51func testConcurrencyLimitationRule(w http.ResponseWriter, r *http.Request) {52 resourceName := "testConcurrencyLimitationRule"53 ch := make(chan struct{})54 for i := 0; i < 10; i++ { // 利用循环goroutine并发55 go func() {56 for j := 0; j < 10; j++{57 // 埋点58 e, b := sentinel.Entry(resourceName, sentinel.WithBatchCount(1))59 if b != nil {60 sleepTime := time.Duration(rand.Uint64()%20) * time.Millisecond61 time.Sleep(sleepTime)62 fmt.Fprintf(w, "[Isolation] Blocked reason %v %v %v %v %v\n", b.BlockType().String(), "rule", b.TriggeredRule(), "snapshot", b.TriggeredValue())63 } else {64 sleepTime := time.Duration(rand.Uint64()%80+10) * time.Millisecond65 time.Sleep(sleepTime)66 fmt.Fprintf(w, "[Isolation] Passed\n")67 e.Exit()68 }69 }70 }()71 }72 <-ch73}xxxxxxxxxx881package main2
3import (4 "fmt"5 "log"6 "math/rand"7 "net/http"8 "time"9
10 sentinel "github.com/alibaba/sentinel-golang/api"11 "github.com/alibaba/sentinel-golang/core/base"12 "github.com/alibaba/sentinel-golang/core/circuitbreaker"13 "github.com/alibaba/sentinel-golang/core/config"14 "github.com/alibaba/sentinel-golang/logging"15 "github.com/alibaba/sentinel-golang/util"16)17
18type slowRtRatioStateChangeListener struct {19}20
21func (s *slowRtRatioStateChangeListener) OnTransformToClosed(prev circuitbreaker.State, rule circuitbreaker.Rule) {22 fmt.Printf("rule.steategy: %+v, From %s to Closed, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis())23}24
25func (s *slowRtRatioStateChangeListener) OnTransformToOpen(prev circuitbreaker.State, rule circuitbreaker.Rule, snapshot interface{}) {26 fmt.Printf("rule.steategy: %+v, From %s to Open, snapshot: %.2f, time: %d\n", rule.Strategy, prev.String(), snapshot, util.CurrentTimeMillis())27}28
29func (s *slowRtRatioStateChangeListener) OnTransformToHalfOpen(prev circuitbreaker.State, rule circuitbreaker.Rule) {30 fmt.Printf("rule.steategy: %+v, From %s to Half-Open, time: %d\n", rule.Strategy, prev.String(), util.CurrentTimeMillis())31}32
33func init() {34 // 初始化 sentinel35 conf := config.NewDefaultConfig()36 conf.Sentinel.Log.Logger = logging.NewConsoleLogger()37 err := sentinel.InitWithConfig(conf)38 if err != nil {39 log.Fatal(err)40 }41
42 circuitbreaker.RegisterStateChangeListeners(&slowRtRatioStateChangeListener{})43
44 // 创建熔断规则:资源名 testCircuitBreakerSlowRtRatioRule 最大慢请求40ms 慢请求率50% 统计间隔 5000ms 恢复时间 3000ms45 _, err = circuitbreaker.LoadRules([]*circuitbreaker.Rule{46 {47 Resource: "testCircuitBreakerSlowRtRatioRule", // 埋点资源名48 Strategy: circuitbreaker.SlowRequestRatio, // lowRequestRatio-慢请求,ErrorRatio-请求错误率,ErrorCount-请求错误量49 RetryTimeoutMs: 3000, // 熔断时长50 MinRequestAmount: 10, // 最小请求数51 StatIntervalMs: 5000, // 统计时间52 MaxAllowedRtMs: 40, // 慢请求的时间阈值53 Threshold: 0.5, // 阈值,数值根据strategy分别代表:最大慢请求比率(SlowRequestRatio);最大错误请求比率(ErrorRatio);最大错误请求计数(ErrorCount)54 },55 })56 if err != nil {57 log.Fatalf("Unexpected error: %+v", err)58 return59 }60}61
62func main() {63 server := http.Server{64 Addr: "127.0.0.1:8080",65 }66
67 http.HandleFunc("/test_circuit_breaker_slow_rt_ratio_rule", testCircuitBreakerSlowRtRatioRule)68 server.ListenAndServe()69}70
71// 测试熔断规则72func testCircuitBreakerSlowRtRatioRule(w http.ResponseWriter, r *http.Request) {73 resourceName := "testCircuitBreakerSlowRtRatioRule"74
75 // 埋点76 e, b := sentinel.Entry(resourceName, sentinel.WithTrafficType(base.Inbound))77 if b != nil {78 sleepTime := time.Duration(rand.Uint64()%20) * time.Millisecond79 time.Sleep(sleepTime)80 fmt.Fprintf(w, "熔断")81 } else {82 // 模拟随机响应时间83 sleepTime := time.Duration(rand.Uint64()%80+10) * time.Millisecond84 time.Sleep(sleepTime)85 fmt.Fprintf(w, "不熔断")86 e.Exit()87 }88}xxxxxxxxxx771package main2
3import (4 "fmt"5 "log"6 "math/rand"7 "net/http"8 "time"9
10 sentinel "github.com/alibaba/sentinel-golang/api"11 "github.com/alibaba/sentinel-golang/core/config"12 "github.com/alibaba/sentinel-golang/core/hotspot"13 "github.com/alibaba/sentinel-golang/logging"14)15
16func init() {17 // 初始化 sentinel18 conf := config.NewDefaultConfig()19 conf.Sentinel.Log.Logger = logging.NewConsoleLogger()20 err := sentinel.InitWithConfig(conf)21 if err != nil {22 logging.Error(err, "fail")23 return24 }25 logging.ResetGlobalLoggerLevel(logging.DebugLevel)26
27 // 创建并发规则:资源名 testHotSpotParamsQpsRejectRule 并发请求数528 _, err = hotspot.LoadRules([]*hotspot.Rule{29 {30 Resource: "testHotSpotParamsQpsRejectRule", // 埋点资源名31 MetricType: hotspot.QPS, // qps-请求量,Concurrency-并发32 ControlBehavior: hotspot.Reject, // 控制行为:Reject-拒绝,Throttling-匀速限流33 ParamIndex: 1, // 上下文参数切片中的索引34 Threshold: 5, // 阈值,数值代表请求量或并发数35 BurstCount: 0, // 沉默计数,QPS和Reject时有用36 DurationInSec: 1, // 统计间隔,QPS时有用37 },38 })39 if err != nil {40 log.Fatalf("Unexpected error: %+v", err)41 return42 }43}44
45func main() {46 server := http.Server{47 Addr: "127.0.0.1:8080",48 }49
50 http.HandleFunc("/test_hot_spot_params_qps_reject_rule", testHotSpotParamsQpsRejectRule)51 server.ListenAndServe()52}53
54// 测试热点防护规则55func testHotSpotParamsQpsRejectRule(w http.ResponseWriter, r *http.Request) {56 resourceName := "testHotSpotParamsQpsRejectRule"57 ch := make(chan struct{})58 for i := 0; i < 10; i++ { // 利用循环goroutine并发59 go func() {60 for j := 0; j < 10; j++ {61 // 埋点62 e, b := sentinel.Entry(resourceName, sentinel.WithArgs(true, rand.Uint32()%30, "sentinel"))63 if b != nil {64 sleepTime := time.Duration(rand.Uint64()%20) * time.Millisecond65 time.Sleep(sleepTime)66 fmt.Fprintf(w, "[HotSpot Reject] Blocked reason %v %v %v %v %v\n", b.BlockType().String(), "rule", b.TriggeredRule(), "snapshot", b.TriggeredValue())67 } else {68 sleepTime := time.Duration(rand.Uint64()%20) * time.Millisecond69 time.Sleep(sleepTime)70 fmt.Fprintf(w, "[HotSpot Reject] Passed\n")71 e.Exit()72 }73 }74 }()75 }76 <-ch77}
1、初始化
使用sentinel,需要在程序启动时对sentinel进行相关初始化配置。
2、配置规则
根据业务场景的要求来配置相应的规则。
3、埋点
通过sentinel的Entry()方法,将业务逻辑封装起来,也就是“埋点”。每个埋点都需要指定规则的资源名称(resource),代表触发了这个规则的调用或访问。