服务降级

服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”
如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

Hystrix

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,
Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类以熔断保险丝),向调用方返回一个符合
预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会
被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

使用

hystrix既可以使用在消费侧,也可以使用在服务侧
首先要导包:

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

服务降级(fallback)

立即返回一个表示服务出错的响应

-. 给服务端(生产者)进行配置

  1. 主启动类激活:
    1
    2
    @EnableCircuitBreaker
    open class PaymentHystrixMain8001
  2. 给服务类添加注解和配置:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @HystrixCommand(
    commandProperties = [
    HystrixProperty(name="execution.timeout.enabled", value="true"),
    HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000"),
    ],
    fallbackMethod = "error_handle")
    open fun paymentInfo_TimeOut(id:Int):String{
    try {
    Thread.sleep(3000)
    }catch (e:InterruptedException){
    e.printStackTrace()
    }
    return "线程池:${Thread.currentThread().name} paymentInfo_TimeOut,id:${id}\t. 耗时3秒钟"
    }
    open fun error_handle(id:Int):String{
    return "出错了呢"
    }

-. 给客户端(消费者)进行配置
首先配置yml:

1
2
3
feign:
hystrix:
enabled: true

主启动类开启注解:

1
@EnableHystrix

然后为他的controller类添加上注解和错误解决方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@HystrixCommand(
commandProperties = [
HystrixProperty(name="execution.timeout.enabled", value="true"),
HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds", value="3000"),
],
fallbackMethod = "error_handle")
open fun paymentInfo_TimeOut(id:Int):String{
try {
Thread.sleep(3000)
}catch (e:InterruptedException){
e.printStackTrace()
}
return "线程池:${Thread.currentThread().name} paymentInfo_TimeOut,id:${id}\t. 耗时3秒钟"
}
open fun error_handle(id:Int):String{
return "出错了呢"
}
解决代码膨胀问题

如果每个方法都需要一个fallback解决方法。那么就会导致代码过于繁多。不利于查看。

  1. 全局配置一个fallback方法。在需要处理的业务类上加上注解
    1
    2
    3
    @RestController
    @DefaultProperties(defaultFallback = "global_error_handle")
    open class PaymentController
    如果具体的方法中没有标明fallback方法,那么就会找全局的fallback方法。否则就选择就近的fallback方法。
    但是这样子也会面临一些代码耦合度高的问题,需要去解耦。
    常见的三个服务器问题:运行,超时,宕机
    新的解决方法。不在业务层中进行修改。而是在service层中修改。这样子可以更好的解耦合。
  2. 定义一个类实现这个service接口:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Component
    class PaymentFallbackService: PaymentService {
    override fun paymentInfo_OK(id: Int): String {
    return "paymentInfo_OK-------paymentFallbackService fallback-------"
    }

    override fun paymentInfo_TimeOut(id: Int): String {
    return "paymentInfo_TimeOut-------paymentFallbackService fallback-------"
    }
    }
  3. 然后配置yaml选项,开启配置:
    1
    2
    3
    feign:
    hystrix:
    enabled: true
  4. 设置统一的fallback。
    1
    2
    @Component
    @FeignClient("CLOUD-PROVIDER-SERVICE", fallback = PaymentFallbackService::class)
    然后测试

服务熔断(break)

即保险丝的意思
熔断机制概述
熔断机制是应对雪崩效亚的一种微服务链路保护机制。当扇出链路的某个微服务出错不可用或者响应时间太长时,
会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。
当检测到该节点微服务调用响应正常后,恢复调用链路。
在Spring Cloud框架里,熔断机制通过Hystrix3实现。Hystrix会监控微服务间调用的状况,
当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。

配置8001支付模块Service中的熔断器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//=====服务熔断
@HystrixCommand(
fallbackMethod = "paymentCircuitBreaker_fallback",
commandProperties = [
HystrixProperty(name = "circuitBreaker.enabled", value = "true"),//是否开启断路器
HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),//请求次数
HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),//时间窗口期
HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),//失败率达到多少后跳闸
]
)
open fun paymentCircuitBreaker(@PathVariable("id") id: Int): String {
if (id < 0) {
throw RuntimeException("*****id不能为负数")
}
val serialNumber = IdUtil.simpleUUID()
return Thread.currentThread().name + "调用成功,流水号:$serialNumber"

}

open fun paymentCircuitBreaker_fallback(@PathVariable("id") id: Int): String {
return "id不能为负数,请稍后重试:$id"
}

以下是controller的代码:

1
2
3
4
5
6
7
8
9
10
//===服务熔断
@GetMapping(
"/payment/circuit/{id}"
)
fun paymentcircuitBreaker(@PathVariable("id") id:Int): String {

var result = paymentService.paymentCircuitBreaker(id);
log.info("****result:" + result);
return result;
}

通过访问这个端口,然后实现服务熔断

服务限流(flowlimit)

hystrix仪表盘使用

  1. 建module

  2. 修改pom

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>

    //提供监控信息
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    关键是这个dashboard的包导入

  3. 设置yml。只设置端口即可。

  4. 主启动类
    以下是8001提供服务端,只有这样配置才能被找到

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    需要配置一些固定的内容(以下是8001的提供服务端,配置后才能被dashboard找到)
    @SpringBootApplication(exclude = [JacksonAutoConfiguration::class])
    @EnableEurekaClient
    @EnableCircuitBreaker
    open class PaymentHystrixMain8001 {
    /**
    *此配置是为了服务监控而配置,与服务容错本身无关,springcloud升级后的坑
    *ServletRegistrationBean因为springboot的默认路径不是"/hystrix.stream",
    *只要在自己的项目里配置上下面的serv儿et就可以了
    */
    @Bean
    open fun getServlet(): ServletRegistrationBean<HystrixMetricsStreamServlet> {
    var streamServlet: HystrixMetricsStreamServlet = HystrixMetricsStreamServlet();
    var registrationBean = ServletRegistrationBean(streamServlet);
    registrationBean.setLoadOnStartup(1);
    registrationBean.addUrlMappings("/hystrix.stream");
    registrationBean.setName("HystrixMetricsstreamServlet");
    return registrationBean;
    }
    }
    fun main(args: Array<String>) {
    runApplication<PaymentHystrixMain8001>(*args)


    }