概念

接口限流

与接口限流不同的是,熔断降级在调用端使用。而接口限流是在被调用端做的限制。

保证微服务稳定的三大利器,缓存 限流 熔断降级

熔断

在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进。但是,一个应用可能会有多个微服务组成,微服务之间的数据交互通过远程过程调用完成。这就带来一个问题,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务。如果调用链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。

熔断机制是应对雪崩效应的一种微服务链路保护机制。

服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。

熔段解决如下几个问题:

  • 当所依赖的对象不稳定时,能够起到快速失败的目的;
  • 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复

降级

服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况

在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。

例如:当双11活动时,把无关交易的服务统统降级,如查看蚂蚁深林,查看历史订单,商品历史评论,只显示最后100条等等。

熔断和降级的区别

相同点:

  • 目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
  • 最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
  • 粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
  • 自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;

区别:

  • 触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
  • 管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
  • 实现方式不太一样;服务降级具有代码侵入性(由控制器完成/或自动降级),熔断一般称为自我熔断。

主流框架

  • Hystrix
  • Sentinel

hystrix

异步

hystrix.ConfigureCommand("get_baidu", hystrix.CommandConfig{
        Timeout:               100,
        MaxConcurrentRequests: 100,
        ErrorPercentThreshold: 1,
    })
    output := make(chan bool, 0)
    hystrix.Go("get_baidu", func() error {
        _, err := http.Get("https://www.baidu.com/")

        output <- true
        if err != nil {
            fmt.Println("get error")
            return err
        }
        return nil
    }, func(err error) error {
        fmt.Println("get an error, handle it", err)
        output <- false
        return nil
    })

    select {
    case out := <-output:
        fmt.Println(out)
    }

同步 + gin

func main() {
    r := router()
    r.Run()
}

func router() *gin.Engine {
    r := gin.New()
    r.Use(BreakerWrapper)
    r.Use(gin.Recovery())
    v1 := r.Group("/v1")
    {
        v1.GET("/panic", Panic)
    }
    return r
}

func Panic(c *gin.Context) {
    c.JSON(200, gin.H{"message": "this is panic!"})
}

func BreakerWrapper(c *gin.Context) {
    name := c.Request.Method + "-" + c.Request.RequestURI
    hystrix.ConfigureCommand(name, hystrix.CommandConfig{
        Timeout:               1000,
        MaxConcurrentRequests: 100,
        ErrorPercentThreshold: 1,
    })
    hystrix.Do(name, func() error {
        c.Next()
        statusCode := c.Writer.Status()
        if statusCode >= http.StatusInternalServerError {
            str := fmt.Sprintf("status code %d", statusCode)
            return errors.New(str)
        }
        return nil
    }, func(err error) error {
        if err == hystrix.ErrCircuitOpen {
            c.String(http.StatusAccepted, "请稍后重试")
        }
        return err
    })
}
Scroll to Top