zoukankan      html  css  js  c++  java
  • 微服务-高并发情况下接口降级和熔断策略

    介绍一下降级和熔断的概念

    什么是降级呢? 降级意味着多种方案,当系统出现问题的时候,你有一个备选方案可以马上切换,比如有一个接口的功能是实时预测未来一个月某个商品的采购数量,突然间依赖的上游系统出现问题了,那么我们的接口就完全不可用了吗?显然这是不应该的,这时我接口就可以降级,返回昨天实时计算出来的结果,虽然准确性可能差一点,但系统能够正常运转,降级也分为自动降级和手动降级,前者是系统自动检测到问题时自动切换,后者是系统检测到问题报警,人为的切换,降级代表着系统相比降级之前其功能表现不如之前的完美(这个具体体现在功能准确性,可用性上等,如上面接口的例子)
    什么是熔断呢? 通俗来讲,熔断指的是遇到危险了,必须马上停掉,比如生活中的电流过大,必须马上切断,否则就发生了火灾了,熔断之后就会导致断电,完全不可用,在一个系统中,假设一个接口部署了10台机器(分布式),突然某一台机器的接口调用情况正确率降到90%,那么这台机器肯定出现问题了,这个时候就需要熔断这台机器,把这台机器从整个集群中摘掉,从而保证用户的请求100%的正确,再比如,一个系统中有很多功能,这些功能有些是核心功能,有些是非核心功能,那么在一些大促中,我们可能熔断掉一些非核心功能,从而保证核心功能的流转(登录和注册,登录属于核心,注册是属于非核心)

    为什么需要降级和熔断

    不管是降级还是熔断,都是为了保证了系统的稳定性,可用性。降级往往代表系统功能部分不可用,熔断代表的是完全不可用,再举一个简单例子,注册功能,降级可能出现的情况是手机号可以正常,邮箱不能注册,而熔断出现的情况是注册功能完全不可用,所以说有时候熔断是一种特殊的降级。这在整个系统设计编码中都需要考虑到。

    常用的降级和熔断策略

    在业务系统时,降级在编码时需要考虑好备选方案,和业务确认方案的合理性,熔断在编码时需要分离核心功能和非核心功能,梳理上下游依赖关系,防止强依赖引起的系统的雪崩,这些是业务系统功能设计时需要经常考虑的。

    所有业务系统都需要考虑的东西,就意味着可以优化,可以剥离,抽象出来,做成公共组件,中间件,形成通用性。

    上面有提到,降级和熔断的最终目的都是保证系统的稳定性,可靠性,保证核心服务可用,那么在形成中间件时具体措施是什么呢?

    降级
    1. 超时降级(调用服务时超时返回默认值或者其它处理办法)
    2. 失败次数降级(服务可用率下降时降级)
    3. 限流也是降级的一种办法
    4. 故障降级(依赖的外系统发生故障时降级)
    5. 拒绝服务降级
    熔断
    1. 系统攻击熔断(当某个服务遭遇流量攻击时,可以熔断这个服务)
    2. 涉及核心功能运行时的熔断(下单和评论功能,关键时刻可以熔断评论功能)

    不管降级还是熔断,在设计时都要考虑:降级熔断算法,恢复机制,报警。这些是必备的,不能系统降级了或者熔断了就无法回复之前的情况,也不能不报警,要不然开发人员都不知道,这还得了。

    熔断和降级的异样性
    1. 两者的目的相当
    2. 两者的最终的表现的相同
    3. 粒度一样,大多数都是服务级别的粒度,也有可能是方法级别的
    4. 自治性要求比较高(尽可能的智能化)
    5. 降级一般是客户端处理,熔断是在服务端处理的
    设计方案

    介绍一种的常见的方案,服务码+配置中心,调用任何服务时都传入必要参数服务码和开关,默认关闭,当触发某种条件时可打开开关,或者通过配置中心手动推送开关新的值,从而保护系统不被单个服务压垮,别看这个简单,很多系统都是这么做的。

    
    func  DowngradeAndFuse (ctx context.Context){
        //业务码
        bizValue := ctx.Value("bizCode")
        //熔断降级标识
        flag := ctx.Value("flag")
       
        if bizValue == "指定业务" && flag {
           //降级或者熔断
           return
        }
    }
    
    

    Hystrix的原理

    Hystrix有Java和Go版本的,Java版本的是Netflix公司开发并开源的,Go版本的是由afex(个人)创建的,代码库地址如下:

    https://github.com/Netflix/Hystrix
    https://github.com/afex/hystrix-go
    

    Hystrix引入以下手段来保护系统:

    1. 资源隔离(线程池和信号量两种手段的隔离)
    2. 限流
    3. 降级
    4. 熔断(断路器)

    Hystrix如何设计实现这些手段呢?

    1. 使用命令模式将所有对外部服务(或依赖关系)的调用包装在HystrixCommand或HystrixObservableCommand对象中,并将该对象放在单独的线程中执行
    2. 每一个依赖都有自己对应的线程池或者信号量,线程池耗尽时,拒绝请求
    3. 维护请求的各种状态(成功,失败,超时的次数)
    4. 当错误率到达一定阈值时,进行熔断,过一定的时间后又恢复
    5. 提供降级,失败,成功,熔断后的回调逻辑
    6. 实时的监控指标和配置信息的修改

    用代码实现一个hystrix-go的Demo,第一步写在init初始化中,配置hystrix的一些参数,如果不配置的话,也会有默认参数。

     func init() {
       hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{
          //多长时间 超时
          Timeout: 5000,
          //最大并发数
          MaxConcurrentRequests: 1,
          //错误百分比,错误率达到这个数值开启熔断
          ErrorPercentThreshold: 25,
          //当熔断器被打开后,SleepWindow的时间就是控制过多久后去尝试服务是否可用了(毫秒)
          SleepWindow: 10,
          //最小请求数,只有到达这个数量后才判断是否开启熔断
          RequestVolumeThreshold: 10,
       })
    }
    

    如何使用hystrix熔断呢,总的来说分为4个步骤:
    第一步:定义你调用的外部系统的服务
    第二步:设置回调函数(当超时或者熔断了会调用回调函数)
    第三步:使用hystrix的api调用第一步定义好的服务
    第四步:获取最终结果(结果可能时正确的,也可能是一个err)

    //异步调用
    func HystrixAsyStudy() {
       //第一步:
       result := make(chan string, 1)
       //定义依赖外部系统的函数
       f1 := func() error {
          // 处理业务系统(调用外部服务)
          fmt.Println("处理业务逻辑")
          result <- "处理结果"
          return nil
       }
       //第二步:
       //回调函数,只有 err不为空,才会执行回调函数(如果发生了超时,熔断,
       //限流,超时之后也会回调)
       fallBack1 := func(err error) error {
          fmt.Println("回调函数")
          return err
       }
       //第三步:
       errors := hystrix.Go("my_command", f1, fallBack1)
       //第四步:
       select {
       case r := <-result:
          fmt.Println(r)
       case e := <-errors:
          fmt.Println(e)
       }
    }
    

    Hystrix更加详细的文档参考如下地址:

    //Java版本的
    https://github.com/Netflix/Hystrix/wiki/How-To-Use#Common-Patterns-FailFast
    //Go版本的
    https://github.com/afex/hystrix-go
    
  • 相关阅读:
    列表组件抽象(5)-简洁易用的表格组件
    列表组件抽象(4)-滚动列表及分页说明
    列表组件抽象(3)-分页和排序管理说明
    列表组件抽象(2)-listViewBase说明
    列表组件抽象(1)-概述
    简单实用的进度条加载组件loader.js
    简单封装分页功能pageView.js
    为什么不能用速度与时间的关系去实现动画
    java开发面试题目及答案(持续更新)
    Java Web目前主流比较成熟的框架以及正在兴起的框架
  • 原文地址:https://www.cnblogs.com/sy270321/p/12675480.html
Copyright © 2011-2022 走看看