概念
与接口限流不同的是,熔断降级在调用端使用。而接口限流是在被调用端做的限制。
保证微服务稳定的三大利器,缓存
限流
熔断降级
熔断
在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进。但是,一个应用可能会有多个微服务组成,微服务之间的数据交互通过远程过程调用完成。这就带来一个问题,假设微服务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
})
}