SpringCloud-9-服务容错

9.1 服务容错和Hystrix

  1. 1.雪崩效应:在微服务架构中,通常会有多个服务层调用,如果某个服务不可用,导致几连故障,造成整个系统不可用的情况。

    如上所示:服务A调用服务B,服务B调用服务C。当服务C发生故障时候,服务B调用不通服务C,则B发生故障,由于服务A也调用服务B,服务A调用不通服务B时候,会采取多次重连,同步等待会造成资源耗尽,则服务A也故障。最后都不可用。

  2. 2.SpringCloud家族中防雪崩的利器就是:SpringCloud Hystrix,它是基于Netflix的开源框架(Hystrix的中文意思是豪猪:defend you app);Hystrix目的就是给微服务提供一系列服务容错保护机制;还记得Eureka的意思吗?Eureka就是找到了,也就是服务注册中心

  3. 3.Spring Cloud Hystrix具有如下功能:
    a.服务降级 b.依赖隔离 c.服务熔断 d.监控(Hystrix Dashboard)

9.1.1 服务降级

比如双十一时候,刷新网站时候会出现”哎呦喂,被挤爆了”,还有秒杀的时候,某些app提示网络开小差,请稍后再试,诸如此类问题,服务降级知道思想是要区分业务,具体是:

  1. 1.优先核心服务,非核心服务不可用或弱可用。
    (比如在订单,商品微服务中:买家查询是核心服务,卖家查询是弱可用服务,我们需要优先保证买家查询服务)
  2. 2.在应用层面,使用服务降级很简单,通过HystrixCommand注解制定。在fallbackMethod(回退函数)中具体实现降级逻辑。

9.2 触发降级

在order服务中有一个create接口调用商品服务代码块

我们将其拎出来

我们单独在order服务的controller包中创建一个:HystrixController类,然后在里面使用restTemplate

然后通过浏览器请求:

我们此时关掉product服务,然后再次请求:发现连接被拒绝:

9.2.1服务降级应用搭建

我们这个时候使用Hystrix用作服务降级,然后就是老套路了

  1. 1.引入依赖:(order-server的pom.xml文件中)
1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>

然后刷新一下:maven

  1. 2.在启动类中加入注解:@EnableCircuitBreaker
    此时我们发现在启动类上:OrderApplication有很多注解,这个时候,我们再看一个注解:@SpringCloudApplication
1
2
3
4
5
6
7
8
9
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}

其实SpringCloudApplication可以替换上面 3个注解

  1. 3.在请求的HystrixController类的方法中加入注解:

浏览器测试输入:

当被调用的server端不能正常提供服务时候,我们换一个思路,被请求的服务不是client端调用server端的代码,而是抛出一个异常:


以上情况说明:降级不一定用在这种场景:被调用的server端不能正常提供服务了,触发降级,我们也可以用在我们自己服务内部来触发降级(比如:自己服务内部抛出异常,内部服务并发数太高,或者数据库连接数太多时候,我们抛出一个异常)

9.2 超时设置

以上就调用了一个fallback方法,那么如果我们有很多业务逻辑都需要处理的话,是不是都需要降级呢?这样写会不会很麻烦,其实SpringCloud提供了一个在类型注解默认的fallback方法:

1
@DefaultProperties(defaultFallback = "defaultFallback")

联系之前我们的介绍,比如我们上面getProductInfoList方法是我们核心业务的方法,比如就是买家查询商品时候,我们需要优先保证其正常使用,使用注解,自定义特殊的降级处理逻辑fallback,但是此时fallback中50%的概率是可以正常处理的,其他方法是非核心业务,统一的降级策略就是直接回到了一个静态页面之类。更多的场景其实要注意服务的时间,也就是超时的问题。比如我们在product服务中根据商品id查询商品时候,我们让其睡眠2秒。

我们在浏览器访问order服务获取商品接口:

其实我们可以在HystrixCommand里面设置超时时间。
我们进入源码发现其配置的默认超时时间是1秒
点击注解:@HystrixProperty 再点击:package com.netflix.hystrix.contrib.javanica.annotation;然后我们看到左边的源码:HystrixCommandProperties属性配置

默认1秒,所以控制台请求响应一般为1秒左右,那我们该如何配置呢?在类中搜索一下:default_executionTimeoutInMilliseconds

然后我们设置getProductInfoList个超时时间为4秒

然后我们在浏览器请求:

超时时间配置很重要,具体超时时间设置需要看调用方和具体业务,比如有些场景请求到第三方:开发、充值等需要将超时时间设置为长一些。

