Spring Cloud 八:Hystrix熔断降级介绍与使用

一,概念

1,雪崩效应

​ 每个服务 发出一个HTTP请求都会 在 服务中 开启一个新线程。而下游服务挂了或者网络不可达,通常线程会阻塞住,直到Timeout。如果并发量多一点,这些阻塞的线程就会占用大量的资源,很有可能把自己本身这个微服务所在的机器资源耗尽,导致自己也挂掉。

​ 如果服务提供者响应非常缓慢,那么服务消费者调用此提供者就会一直等待,直到提供者响应或超时。在高并发场景下,此种情况,如果不做任何处理,就会导致服务消费者的资源耗竭甚至整个系统的崩溃。一层一层的崩溃,导致所有的系统崩溃。

雪崩:由基础服务故障导致级联故障的现象。描述的是:提供者不可用 导致消费者不可用,并将不可用逐渐放大的过程。像滚雪球一样,不可用的服务越来越多。影响越来越恶劣。

总之 : 基础服务故障 导致 级联故障 就是 雪崩。

2,容错机制

  • 为网络请求设置超时:

必须为网络请求设置超时。一般的调用一般在几十毫秒内响应。如果服务不可用,或者网络有问题,那么响应时间会变很长。长到几十秒。

每一次调用,对应一个线程或进程,如果响应时间长,那么线程就长时间得不到释放,而线程对应着系统资源,包括CPU,内存,得不到释放的线程越多,资源被消耗的越多,最终导致系统崩溃。

因此必须设置超时时间,让资源尽快释放。

  • 使用断路器模式:

想一下家里的保险丝,跳闸。如果家里有短路或者大功率电器使用,超过电路负载时,就会跳闸,如果不跳闸,电路烧毁,波及到其他家庭,导致其他家庭也不可用。通过跳闸保护电路安全,当短路问题,或者大功率问题被解决,在合闸。

自己家里电路,不影响整个小区每家每户的电路。

3,断路器

如果对某个微服务请求有大量超时(说明该服务不可用),再让新的请求访问该服务就没有意义,只会无谓的消耗资源。例如设置了超时时间1s,如果短时间内有大量的请求无法在1s内响应,就没有必要去请求依赖的服务了。

  1. 断路器是对容易导致错误的操作的代理。这种代理能统计一段时间内的失败次数,并依据次数决定是正常请求依赖的服务还是直接返回。

  2. 断路器可以实现快速失败,如果它在一段时间内检测到许多类似的错误(超时),就会在之后的一段时间,强迫对该服务的调用快速失败,即不再请求所调用的服务。这样对于消费者就无须再浪费CPU去等待长时间的超时。

  3. 断路器也可自动诊断依赖的服务是否恢复正常。如果发现依赖的服务已经恢复正常,那么就会恢复请求该服务。通过重置时间来决定断路器的重新闭合。

    这样就实现了微服务的“自我修复”:当依赖的服务不可用时,打开断路器,让服务快速失败,从而防止雪崩。当依赖的服务恢复正常时,又恢复请求。

断路器状态转换的逻辑:

关闭状态:正常情况下,断路器关闭,可以正常请求依赖的服务。

打开状态:当一段时间内,请求失败率达到一定阈值,断路器就会打开。服务请求不会去请求依赖的服务。调用方直接返回。不发生真正的调用。重置时间过后,进入半开模式。

半开状态:断路器打开一段时间后,会自动进入“半开模式”,此时,断路器允许一个服务请求访问依赖的服务。如果此请求成功(或者成功达到一定比例),则关闭断路器,恢复正常访问。否则,则继续保持打开状态。

断路器的打开,能保证服务调用者在调用异常服务时,快速返回结果,避免大量的同步等待,减少服务调用者的资源消耗。并且断路器能在打开一段时间后继续侦测请求执行结果,判断断路器是否能关闭,恢复服务的正常调用。

4,降级

