熔断限流原理与实践

2020-12-08 20:04:57 浏览数 (1)

前言:

高稳定性和高可用一直是系统服务追求的终极目标,一个系统如果连最基本的稳定性都无法保障,就不太可能获得用户的认可。随着微服务的流行,服务和服务之间的稳定性变得越来越重要。本文结合自己在实践过程中,基于sentinel来实现grpc服务之间的限流和熔断功能。

理论知识:

Sentinel 是阿里开源的一款面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。它目前java,golang,c , nodejs等多种语言。

资源:是 Sentinel 的关键概念。它可以是程序中的任何内容,只要通过 Sentinel API 定义的代码块,就是资源,能够被 Sentinel 保护起来。

规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则、系统保护规则以及用户自己扩展的规则。所有规则都可以动态实时调整。

实践:

为了防止突发流量导致后端服务雪崩,需要对超出服务承受的流量进行主动丢弃,通过限制多余的流量,来保证后端服务的稳定性。当我们自身服务依赖的第三方服务出现问题时,我们需要及时发现并且进行熔断。

设计思路:

  1. 支持配置化,新增限流或者熔断时通过该配置和尽量少的修改代码来实现
  2. 发生限流或熔断时能够及时发现与告警

我目前使用 Sentinel 的版本为:v1.0.0-M1

配置设计:

这样设计的好处能够最大的保留原生配置,根据自己需要进行任意配置,同时相同策略的接口或者资源可以共享一套配置,这样也能够减少配置项。

代码语言:txt复制
import (
	"github.com/alibaba/sentinel-golang/core/circuitbreaker"
	"github.com/alibaba/sentinel-golang/core/flow"
)

// FlowRule
type FlowRule struct {
	FlowResources []string  `json:"flowResources"`
	FlowRule      flow.Rule `json:"flowRule"`
}

// CbRule
type CbRule struct {
	CbResources []string            `json:"cbResources"`
	CbRule      circuitbreaker.Rule `json:"cbRule"`
}

// Conf
type Conf struct {
	FlowRules []FlowRule `json:"flowRules"`
	CbRules   []CbRule   `json:"cbRules"`
}

核心功能封装:

代码语言:txt复制
import (
	"encoding/json"
	"fmt"

	sentinelapi "github.com/alibaba/sentinel-golang/api"
	"github.com/alibaba/sentinel-golang/core/base"
	"github.com/alibaba/sentinel-golang/core/circuitbreaker"
	"github.com/alibaba/sentinel-golang/core/config"
	"github.com/alibaba/sentinel-golang/core/flow"
)

// cbStateChangeListener
type cbStateChangeListener struct {
}

// 熔断关闭
func (s *cbStateChangeListener) OnTransformToClosed(prev circuitbreaker.State, rule circuitbreaker.Rule) {
	dd, _ := json.Marshal(rule)
	msg := fmt.Sprintf("资源名:%s,n熔断状态:由 %s 变成 Closed,n规则:%sn",
		rule.Resource, prev.String(), string(dd))
	// 这里增加相应的监控告警
	println(msg)
}

// 熔断打开
func (s *cbStateChangeListener) OnTransformToOpen(prev circuitbreaker.State,
	rule circuitbreaker.Rule, snapshot interface{}) {
	dd, _ := json.Marshal(rule)
	msg := fmt.Sprintf("资源名:%s,n熔断状态:由 %s 变成 Open,n规则:%s,nsnapshot: %.2fn",
		rule.Resource, prev.String(), string(dd), snapshot)
	// 这里增加相应的监控告警
	println(msg)
}

// 熔断半开
func (s *cbStateChangeListener) OnTransformToHalfOpen(prev circuitbreaker.State, rule circuitbreaker.Rule) {
	dd, _ := json.Marshal(rule)
	msg := fmt.Sprintf("资源名:%s,n熔断状态:由 %s 变成 Half-Open,n规则:%sn",
		rule.Resource, prev.String(), string(dd))
	// 这里增加相应的监控告警
	println(msg)
}

// InitSentinel
func InitSentinel(confMap Conf) error {

	defaultConf := config.NewDefaultConfig()
	defaultConf.Sentinel.Log.Dir = "./log/sentinel"
	err := sentinelapi.InitWithConfig(defaultConf)
	if err != nil {
		return err
	}

	// 配置限流
	if len(confMap.FlowRules) > 0 {
		var rs = make([]*flow.Rule, 0)
		for _, rule := range confMap.FlowRules {
			for _, res := range rule.FlowResources {
				var r = rule.FlowRule
				r.Resource = res
				rs = append(rs, &r)
			}
		}
		_, err = flow.LoadRules(rs)
		if err != nil {
			println(err.Error())
		}
	}

	// 配置熔断
	circuitbreaker.RegisterStateChangeListeners(&cbStateChangeListener{})
	if len(confMap.CbRules) > 0 {
		var rs = make([]*circuitbreaker.Rule, 0)
		for _, rule := range confMap.CbRules {
			for _, res := range rule.CbResources {
				var r = rule.CbRule
				r.Resource = res
				rs = append(rs, &r)
			}
		}
		_, err = circuitbreaker.LoadRules(rs)
		if err != nil {
			println(err.Error())
		}
	}
	return nil
}

效果:

以下是我们项目中触发熔断时的告警信息

Closed 变成 OpenClosed 变成 Open
 Open 变成 Half-Open Open 变成 Half-Open
HalfOpen 变成 ClosedHalfOpen 变成 Closed

后记:

目前熔断限流功能已经在我们项目中取得了良好的效果,他们是保证系统稳定性不可或缺的利器。

0 人点赞