前言:
高稳定性和高可用一直是系统服务追求的终极目标,一个系统如果连最基本的稳定性都无法保障,就不太可能获得用户的认可。随着微服务的流行,服务和服务之间的稳定性变得越来越重要。本文结合自己在实践过程中,基于sentinel来实现grpc服务之间的限流和熔断功能。
理论知识:
Sentinel 是阿里开源的一款面向分布式服务架构的流量控制组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护等多个维度来帮助您保障微服务的稳定性。它目前java,golang,c , nodejs等多种语言。
资源:是 Sentinel 的关键概念。它可以是程序中的任何内容,只要通过 Sentinel API 定义的代码块,就是资源,能够被 Sentinel 保护起来。
规则:围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则、系统保护规则以及用户自己扩展的规则。所有规则都可以动态实时调整。
实践:
为了防止突发流量导致后端服务雪崩,需要对超出服务承受的流量进行主动丢弃,通过限制多余的流量,来保证后端服务的稳定性。当我们自身服务依赖的第三方服务出现问题时,我们需要及时发现并且进行熔断。
设计思路:
- 支持配置化,新增限流或者熔断时通过该配置和尽量少的修改代码来实现
- 发生限流或熔断时能够及时发现与告警
我目前使用 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
}
效果:
以下是我们项目中触发熔断时的告警信息
后记:
目前熔断限流功能已经在我们项目中取得了良好的效果,他们是保证系统稳定性不可或缺的利器。