9.4 依赖隔离

  1. 1.线程池隔离(依赖隔离)
    SpringCloud Hystrix的依赖隔离类似于docker的”舱壁模式”;docker通过”舱壁模式”实现进程隔离,使得容器之间互不影响,而Hystrix使用该模式实现:“线程池隔离”;会为每一个HystrixCommand创建一个独立线程池,这样就算某个在Hystrix包装下的依赖服务出现延迟过高情况,也只是对该依赖服务的调用产生影响,并不会拖慢其他服务,使用HystrixCommand来将某个函数包装成了Hystrix命令时候,Hystrix框架自动地为这个函数实现了依赖隔离。所以依赖隔离,服务降级在使用时候都是一体化实现的,这样就可以实现服务容错保护。在编程模型上就会非常方便。

  2. 2.服务熔断:除了依赖隔离,服务降级之外,Hystrix还有另一一个重要元素:服务熔断。

9.4.1 服务熔断

  1. 1.服务熔断还是使用@HystrixCommand注解:
    我们如何配置了,我们还是打开HystrixCommandProperties类:

我们将上面注解拷贝出来,使用在Order服务的getProductInfoList上

然后我们在浏览器测试:http://localhost:8082/getProductInfoList
发现其一直返回”默认提示:太拥挤了,请稍后再试~~~”

此时发现一直返回错误,我们该如何进行测试呢?我们加一个请求参数,如果number为偶数直接返回success,否则调用product服务;注意此时调用product服务肯定会进入服务降级回调,原因是因为上面execution.isolation.thread.timeoutInMilliseconds已经注释掉了并且product服务里面线程等待为2秒,并且execution.isolation.thread.timeoutInMilliseconds默认等待时间是1秒。

  1. 2.产生熔断:我们不停地访问:http://localhost:8082/getProductInfoList?number=1让其错误率达到60%

  1. 3.服务熔断:
    circuitBreaker.requestVolumeThreshold
    circuitBreaker.sleepWindowInMilliseconds
    circuitBreaker.errorThresholdPercentage

这是我们上面用到的3个配置,3个配置前面都有一个共同的词:circuitBreaker
a.circuitBreaker:断路器(当某个服务发生故障,类似用电器发生短路,通过断路器的故障监控,就类似于熔断的保险丝,直接切断原来的主逻辑调用)
b.微服务和分布式里面容错必须是要考虑的,通常做法有两种:
1)重试机制:对于预期的短暂的故障问题,可以重试解决的
2)断路器模式:对于更长时间的故障问题,不断重试也是没有意义的,这个时候就可以使用断路器模式。

断路器模式是将受保护的服务封装在一个可监控的断路器对象里面,当故障到达一定的值,断路器将会跳闸,断路器对象返回错误。如下所示:

上图描述了断路器模式的3中状态积:
1)close:熔断器的关闭状态,调用失败次数累计到一定阈值/比例时候,就会启动熔断机制。
2)open:熔断器的打开状态,对服务直接返回错误,但是有一个时钟,到达这个时钟之后,会进入半熔断状态,也就是half open,允许定量的服务请求,如果调用成功到一定比例就认为恢复了,就会关闭熔断器。否则,认为还没有好,又回到熔断器打开状态。
3):half open:

  1. 4.参数含义:
    circuitBreaker.sleepWindowInMilliseconds:这里面的Window很多地方会翻译成时间窗口,断路器确定是否需要断开统计一些请求和错误数据的时候,是有一个时间范围的,这个时间范围就被称为时间窗口;当断路器打开对主逻辑进行熔断之后,Hystrix会启动一个休眠时间窗,在这个时间窗内降级逻辑临时成为主逻辑,当休眠时间窗到期,断路器将进为半开状态,释放一些请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果此次请求依然有问题,断路器将继续进入打开状态,休眠时间窗重新计时。我们上面将此参数设置为10000ms。
    circuitBreaker.requestVolumeThreshold :设置在滚动窗中,断路器的最小请求数
    circuitBreaker.errorThresholdPercentage:设置断路器打开的错误百分比条件,上面设置的是60,表示在滚动时间窗口中,如果发生了10次调用,有7次异常超过60%,此时断路器设置为打开状态,否则设置为关闭状态。

9.5 使用配置项

我们先注释掉所有配置:

之前的关于断路器的配置都是在代码里面配置的,如下图所示,不利于后期的维护
我们能把上面的配置写到配置文件中去吗?我们先写到本地文件中的yml格式
现在要改成用配置文件的方式进行,我们针对超时时间来做一个配置,
Hystrix全局超时时间配置如下图所示,表示3秒

