浅谈微服务Spring Cloud:服务雪崩、降级与熔断 | 训练营第三期

836 阅读8分钟

本文已参与掘金创作者训练营第三期,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

📖前言

马尔克斯在《百年孤独》里写道:

“过去都是假的,回忆是一条没有归途的路。 以往的一切春天都无法复原,即使最狂乱且坚韧的爱情,归根结底也不过是一种转瞬即逝的现实,唯有孤独永恒。”

我要先说明一下,我有很长一段时间将服务降级和服务熔断混在一起,认为是一回事!


✨为什么我会有这样的误解呢?

在这里插入图片描述

Service A 调用 Service B,失败多次达到一定阀值,Service A 不会再去调 Service B,而会去执行本地的降级方法! 对于这么一套机制:在 Spring Cloud 中结合 Hystrix ,将其称为 熔断降级!

毕竟 熔断降级 是一起发生的,而且这二者的概念太相近了!大家如果有不同意见,请轻喷!


🚀服务雪崩

雪崩 是什么?一旦出现就是无法阻拦的大面积的并且是一种 灾难

举个栗子

有三个人他们这样说,相互之间又相互依赖从A->C!

我:"我是ServiceA!"

你:"我是ServiceB!"

他:"我是ServiceC!"

突然,Service A 的流量波动很大,流量经常会突然增加!那么在这种情况下,就算 Service A 能扛得住请求,Service BService C 未必能扛得住这突发的请求。(因为他们不一样不一样!皮一下)

突然,Service C 因为抗不住请求,变得不可用了。当然 Service B 的请求也会阻塞,慢慢耗尽 Service B 的线程资源,Service B 也会变得不可用。紧接着,Service A 也会不可用,

image.png

画一个逗比的——服务雪崩请求时序图(想学怎么用markdown画图可以留言评论)

sequenceDiagram
	Title: 服务雪崩请求时序图
	
	participant ServiceA
  	participant ServiceB
    participant ServiceC
    
     ServiceA->>ServiceB:服务A发起请求
     ServiceB->>ServiceC:服务B发起请求
     Note right of ServiceC:服务C发现自己快扛不住了,挂掉了
     ServiceC->>ServiceB:无法响应并返回数据
     ServiceB->>ServiceA:无法响应并返回数据
     ServiceA->>ServiceB:服务B继续重试
     ServiceB->>ServiceC:服务B继续重试
     ServiceC->>ServiceB:无法响应并返回数据
     ServiceB->>ServiceA:无法响应并返回数据
     ServiceB->>ServiceC:服务B继续重试
     ServiceC->>ServiceB:无法响应并返回数据
     ServiceA->>ServiceB:服务A继续重试
     ServiceB->>ServiceC:服务B继续重试
     Note right of ServiceB:服务B多次重试,发现自己也抗不住了,挂掉了
     ServiceB->>ServiceA:无法响应并返回数据
     ServiceA->>ServiceB:服务A继续重试
     ServiceB->>ServiceA:无法响应并返回数据
     ServiceA->>ServiceB:服务A继续重试
     ServiceB->>ServiceA:无法响应并返回数据
     Note right of ServiceA:重试了多次发现自己也抗不住了,挂掉了
     Note right of ServiceC:至此一个人(服务)的失败,导致整条产业链的服务都失败的状况,我们称之为服务雪崩。

一个人(服务)的失败,导致整条产业链的服务都失败的状况,我们称之为服务雪崩。


💕服务熔断和服务降级就可以视为解决服务雪崩的手段之一。


🚀服务熔断

那么,什么是 服务熔断 呢?

服务熔断:当下游的服务因为某种原因突然变得不可用或响应过慢,上游服务为了保证自己整体服务的可用性,不再继续调用目标服务,直接返回,快速释放资源。如果目标服务情况好转则恢复调用。

需要说明的是熔断其实是一个框架级的处理,那么这套熔断机制的设计,基本上业内用的是断路器模式,如Martin Fowler提供的状态转换图如下所示

在这里插入图片描述

  • 最开始处于 closed 状态,一旦检测到错误到达一定 阈值,便转为 open 状态;
  • 这时候会有个 reset timeout,到了这个时间了,会转移到 half open 状态;
  • 尝试放行一部分请求到后端,一旦检测成功便回归到 closed 状态,即恢复服务;

业内目前流行的熔断器很多,例如阿里出的 Sentinel,以及最多人使用的 Hystrix(博主自己手搓的一个小微服务项目用的就是这个)

