Netflix Hystrix - 断路器

63 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


Hystrix的短路器执行原理

HystrixCommandProperties.withExecutionTimeoutInMilliseconds():设置单位时间,判断HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()的时间单位,默认10秒。单位毫秒。

HystrixCommandProperties.circuitBreakerRequestVolumeThreshold():单位时间内(默认10s内),请求超时数超出则触发熔断策略。默认值为20次请求数。

HystrixCommandProperties.circuitBreakerErrorThresholdPercentage():单位时间内,出现错误的请求百分比达到限制,则触发熔断策略。默认为50%。

HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds():当熔断策略开启后,延迟多久尝试再次请求远程服务。默认为5秒。单位毫秒。这5秒直接执行fallback方法,不在请求远程application service。
  1. 经过短路器的流量超过一定的阈值,HystrixCommandProperties.circuitBreakerRequestVolumeThreshold(20)

要求在10s内,经过短路器的流量必须达到20个;在10s内,经过短路器的流量才10个,不会去判断要不要短路

  1. 断路器统计的异常调用占比超过一定的阈值,HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()

在10s内,经过短路器的流量,达到了30个;同时其中异常的访问数量,占到了一定的比例,比如说60%的请求都是异常(error,reject,timeout),会开启短路

  1. 断路器从close状态转换到open状态

  2. 断路器打开时,所有经过该断路器的请求全部被短路,不调用后端服务,直接走fallback降级

  3. 经过了一段时间之后,HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds(),会half-open,让一条请求经过短路器。如果调用成功了,那么就自动恢复,转到close状态

短路器的配置

circuitBreaker.enabled:控制短路器是否允许工作,包括跟踪依赖服务调用的健康状况,以及对异常情况过多时是否允许触发短路,默认是true

circuitBreaker.requestVolumeThreshold:设置一个rolling window滑动窗口,最少要有多少个请求时,才触发开启短路

circuitBreaker.sleepWindowInMilliseconds:设置在短路之后,需要在多长时间内直接reject请求,然后在这段时间之后,再重新设置为half-open状态,尝试允许请求通过以及自动恢复,默认值是5000毫秒

circuitBreaker.errorThresholdPercentage:设置异常请求量的百分比,当异常请求达到这个百分比时,就触发打开短路器,默认是50,也就是50%

circuitBreaker.forceOpen:如果设置为true的话,直接强迫打开短路器,相当于是手动短路,手动降级,默认false

circuitBreaker.forceClosed:如果设置为ture的话,直接强迫关闭短路器,相当于是手动停止短路,手动升级,默认false

package center.leon.eurekaconsumerribbonfeignapiimplhystrix.zoo.command;

import com.netflix.hystrix.*;
import lombok.extern.slf4j.Slf4j;

/**
 * @author : Leon on XXM Mac
 * @since : create in 2022/7/20 12:18 PM
 */
@Slf4j
public class MonkeyGetByIdOccurExceptionCommand extends HystrixCommand<String> {

    private Long id;

    public MonkeyGetByIdOccurExceptionCommand(Long id) {
        super(Setter
                .withGroupKey(
                        HystrixCommandGroupKey.Factory.asKey(MonkeyGetByIdOccurExceptionCommand.class.getSimpleName())
                )
                .andCommandPropertiesDefaults(
                        HystrixCommandProperties.Setter()
                                // 开启断路器
                                // 10秒内至少通过10个流量,失败占比10%时开启。10秒内通过10个,1个失败。
                                // 0-8 success, 9 error
                                // open circuit breaker
                                // sleep window 10秒 stt, circuit close
                                // 10-19 getFallback (actual 10-19 already recover)
                                // sleep window 10秒 end, circuit close
                                // 20-29 success
                                .withCircuitBreakerEnabled(true)
                                .withExecutionTimeoutInMilliseconds(10)
                                .withCircuitBreakerRequestVolumeThreshold(10)
                                .withCircuitBreakerErrorThresholdPercentage(10)
                                .withCircuitBreakerSleepWindowInMilliseconds(10 * 1000)
                )
        );
        this.id = id;
    }

    @Override
    protected String run() throws Exception {
        if (id <= 0) {
            throw new UnsupportedOperationException("ID不支持");
        }
        return "run " + MonkeyGetByIdOccurExceptionCommand.class.getSimpleName();
    }

    @Override
    protected String getFallback() {
        return "getFallback " + MonkeyGetByIdOccurExceptionCommand.class.getSimpleName();
    }
}
package center.leon.eurekaconsumerribbonfeignapiimplhystrix;

import center.leon.common.web.response.RestResponse;
import center.leon.eurekaconsumerribbonfeignapiimplhystrix.cache.controller.CacheController;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.concurrent.ExecutionException;


/**
 * @author Leon
 */
@Slf4j
@SpringBootApplication
public class EurekaConsumerRibbonFeignApiImplHystrixApplication {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ConfigurableApplicationContext run =
                SpringApplication.run(EurekaConsumerRibbonFeignApiImplHystrixApplication.class,
                        args);
        CacheController cacheController = run.getBean(CacheController.class);
        RestResponse restResponse = null;
        // 开启断路器
        // 10秒内至少通过10个流量,失败占比10%时开启。10秒内通过10个,1个失败。
        // 0-8 success, 9 error
        // open circuit breaker
        // sleep window 10秒 stt, circuit close
        // 10-19 getFallback (actual 10-19 already recover)
        // sleep window 10秒 end, circuit close
        // 20-29 success
        for (int i = 0; i < 100; i++) {
            if (i < 9) {
                restResponse = cacheController.cacheZooByIdOccurException(1L);
            } else if (i < 10) {
                restResponse = cacheController.cacheZooByIdOccurException(-1L);
            } else if (i < 20) {
                restResponse = cacheController.cacheZooByIdOccurException(1L);
            } else if (i < 30) {
                restResponse = cacheController.cacheZooByIdOccurException(1L);
            } else {
                restResponse = cacheController.cacheZooByIdOccurException(1L);
            }
            log.info("restResponse : {} - {}", i, restResponse.getData());
            Thread.sleep(1000);
        }
    }

}