为了在整体资源不够的时候,适当放弃部分服务,将主要的资源投放到核心服务中,待渡过难关之后,再重启已关闭的服务,保证了系统核心服务的稳定。当服务停掉后,自动进入fallback替换主方法。

用fallback方法代替主方法执行并返回结果,对失败的服务进行降级。当调用服务失败次数在一段时间内超过了断路器的阈值时,断路器将打开,不再进行真正的调用,而是快速失败,直接执行fallback逻辑。服务降级保护了服务调用者的逻辑。

熔断和降级:

共同点:
	1、为了防止系统崩溃,保证主要功能的可用性和可靠性。
	2、用户体验到某些功能不能用。
不同点:
	1、熔断由下级故障触发,主动惹祸。
	2、降级由调用方从负荷角度触发,无辜被抛弃。

二,Hystrix

1,什么是Hystrix

Hystrix是一个容错组件,实现了 超时机制和断路器模式。

Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:

  1. 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
  2. 防止雪崩。
  3. 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
  4. 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
  5. 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
  6. 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
  7. 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
  8. 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
  9. 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。前面有介绍。

2,Hystrix配合RestTemplate使用

2.1,Eureka注册中心eureka-server

application.xml

spring:
  application:
    name: eureka-server
eureka:
  client:
    service-url:
      defaultZone: http://euk-server1:7001/eureka/
  instance:
    hostname: euk-server1
server:
  port: 7001

启动类:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaServerApplication.class, args);
    }

}

2.2,Eureka服务提供者eureka-provider

application.yaml

spring:
  application:
    name: eureka-provider
eureka:
  client:
    service-url:
      defaultZone: http://euk-server1:7001/eureka/
  instance:
    hostname: euk-client1
server:
  port: 7002

提供服务的订单类

@RestController
public class OrderController {
    
    

    @Value("${server.port}")
    private int value;

    @RequestMapping("/getOrder")
    public String getOrder() {
    
    
        return "我是订单,我的端口为:" + value;
    }
}

启动类

@SpringBootApplication
@EnableEurekaClient
public class EurekaProviderApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaProviderApplication.class, args);
    }
}

3.3,Eureka服务消费者eureka-cunsumer

首先添加Hystrix依赖

<dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
     <version>2.2.5.RELEASE</version>
</dependency>

application.yaml

spring:
  application:
    name: eureka-consumer
eureka:
  client:
    service-url:
      defaultZone: http://euk-server1:7001/eureka/
  instance:
    hostname: euk-client2
server:
  port: 7008

RestTemplate配置类RestTemplateConfiguration.java

@Configuration
public class RestTemplateConfiguration {
    
    
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
    
    
        return new RestTemplate();
    }
}

获取订单服务类OrderController.java,其中@HystrixCommand(fallbackMethod = “fallback”)设置失败降级处理。

@RestController
public class OrderController {
    
    

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/order")
    @HystrixCommand(fallbackMethod = "fallback")
    public String getOrder() {
    
    
        return restTemplate.getForObject("http://eureka-provider/getOrder", String.class);
    }

    public String fallback(){
    
    
        return "很抱歉,请求订单失败!";
    }
}

启动类,通过@EnableHystrix开启Hystrix功能:

@EnableHystrix
@SpringBootApplication
public class EurekaConsumerApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaConsumerApplication.class, args);
    }
}

3.4,正常启动,发起订单服务调用:http://euk-client2:7008/order

在这里插入图片描述

3.5,给调用订单服务设置超时时间1s,然后让订单服务睡眠2s

服务设置超时时间1s

@RestController
public class OrderController {
    
    

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/order")
    @HystrixCommand(fallbackMethod = "fallback",commandProperties = {
    
    
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000")
    })
    public String getOrder() {
    
    
        return restTemplate.getForObject("http://eureka-provider/getOrder", String.class);
    }

    public String fallback(){
    
    
        return "很抱歉,请求订单失败!";
    }
}

订单服务睡眠2s

@RestController
public class OrderController {
    
    

    @Value("${server.port}")
    private int value;

