服务雪崩预防:Hystrix组件

358 阅读7分钟

服务雪崩(service outage)是指一个或多个服务或系统的故障引起的级联效应,导致整个系统或应用程序无法正常运行的情况。通常情况下,服务雪崩会在高负载或异常负载下发生,因为此时系统的资源已经达到了极限,而服务的故障会导致其他服务无法正常工作,从而导致整个系统崩溃。

服务雪崩是互联网领域中常见的问题之一,由于系统中的每个组件都是相互依赖的,因此一个组件的故障很容易影响到其他组件。为了避免服务雪崩的发生,开发人员需要采取一系列措施 。

假如有这样的调用链:Service A调用Service B,某一时刻,请求数量突然上升,Service A节点比较多暂时能抗住请求,但是如果Service B抗不住,很快消耗完Service B的线程,Service B变得不可用,Service A也会跟着变得不可用。

如果 Service B 无法处理 Service A 的请求,那么 Service A 的请求可能会一直等待直到超时,这将占用 Service A 的资源(如线程池),最终导致 Service A 不可用。如果调用链又中存在另外服务调用A,慢慢的这个服务也会变得不可用。这就是一个典型的服务雪崩效应。因此,在微服务架构中,需要采取相应的措施来避免服务雪崩的发生。

服务熔断和服务降级就可以视为解决服务雪崩两个比较好用的方案,通常会使用Hystrix组件去完成。

服务熔断

还是Service A调用Service B

当Service B 因为某种原因变得不可用,Service A为了保证自己可用性,不再继续调用Service B,直接返回,快速释放资源。

熔断机制基本采用断路器模式

  1. 服务处于正常状态:服务正常响应请求,断路器保持关闭状态。
  2. 服务处于熔断状态:当服务出现错误率超过一定阈值时,断路器进入熔断状态,拒绝所有请求并快速返回预定的默认响应。
  3. 服务处于半开状态:在一定时间间隔内,断路器会定期允许一定数量的请求到达服务端进行测试,如果请求成功,则断路器恢复到正常状态,否则返回熔断状态

熔断器检查到错误超过设置阈值服务正常状态切换到服务熔断状态,过了一段时间后,熔断器为了确定服务可用正常使用使得服务进入半开状态,开始逐渐正常接受一定量的请求,如果请求成功,服务恢复到正常状态。

熔断器也会有三种状态:closed,open,half open

服务降级

服务降级是一种应对系统负载过高或者出现异常情况的策略,通过在保证系统可用性的前提下,丢弃一些不重要或者可以稍后处理的请求,以减轻系统负担,保障核心功能的正常运行。

服务降级可以针对不同的业务场景和应用场景来实现,通常包括以下几种方式:

  1. 返回默认值或者空数据:当服务无法提供有效的响应结果时,可以返回一个默认值或者空数据,以避免系统崩溃或者出现异常。
  2. 返回缓存数据:当服务无法提供实时数据时,可以返回缓存中的数据,以减轻服务的负担,避免频繁的查询数据库或者调用其他服务。
  3. 返回友好提示信息:当服务出现故障或者无法提供有效响应时,可以返回友好提示信息,让用户知道系统正在维护或者出现异常,以避免用户流失和投诉。
  4. 降级服务的功能:当系统出现异常或者负载过高时,可以暂时关闭某些不重要或者不紧急的功能,以减轻系统的负担,保障核心功能的正常运行。

Hystrix组件:熔断设置

可见熔断是针对服务提供方的,所以在服务提供方提供设置即可。

添加Hystrix组件依赖

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

开启熔断功能

@SpringBootApplication
@EnableCircuitBreaker // 开启Hystrix的服务熔断功能
@EnableDiscoveryClient
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

熔断配置

默认是10s内请求数量超过20s,或者10s内请求错误超过百分之50进入熔断

@RestController
public class DemoController {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);
​
    private final Random random = new Random();
​
    @GetMapping("/hello")
    @HystrixCommand(fallbackMethod = "fallback",
            commandProperties = {
                    // 是否开启短路器
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED, value = "true"),
                    // 请求次数
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value = "10"),
                    // 熔断时间窗口
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value = "10000"),
                    // 失败率
                    @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value = "70")})
    public String hello() throws InterruptedException {
        return "hello world!";
        
    }