Hystrix 中,对应配置如下

    //滑动窗口的大小,默认为20
    circuitBreaker.requestVolumeThreshold 
    //过多长时间,熔断器再次检测是否开启,默认为5000,即5s钟
    circuitBreaker.sleepWindowInMilliseconds 
    //错误率,默认50%
    circuitBreaker.errorThresholdPercentage

每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。

这些属于框架层级的实现,我们只要实现对应接口就好!是不是很简单!


🙌服务降级

那么,什么是 服务降级 呢?

这里有两种场景:

  • 当下游的服务因为某种原因响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,增加响应速度!
  • 当下游的服务因为某种原因不可用,上游主动调用本地的一些降级逻辑,避免卡顿,迅速返回给用户!

乍看之下,很多人还是不懂 熔断降级 的区别!

应该要这么理解:

  • 服务降级有很多种降级方式!如开关降级、限流降级、熔断降级!
  • 服务熔断属于降级方式的一种!

这是有的人就会不服了,觉得熔断是熔断、降级是降级,分明是两回事!其实不然,因为从实现上来说,熔断和降级必定是一起出现。因为当发生下游服务不可用的情况,这个时候为了对最终用户负责,就需要进入上游的降级逻辑了。因此,将熔断降级视为降级方式的一种,也是可以滴!

撇开框架,以最简单的代码来说明!上游代码如下

    try{
        //调用下游的helloWorld服务
        xxRpc.helloWorld();
    }catch(Exception e){
        //因为熔断,所以调不通
        doSomething();
    }

注意看,下游的 helloWorld 服务因为熔断而调不通。此时上游服务就会进入 catch 里头的代码块,那么 catch 里头执行的逻辑,你就可以理解为降级逻辑!

什么,你跟我说你不捕捉异常,直接丢页面?

那我甘拜下风!

服务降级大多是属于一种业务级别的处理。这里要讲的是另一种降级方式,也就是开关降级!这也是我们生产上常用的另一种降级方式!

做法很简单,做个开关,然后将开关放配置中心(数据字典也是可以的)!在配置中心更改开关,决定哪些服务进行降级。至于配置变动后,应用怎么监控到配置发生了变动,这就不是本文该讨论的范围。

在应用程序中部下开关的这个过程,业内也有一个名词,称为埋点!


接下来最关键的一个问题,哪些业务需要 埋点

  1. 简化执行流程 自己梳理出核心业务流程和非核心业务流程。然后在非核心业务流程上加上开关,一旦发现系统扛不住,关掉开关,结束这些次要流程。

  2. 关闭次要功能 一个微服务下肯定有很多功能,那自己区分出主要功能和次要功能。然后次要功能加上开关,需要降级的时候,把次要功能关了吧!

  3. 降低一致性 假设,你在业务上发现执行流程没法简化了,愁啊!也没啥次要功能可以关了,桑心啊!那只能降低一致性了,即将核心业务流程的同步改异步,将强一致性改最终一致性!

可是这些都是手动降级,有办法自动降级么?

一般需要降级的场景,都是可以预见的,例如某某活动。假设,平时真的有突发事件,流量异常,也有监控系统发邮件通知,提醒我们去降级!

但是,这并不代表自动降级不能做,因为我在生产上没实践过,只是理论,如果让做自动降级我能想到的:

  1. 自己设一个阈值,例如几秒内失败多少次,就启动降级

  2. 自己做接口监控(有兴趣的可以了解一下 Rxjava ),达到阈值就走推送逻辑。怎么推呢?比如你配置是放在 Git 上,就用Jgit去改配置中心的配置。如果配置放数据库,就用 Jdbc 去改。

  3. 改完配置中心的配置后,应用就可以自动检测到配置的变化,进行降级!(这句不了解的,了解一下配置中心的热刷新功能)

  4. 通过服务发现+健康检查+异常跟踪处理的,服务发现用 consul,异常跟踪会调整服务节点的权重,进而调整流量。(阈值参数 不是很好控制需要根据业务去进行测试,当然这不是我需要关注的哈哈!)首先对部署节点 流量降级,如果还不行就 熔断降级然后每隔一段时间放一些流量,如果没问题就把浏览慢慢加上去,应该可以实现全程自动。


👏小问题

配置变动后,应用怎么监控配置?

  • 第一种是长轮询,就是一直问,配置变了没啊,变了没啊,实效性快。

  • 第二种消息总线(SpringCloud Bus)的方式,实效慢,依赖消息队列(博主会在项目中使用 RabbitMq 后续慢慢更新)。


🎉最后

  • 更多参考精彩博文请看这里:陈永佳的博客

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!