SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

图3.2 极端情况下的微服务网状调用示意图

最常见的场景就是当负载过高时,如果某个服务的性能或抗压能力不好,那么当请求到这个服务时就需要等待或直接出现超时、不可用等情况。在图3.2中,一旦服务C出现问题,可能会影响服务A和服务B,虽然服务D、E、F并没有直接与服务C相互依赖,但是服务C导致了服务A和服务B的阻塞,就会间接地影响服务D、E、F,从而让整个系统变得缓慢或不可用,这就是微服务的雪崩效应。

微服务调用都以远程调用为主,内存中调用和远程调用之间的一个重大区别是远程调用可能会失败,或者在达到某个超时限制之前挂起而没有响应。如果在没有响应的供应商上有许多呼叫者,那么更糟糕的是,你可能会耗尽关键资源,导致跨多个系统的级联故障。这些问题还是很容易发生的,那么应该如何避免呢?其实要避免雪崩效应,根本的问题就是如何让单个有问题的服务不去影响其他服务的正常运行,这样就能将“雪崩”的范围控制到最小,从而不会将问题的影响蔓延至整个系统。

断路器(Circuit Breaker)就是为了解决这个问题而产生的。那么什么是断路器呢?听起来就像日常生活中的保险丝或空气开关一样,很多高功率的电器、家中和生产场所中都会有电路的保险装置,这些装置一般称为断路器,断路器会在短路和严重超载的情况下切断电路,从而有效地保护回路中的电器,防止电器损坏和火灾的情况发生。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

技术有很多时候的设计往往都源于生活,微服务中的断路器其实就是效仿真实生活中的断路器,当服务过载而导致响应过慢或不可用时,断路器能及时地切断真实的服务调用,并返回提前设定好的响应,保证调用方其他功能的正常运行。

那么,如何实现一个断路器呢?你可以自己去实现一个断路器,首先需要在服务调用方监控服务的调用情况,然后设置一些阈值,如响应时间不得超过15s、错误次数比例不得超过30%等,再去定义一些调用失败后预期的返回结果,如查询一个员工的姓名,返回张三,当调用结果情况超过了设定的阈值时,断路器就改变服务的调用策略,消费者将不再调用原来的服务,当请求发起时,直接返回之前设置的另一个调用策略,通常称这种方式为服务降级策略,这样就可以实现一个断路器的基本功能了。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

下面来了解一下Spring Cloud系列的断路器:Spring Cloud Netflix Hystrix是如何使用的。Spring Cloud Netflix Hystrix是Spring Cloud关于解决微服务雪崩效应的解决方案,不过看名称可以知道,Hystrix仍然是由大名鼎鼎的Netflix开发并开源的产品,Spring Cloud很好地对它进行了封装,并且与Spring Cloud Ribbon、Spring Cloud Feign等框架可以无缝集成。

Spring Cloud Netflix Hystrix提供了断路器的全部功能,首先需要在服务调用端引入Hystrixstarter:

spring-cloud-starter-netflix-hystrix,配置如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在Spring Boot的启动类上添加@EnableCircuitBreaker注解即可,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

如果我们集成了Spring Cloud Netflix Eureka,还有一种简便的写法,可以直接使用@SpringCloudApplication注解来替代其他注解,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

通过查看@SpringCloudApplication的源码可以发现,实际上SpringCloudApplication就包括 了 @SpringBootApplication、@EnableDiscoveryClient和@EnableCircuitBreaker 3个注解,SpringCloudApplication的源码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

这样就完成了断路器的基本配置,之前提到过,断路器的开启有触发条件,通常通过设置阈值来作为断路器的触发条件,Spring Cloud Netflix Hystrix 提供了几种常用的阈值配置 。 首先由circuitBreakerEnabled的配置项来配置是否启用断路器,默认开启,但是如果使用Spring Cloud Feign作为远程调用框架,那么这里需要额外的配置来启动Hystrix,在application.yml中添加如下配置即可。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