    @RequestMapping("/getOrder")
    public String getOrder() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
        return "我是订单,我的端口为:" + value;
    }
}

3.6,再运行访问获取订单

http://euk-client2:7008/order

在这里插入图片描述

发现执行了fallback()中的内容,服务降级。

3,Hystrix配合Feign使用

还是保留以上例子中的eureka-server和eureka-provider不变,新建model,eureka-consumer-feign

3.1,第一步还是先导入Hystrix的依赖

<dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

3.2,Feign自带Hystrix,但是默认没有打开,首先打开Hystrix

application.yaml

spring:
  application:
    name: eureka-consumer-feign
eureka:
  client:
    service-url:
      defaultZone: http://euk-server1:7001/eureka/
  instance:
    hostname: localhost
server:
  port: 7009
feign:
  hystrix:
    enabled: true

3.3,配置Feign的远程服务调用接口,和降级处理类

@FeignClient(name = "eureka-provider", fallback = OrderFallback.class)
public interface OrderInterface {
    
    
    @GetMapping("/getOrder")
    String order();
}

3.4,新建降级处理类OrderFallback.java

@Component
public class OrderFallback implements OrderInterface {
    
    
    @Override
    public String order() {
    
    
        return "很抱歉,请求订单失败!";
    }
}

3.5,远程服务调用获取订单

@RestController
public class OrderController {
    
    

    @Autowired
    private OrderInterface orderInterface;

    @GetMapping("/order")
    public String order(){
    
    
        return orderInterface.order();
    }
}

3.6,配置启动类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
public class EurekaConsumerFeignApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(EurekaConsumerFeignApplication.class, args);
    }

}

3.7,调用服务

因为hystrix默认超时时间是1s,然后我们调用getOrder服务睡眠了2秒,所以此时会走到fallback:

在这里插入图片描述

4,FallbackFactory捕获熔断的异常信息

我们需要对上面例子做如下修改:

OrderFallbackFactory.java

@Component
public class OrderFallbackFactory implements FallbackFactory<OrderFallback> {
    
    
    @Override
    public OrderFallback create(Throwable throwable) {
    
    
        return new OrderFallback(throwable);
    }
}

OrderFallback.java

@Component
public class OrderFallback implements OrderInterface {
    
    

    private Throwable throwable;

    public OrderFallback() {
    
    
    }

    public OrderFallback(Throwable throwable) {
    
    
        this.throwable = throwable;
    }

    @Override
    public String order() {
    
    
        if (throwable != null) {
    
    
            return throwable.toString();
        }
        return "很抱歉,请求订单失败!";
    }
}

OrderInterface.java

//@FeignClient(name = "eureka-provider", fallback = OrderFallback.class)
@FeignClient(name = "eureka-provider", fallbackFactory = OrderFallbackFactory.class)
public interface OrderInterface {
    
    
    @RequestMapping("/getOrder")
    String order();
}

调用服务:

在这里插入图片描述

5,Hystrix配置

5.1,HystrixProperty

1、Execution:
用来控制HystrixCommand.run()的执行
具体意义:
execution.isolation.strategy:该属性用来设置HystrixCommand.run()执行的隔离策略。默认为THREAD。
execution.isolation.thread.timeoutInMilliseconds:该属性用来配置HystrixCommand执行的超时时间,单位为毫秒。
execution.timeout.enabled:该属性用来配置HystrixCommand.run()的执行是否启用超时时间。默认为true。
execution.isolation.thread.interruptOnTimeout:该属性用来配置当HystrixCommand.run()执行超时的时候是否要它中断。
execution.isolation.thread.interruptOnCancel:该属性用来配置当HystrixCommand.run()执行取消时是否要它中断。
execution.isolation.semaphore.maxConcurrentRequests:当HystrixCommand命令的隔离策略使用信号量时,该属性用来配置信号量的大小。当最大并发请求达到该设置值时,后续的请求将被拒绝。