有时候我们发现我们配置不起作用,服务器不触发降级,原因是我们没有在方法上添加@HystrixCommand注解

加上之后就可以了。上面是配置的全局的default配置,如果我们想给某一个类单独配置:在方法上注解:@HystrixComman然后配置属性里面的comandKey,然后再配置文件中加上。

9.6 feign-hystrix的使用

feign如何搭配hystrix,我们先看一下feign依赖了哪些组件?

再按:Ctrl+F

说明feign已经依赖了hystrix,所以使用按照如下方式:

  1. 1.order服务添加依赖

  1. 2.product服务在client端改造:@FeignClient注解属性里面添加fallback
    a.在fallback填写ProductClientFallback.class类。
    b.书写内部静态类并且实现ProductClient接口。
    c.内部静态类需要添加@Component注解。
    d.将此包打出去:在Terminal中输入:mvn install
    此时我们进入order服务里面OrderServiceImpl里面的productClient,然后点击进去:发现就有了。

启动order服务,我们发现报错了:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘orderServiceImpl’: Unsatisfied dependency expressed through field ‘productClient’; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘com.yxm.product.client.ProductClient’: FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: No fallback instance of type class com.yxm.product.client.ProductClient$ProductClientFallback found for feign client product

以上说明orderServiceImpl里面引入的com.yxm.product.client.ProductClient$ProductClientFallbac没有实例化。

原因:以上order服务引入productClient时候,已经把其当做order服务代码的一部分引入,productClient已经是order服务里面的一块代码了,所以运行的时候是在order服务中运行的,为什么不起作用了,原因是我们的启动类中没有扫描到。

明显和com.yxm.product.client.ProductClient不一样了,所以我们需要把扫描扩大:

然后启动正常。

使用post下单(此时我们的product服务没有启动)

以上说明我们已经达到了我们的要求。

9.7 hystrix-dashboard使用

hystrix-dashboard是进行可视化的一个组件,使用老套路

  1. 1.引入依赖
1
2
3
4
5
6
7
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
</dependency>
```
有时候一些网站博客里面可能需要让我们再引入:

org.springframework.boot

spring-boot-starter-actuator

```

但是其实我们这里不需要再次引入了:

有时候我们会遇到@EnableHystrixDashboard注解始终加不上问题,我们统一下SpringBoot和SpringCloud版本。
SpringBoot:2.1.8.RELEASE
SpringCloud:Greenwich.SR3

  1. 2.启动类加注解

  1. 3.浏览器访问:
    http://localhost:8082/hystrix
    a.最上面输入框:填写应用地址
    b.填写应用的3种方式,我们这里是Single Hystrix App所以使用:https://hystrix-app:port/actuator/hystrix.stream
    Cluster via Turbine (default cluster): https://turbine-hostname:port/turbine.stream
    Cluster via Turbine (custom cluster): https://turbine-hostname:port/turbine.stream?cluster=[clusterName]
    Single Hystrix App: https://hystrix-app:port/actuator/hystrix.stream

我们点击Monitor Stream进去后进入:

Unable to connect to Command Metric Stream.
查看控制台发现:java.lang.IllegalArgumentException: Invalid character found in method name. HTTP method names must be tokens
参考:
https://blog.csdn.net/HonsonNgai/article/details/89300789

如果一直loading,我无意中的测试,发现这个Loading…是一直在等待负载均衡的提供方要去消费服务,即访问负载均衡服务器,去调用客户端,如果有数据响应则监控界面就会有图形数据展示:

如果想让图中的数据发生变化,则需要循环多次的去访问负载均衡的提供方,让其消费服务,以至于达到监控的目的。
http://localhost:8082/getProductInfoList?number=1

a.上面颜色小圈对应的就是:Success | Short-Circuited | Bad Request | Timeout | Rejected | Failure | Error
b.Circuit Closed表明已经是熔断了
c.中间有一个圆;圆越大说明流量就越大;圆颜色偏向于红色,表示服务越不健康。线条表示流量的相对变化。
d.最上面百分比是错误率;下面两个百分比是请求的频率。

在HystrixController中打开之前设置的参数,然后请求:

有时候,我们如果把熔断百分比设置比较大的话,我们一次次请求是很耗费时间的,其实postman提供了一个多次发送的功能:

毕业于<br>相信技术可以改变人与人之间的生活<br>码农一枚