​
    public String fallback() {
        //进入熔断后,自动调用这个方法,进行快速响应。
        return "Sorry, the service is unavailable. Please try again later.";
    }
}

可以在yml进行全局配置:请求阈值,时间窗口,失败率,开启短路器

hystrix:
  command:
    default:
      metrics:
        rollingStats:
          timeInMilliseconds: 10000
      circuitBreaker:
        sleepWindowInMilliseconds: 5000
        errorThresholdPercentage: 50 
  circuitBreaker: # 全局
    enabled: true

每个接口都额外写一个方法,代码量会很大。

@RestController
@DefaultProperties(defaultFallback = "fallback") // 配置全局的默认服务降级
public class DemoController {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoController.class);
​
    private final Random random = new Random();
​
    @GetMapping("/hello")
    @HystrixCommand //使用默认的窗口,默认开启了全局的断路器
    public String hello() throws InterruptedException {
        return "hello world!";
        
    }
​
    public String fallback() {
          //进入熔断后,自动调用这个方法,进行快速响应。
        return "Sorry, the service is unavailable. Please try again later.";
    }
}

Hystrix组件:降级设置

熔断降级可在服务提供方和服务消费方进行配置,通常在消费方进行配置。

消费方配置降级

消费方配置降级和熔断的方式基本一致

通常是接口出错或者超某个时间的时候会去调用备用方法

依赖和激活

同样需要导入依赖同时在启动类内激活

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker // 开启Hystrix的服务熔断功能
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

降级配置

@RestController
public class DemoController {
​
    @Resource
    private DemoHystrixService demoHystrixService;
​
    @HystrixCommand(fallbackMethod = "testHystrixTimeoutFallback", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") //超过3s着调用备用方法
    })
    @GetMapping("/test/hystrix/timeout/{id}")
    public String testHystrixTimeout(@PathVariable("id") Integer id) throws InterruptedException {
        return demoHystrixService.testHystrixTimeout(id);
    }
​
    public String testHystrixTimeoutFallback(@PathVariable("id") Integer id) {
        return "Sorry, the service is unavailable. Please try again later.";
    }
}

在配置文件中配置响应超时时间

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

配置默认方法

@RestController
@DefaultProperties(defaultFallback = "testHystrixTimeoutFallback")
public class DemoController {
​
    @Resource
    private DemoHystrixService demoHystrixService;
​
    @HystrixCommand // 默认使用上面yml中配置
    @GetMapping("/test/hystrix/timeout/{id}")
    public String testHystrixTimeout(@PathVariable("id") Integer id) throws InterruptedException {
        return demoHystrixService.testHystrixTimeout(id);
    }
​
    public String testHystrixTimeoutFallback(@PathVariable("id") Integer id) {
        return "Sorry, the service is unavailable. Please try again later.";
    }
}

服务方配置降级

客户端配置服务降级处理,Hystrix组件以及和Feign一起导入,可以不用再次导入依赖。

feign:
    hystrix:
        enabled: true #开启支持降级

超时时间和feign的设置一致,不用额外配置

feign客户端实现降级

@FeignClient(name = "example-service", fallback = ExampleServiceFallback.class)
public interface ExampleServiceClient {
    @GetMapping("/example")
     String getExampleData();
}
@Component
public class ExampleServiceFallback implements ExampleServiceClient {
    @Override
    public String getExampleData() {
        return "Fallback response";
    }
}

总结

降级和熔断是微服务架构中常用的两种技术手段。

  1. 目的都是用于提高系统的可靠性和稳定性。
  2. 粒度都是服务级别的,通常以服务或功能模块为单位进行降级处理。 业界有的服务实现更加细粒度的处理,比如做到数据持久层。
  3. 一般由开发人员手动定义,系统会根据预设的规则或配置决定是否启用。 熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段。

熔断机制的目的是为了保护系统和依赖服务,提高系统的可恢复性和稳定性。当服务出现故障或达到一定的错误阈值时,熔断机制会触发,快速断开对该服务的调用,并采取预先定义的熔断策略,例如返回默认值或执行降级逻辑。这种熔断的行为可以视为对服务的一种降级处理。

熔断一般配置在服务提供方,降级则都可配置,但是一般配置的服务的消费方。