其实,设置阈值就是和现实情况的统计做对比,如服务失败比例、最大并发数等,一旦统计的结果超过阈值,就开启断路器。既然做统计,就会有一个维度,通过观察Hystrix的源码,我们发现在Hystrix中称这个维度为Statistical Window Buckets(统计窗口时段),即采用时间的维度,默认10s,也就是10个Bucket,通过在application.yml的配置设置

metrics.rollingStats.numBuckets的值可以改变这个窗口时段。例如,想设置统计时间是20s,那么代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

需要注意的是,numBuckets的配置不能动态更新,如果修改了,只能重启服务器才能生效。断路器提供了错误百分比的阈值设置,表示错误的百分比,默认是50%。也就是说,如果我们的numBuckets是20,就表示在20s内,请求的失败比例超过50%时,断路器就开启,当然可以通过配置来修改它,具体如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

断路器还提供了最大请求数的阈值设置,表示在指定的时段内,请求数量超过了阈值,则启动断路器,这项配置主要针对高并发时服务负载过大的情况下,有效地缓解服务的压力,做到负载保护,这也是断路器的一个比较核心的功能,对应的配置如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

断路器中还有个配置,称为sleepWindowInMilliseconds,源码中的注释是seconds that we will sleep before trying again after tripping the circuit(我们将会切断线路之后在重试之前休眠的秒数),意思就是断路器一旦启动,就会熔断之前的请求线路,当新的请求进来时,则会采用一定的降级策略来处理请求,但断路器并不会一直运行,在一定时间后会进入半开启状态,即会释放一部分的请求进行重试,若重试结果正常则断路器关闭,若仍然有问题,则断路器再由半开启状态进入完全开启状态。sleepWindowInMilliseconds就是用来配置断路器开启后多长时间会进入半开启状态的,即配置断路器的工作时间,具体如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

常用的配置还有很多,如设置请求的超时时间 :

execution.isolation.thread、timeoutIn Milliseconds等,这里就不一一介绍了,其他的阈值配置大家可以通过查看GitHub的Netflix Hystrix的教程wiki,或者查看HystrixCommandProperties类的源码来学习。

服务降级

====

之前讲解了开启断路器的方法和断路器启动的阈值配置,那么断路器开启后会如何处理新到来的请求呢?最常用的就是采用服务降级的方式,即提前指定好降级方法,当断路器启动时,则调用降级方法而不再调用原来的服务,以达到服务降级、保护负载的目的。

下面来看一下在Spring Cloud Netflix Hystrix中如何做到服务降级。其实很简单,如果在服务消费者端使用的是SpringCloudRibbon,那么只需在调用服务的方法上增加@HystrixCommand注解,然后指定对应熔断后调用的方法名即可,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在上述代码中,fallbackMethod="findUserNameByIdFallback"就指定了服务被熔断时的降级方法,当断路器启动后,原来调用restTemplate的方法(findUserNameById)就不会被执行,findUserNameById 方法将直接返回findUserNameByIdFallback的执行结果(ZhangGang)。

在之前介绍过,Spring Cloud Feign是对Spring Cloud Ribbon的封装,提供了更简便的远程方法调用方式,其实Spring Cloud Feign不仅封装了Ribbon,还封装了Hystrix,因为几乎在微服务架构中每个服务调用方都需要使用断路器,Spring Cloud干脆就开发了一个集成了远 程 调 用 ( Spring Cloud Ribbon ) 和 断 路 器 ( Spring Cloud Netflix Hystrix)的框架,它就是Spring Cloud Feign。

那么,在Feign中如何使用服务降级呢?配置代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在以上代码中,Spring Cloud Feign可以直接定义类级别的fallback,也可以直接在Feign的接口注解@FeignClient 中 配 置FallbackFactory ,指定降级服务为UserFeignClientFallBack。

UserFeignClientFallBack的代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

由以上代码可以看出,fallback的类继承了FallbackFactory,实现的create方法会创建一个新的服务实现,用来代替之前的真实方法来达到降级的目的。