2、Fallback:
用来控制HystrixCommand.getFallback()的执行
fallback.isolation.semaphore.maxConcurrentRequests:该属性用来设置从调用线程中允许HystrixCommand.getFallback()方法执行的最大并发请求数。当达到最大并发请求时,后续的请求将会被拒绝并抛出异常。
fallback.enabled:该属性用来设置服务降级策略是否启用,默认是true。如果设置为false,当请求失败或者拒绝发生时,将不会调用HystrixCommand.getFallback()来执行服务降级逻辑。

3、Circuit Breaker:用来控制HystrixCircuitBreaker的行为。
circuitBreaker.enabled:确定当服务请求命令失败时,是否使用断路器来跟踪其健康指标和熔断请求。默认为true。
circuitBreaker.requestVolumeThreshold:用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为20的时候,如果滚动时间窗(默认10秒)内仅收到19个请求,即使这19个请求都失败了,断路器也不会打开。
circuitBreaker.sleepWindowInMilliseconds:用来设置当断路器打开之后的休眠时间窗。休眠时间窗结束之后,会将断路器设置为“半开”状态,尝试熔断的请求命令,如果依然时候就将断路器继续设置为“打开”状态,如果成功,就设置为“关闭”状态。
circuitBreaker.errorThresholdPercentage:该属性用来设置断路器打开的错误百分比条件。默认值为50,表示在滚动时间窗中,在请求值超过requestVolumeThreshold阈值的前提下,如果错误请求数百分比超过50,就把断路器设置为“打开”状态,否则就设置为“关闭”状态。
circuitBreaker.forceOpen:该属性默认为false。如果该属性设置为true,断路器将强制进入“打开”状态,它会拒绝所有请求。该属性优于forceClosed属性。
circuitBreaker.forceClosed:该属性默认为false。如果该属性设置为true,断路器强制进入“关闭”状态,它会接收所有请求。如果forceOpen属性为true,该属性不生效。

4、Metrics:该属性与HystrixCommand和HystrixObservableCommand执行中捕获的指标相关。
metrics.rollingStats.timeInMilliseconds:该属性用来设置滚动时间窗的长度,单位为毫秒。该时间用于断路器判断健康度时需要收集信息的持续时间。断路器在收集指标信息时会根据设置的时间窗长度拆分成多个桶来累计各度量值,每个桶记录了一段时间的采集指标。例如,当为默认值10000毫秒时,断路器默认将其分成10个桶,每个桶记录1000毫秒内的指标信息。
metrics.rollingStats.numBuckets:用来设置滚动时间窗统计指标信息时划分“桶”的数量。默认值为10。
metrics.rollingPercentile.enabled:用来设置对命令执行延迟是否使用百分位数来跟踪和计算。默认为true,如果设置为false,那么所有的概要统计都将返回-1。
metrics.rollingPercentile.timeInMilliseconds:用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
metrics.rollingPercentile.numBuckets:用来设置百分位统计滚动窗口中使用桶的数量。
metrics.rollingPercentile.bucketSize:用来设置每个“桶”中保留的最大执行数。
metrics.healthSnapshot.intervalInMilliseconds:用来设置采集影响断路器状态的健康快照的间隔等待时间。

5、Request Context:涉及HystrixCommand使用HystrixRequestContext的设置。
requestCache.enabled:用来配置是否开启请求缓存。
requestLog.enabled:用来设置HystrixCommand的执行和事件是否打印到日志的HystrixRequestLog中。

5.2,类中的默认配置值

HystrixCommandProperties.java

