介绍一下降级和熔断的概念
什么是降级呢? 降级意味着多种方案, 当系统出现问题的时候, 你有一个备选方案可以马上切换, 比如有一个接口的功能是实时预测未来一个月某个商品的采购数量, 突然间依赖的上游系统出现问题了, 那么我们的接口就完全不可用了吗? 显然这是不应该的, 这时我接口就可以降级, 返回昨天实时计算出来的结果, 虽然准确性可能差一点, 但系统能够正常运转, 降级也分为自动降级和手动降级, 前者是系统自动检测到问题时自动切换, 后者是系统检测到问题报警, 人为的切换, 降级代表着系统相比降级之前其功能表现不如之前的完美(这个具体体现在功能准确性, 可用性上等, 如上面接口的例子)
什么是熔断呢? 通俗来讲, 熔断指的是遇到危险了, 必须马上停掉, 比如生活中的电流过大, 必须马上切断, 否则就发生了火灾了, 熔断之后就会导致断电, 完全不可用, 在一个系统中, 假设一个接口部署了 10 台机器(分布式), 突然某一台机器的接口调用情况正确率降到 90%, 那么这台机器肯定出现问题了, 这个时候就需要熔断这台机器, 把这台机器从整个集群中摘掉, 从而保证用户的请求 100% 的正确, 再比如, 一个系统中有很多功能, 这些功能有些是核心功能, 有些是非核心功能, 那么在一些大促中, 我们可能熔断掉一些非核心功能, 从而保证核心功能的流转(登录和注册, 登录属于核心, 注册是属于非核心)
为什么需要降级和熔断
不管是降级还是熔断, 都是为了保证了系统的稳定性, 可用性. 降级往往代表系统功能部分不可用, 熔断代表的是完全不可用, 再举一个简单例子, 注册功能, 降级可能出现的情况是手机号可以正常, 邮箱不能注册, 而熔断出现的情况是注册功能完全不可用, 所以说有时候熔断是一种特殊的降级. 这在整个系统设计编码中都需要考虑到.
常用的降级和熔断策略
在业务系统时, 降级在编码时需要考虑好备选方案, 和业务确认方案的合理性, 熔断在编码时需要分离核心功能和非核心功能, 梳理上下游依赖关系, 防止强依赖引起的系统的雪崩, 这些是业务系统功能设计时需要经常考虑的.
所有业务系统都需要考虑的东西, 就意味着可以优化, 可以剥离, 抽象出来, 做成公共组件, 中间件, 形成通用性.
上面有提到, 降级和熔断的最终目的都是保证系统的稳定性, 可靠性, 保证核心服务可用, 那么在形成中间件时具体措施是什么呢?
降级
超时降级(调用服务时超时返回默认值或者其它处理办法)
失败次数降级(服务可用率下降时降级)
限流也是降级的一种办法
故障降级(依赖的外系统发生故障时降级)
拒绝服务降级
熔断
系统攻击熔断(当某个服务遭遇流量攻击时, 可以熔断这个服务)
涉及核心功能运行时的熔断(下单和评论功能, 关键时刻可以熔断评论功能)
不管降级还是熔断, 在设计时都要考虑: 降级熔断算法, 恢复机制, 报警. 这些是必备的, 不能系统降级了或者熔断了就无法回复之前的情况, 也不能不报警, 要不然开发人员都不知道, 这还得了.
熔断和降级的异样性
两者的目的相当
两者的最终的表现的相同
粒度一样, 大多数都是服务级别的粒度, 也有可能是方法级别的
自治性要求比较高(尽可能的智能化)
降级一般是客户端处理, 熔断是在服务端处理的
设计方案
介绍一种的常见的方案, 服务码 + 配置中心, 调用任何服务时都传入必要参数服务码和开关, 默认关闭, 当触发某种条件时可打开开关, 或者通过配置中心手动推送开关新的值, 从而保护系统不被单个服务压垮, 别看这个简单, 很多系统都是这么做的.
- 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 引入以下手段来保护系统:
资源隔离(线程池和信号量两种手段的隔离)
限流
降级
熔断(断路器)
Hystrix 如何设计实现这些手段呢?
使用命令模式将所有对外部服务 (或依赖关系) 的调用包装在 HystrixCommand 或 HystrixObservableCommand 对象中, 并将该对象放在单独的线程中执行
每一个依赖都有自己对应的线程池或者信号量, 线程池耗尽时, 拒绝请求
维护请求的各种状态(成功, 失败, 超时的次数)
当错误率到达一定阈值时, 进行熔断, 过一定的时间后又恢复
提供降级, 失败, 成功, 熔断后的回调逻辑
实时的监控指标和配置信息的修改
用代码实现一个 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
来源: https://www.cnblogs.com/sy270321/p/12675480.html