线程隔离

====

线程隔离又称为舱壁机制。一般为了提高船的生存能力,会将船体分为多个舱室,当船体发生事故导致进水时,只有受损的舱室会进水,其他舱室由于舱壁的隔离,并不会受到影响,从而将船体浮力的下降控制到最小。

Spring Cloud Netflix Hystrix也采用了同样的设计原理来保护微服务应用,在微服务的远程调用中,如果所有的请求都在一个线程池中,一旦有个别请求响应缓慢,这些请求可能会不断地消耗可用的资源,直至占满整个资源,其他的请求都会进入等待队列,从而拖垮整个应用。

那么,如何做到线程隔离呢?Hystrix提供了两种实现方式:线程池、信号量。

1. 线程池

线程池的做法很简单,可以将同一个请求分到同一个线程池中,使不同的请求拥有不同的线程池,这样每个请求就像有了自己的舱室,当其中一个舱室由于故障导致线程池被占满后,并不会影响其他“舱室”的请求。

Hystrix采用命令模式来完成线程池隔离线程的功能(命令模式是一种数据驱动的设计模式,也属于行为型模式)。也就是说,请求会以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适对象,并把该命令传给相应的对象,由该对象执行命令。图3.3所示为Hystrix的命令模式+线程池模式实现线程隔离的方式。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

由图3.3可知,当依赖B调用的后端服务故障时,请求可能会超时或报错,但是只会阻碍自己资源池内的请求,并不会影响依赖A和依赖C中的请求。

Hystrix中要执行Command有4种方式:execute()、queue()、observe()和toObservable()。详细说明如下。

(1)execute()是以阻塞的方式运行Command的,在执行时会先以与queue()同样的方式获得Future对象,然后调用Future的get方法,get方法会阻塞execute的执行,直到方法运行完成。

(2)queue()是以非阻塞的方式运行Command的,调用queue方法会直接返回Future对象。需要调用者使用get方法来获取Command的返回,get是阻塞式的。

(3)observe()表示立即订阅并开始执行Command,然后返回一个Observable,当subscribe到该对象时,会重新触发流程的排序和通知。

( 4 ) toObservable() 与 observe 类 似 , 返 回 Observable , 但Command不会立即执行,必须subscribe后才能真正开始执行命令的流程。

下面来看一个简单的HelloCommand,代码如下。

SpringCloud微服务架构之断路器,如何解决微服务中的雪崩效应?

在上述代码中,要执行一个hello的方法,那么首先可以定义一个HelloCommand,继承HystrixCommand,再通过构造器将参数传递到Command,然后通过设置HystrixCommand GroupKey.Factory.asKey(“HelloGroup”)的方法返回HystrixCommandGroupKey,Hystrix通过HystrixCommandGroupKey来定义线程池,当HystrixCommandGroupKey相同时,则会在同一个线程池内执行,最后通过getFallback方法来定义断路后的返回,当我们在执行execute时,run方法首先被执行,然后执行异常后,getFallback将被执行,以达到优雅的降级。

结语

小编也是很有感触,如果一直都是在中小公司,没有接触过大型的互联网架构设计的话,只靠自己看书去提升可能一辈子都很难达到高级架构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。

我们选择的这个行业就一直要持续的学习,又很吃青春饭。

虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。

送给每一位想学习Java小伙伴,用来提升自己。

在这里插入图片描述

本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!

构师的技术和认知高度。向厉害的人去学习是最有效减少时间摸索、精力浪费的方式。

我们选择的这个行业就一直要持续的学习,又很吃青春饭。

虽然大家可能经常见到说程序员年薪几十万,但这样的人毕竟不是大部份,要么是有名校光环,要么是在阿里华为这样的大企业。年龄一大,更有可能被裁。

送给每一位想学习Java小伙伴,用来提升自己。

[外链图片转存中…(img-2XGmXRC3-1714469051761)]

本文到这里就结束了,喜欢的朋友可以帮忙点赞和评论一下,感谢支持!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值