/* --------------统计相关------------------*/ 
// 统计滚动的时间窗口,默认:5000毫秒(取自circuitBreakerSleepWindowInMilliseconds)   
private final HystrixProperty metricsRollingStatisticalWindowInMilliseconds;   
// 统计窗口的Buckets的数量,默认:10个,每秒一个Buckets统计   
private final HystrixProperty metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow   
// 是否开启监控统计功能,默认:true   
private final HystrixProperty metricsRollingPercentileEnabled;   
/* --------------熔断器相关------------------*/ 
// 熔断器在整个统计时间内是否开启的阀值,默认20。也就是在metricsRollingStatisticalWindowInMilliseconds(默认10s)内至少请求20次,熔断器才发挥起作用   
private final HystrixProperty circuitBreakerRequestVolumeThreshold;   
// 熔断时间窗口,默认:5秒.熔断器中断请求5秒后会进入半打开状态,放下一个请求进来重试,如果该请求成功就关闭熔断器,否则继续等待一个熔断时间窗口
private final HystrixProperty circuitBreakerSleepWindowInMilliseconds;   
//是否启用熔断器,默认true. 启动   
private final HystrixProperty circuitBreakerEnabled;   
//默认:50%。当出错率超过50%后熔断器启动
private final HystrixProperty circuitBreakerErrorThresholdPercentage;  
//是否强制开启熔断器阻断所有请求,默认:false,不开启。置为true时,所有请求都将被拒绝,直接到fallback 
private final HystrixProperty circuitBreakerForceOpen;   
//是否允许熔断器忽略错误,默认false, 不开启   
private final HystrixProperty circuitBreakerForceClosed; 
/* --------------信号量相关------------------*/ 
//使用信号量隔离时,命令调用最大的并发数,默认:10   
private final HystrixProperty executionIsolationSemaphoreMaxConcurrentRequests;   
//使用信号量隔离时,命令fallback(降级)调用最大的并发数,默认:10   
private final HystrixProperty fallbackIsolationSemaphoreMaxConcurrentRequests; 
/* --------------其他------------------*/ 
//使用命令调用隔离方式,默认:采用线程隔离,ExecutionIsolationStrategy.THREAD   
private final HystrixProperty executionIsolationStrategy;   
//使用线程隔离时,调用超时时间,默认:1秒   
private final HystrixProperty executionIsolationThreadTimeoutInMilliseconds;   
//线程池的key,用于决定命令在哪个线程池执行   
private final HystrixProperty executionIsolationThreadPoolKeyOverride;   
//是否开启fallback降级策略 默认:true   
private final HystrixProperty fallbackEnabled;   
// 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作.默认:true   
private final HystrixProperty executionIsolationThreadInterruptOnTimeout; 
// 是否开启请求日志,默认:true   
private final HystrixProperty requestLogEnabled;   
//是否开启请求缓存,默认:true   
private final HystrixProperty requestCacheEnabled; // Whether request caching is enabled.

HystrixThreadPoolProperties.java

/* 配置线程池大小,默认值10个 */ 
private final HystrixProperty corePoolSize; 
/* 配置线程值等待队列长度,默认值:-1 建议值:-1表示不等待直接拒绝,测试表明线程池使用直接决绝策略+ 合适大小的非回缩线程池效率最高.所以不建议修改此值。 当使用非回缩线程池时,queueSizeRejectionThreshold,keepAliveTimeMinutes 参数无效 */
private final HystrixProperty maxQueueSize; 

6,Hystrix Dashboard监控系统

Hystrix提供了准实时的调用监控(Hystrix DashBoard),Hystrix会持续的记录通过Hystrix发起的请求的执行信息,以统计报表和图形的形式展示给客户,包括每秒执行多少,请求多少成功,请求失败多少等。

Netflix通过Hystrix-metics-event-stream项目实现了对以上指标的监控,SpringCloud也提供了Hystrix DashBoard的整合,对监控内容转化成可视化的界面,以便于用户能够直接的看到服务和集群的状态,在实际使用中,我们往往还要结合Turbine来使用。

Hystrix Dashboard的搭建其实很简单,分为三步:

6.1,添加Hystrix Dashboard依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>

6.2,设置端口application.yaml

server:
  port: 7010

6.3,设置启动类

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }

}

访问http://localhost:7010/hystrix

在这里插入图片描述

三,写到最后

本文所用到的例子代码均已上传gitee

https://gitee.com/songbozhao/spring-cloud-hystrix-test

猜你喜欢

转载自blog.csdn.net/u013277209/article/details/110975722