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

/**
 * @author reghao
 * @date 2025-07-20 11:01:33
 */
@Slf4j
public class HystrixTest {
    /**
     * 异常降级
     * 非HystrixBadRequestException异常：当抛出HystrixBadRequestException时，
     * 调用程序可以捕获异常，此时不会触发fallback，而其他异常则会触发fallback，
     * 调用程序将获得fallback逻辑的返回结果。
     */
    static class HelloWorldExceptionCommand extends HystrixCommand<String> {
        private final int n;

        public HelloWorldExceptionCommand(int n) {
            // 最小配置,指定groupKey
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldExceptionGroup")));
            this.n = n;
        }

        @Override
        protected String run() throws Exception {
            log.info(n + "-HelloWorldExceptionCommand A--> " + Thread.currentThread().getName());
            // 制造异常
            int i = 1 / n;
            // 若异常被捕获则不会进入 getFallback()
            /*try {
                int i = 1 / n;
            } catch (Exception e) {
                log.error("异常:" + e.getMessage());
            }*/

            log.info(n + "-HelloWorldExceptionCommand B--> " + Thread.currentThread().getName());
            return n + "执行成功";
        }

        @Override
        protected String getFallback() {
            log.error(n + "-异常降级! C--> " + Thread.currentThread().getName());
            return n + "执行失败";
        }
    }

    /**
     * 超时降级
     * run()/construct()运行超时：执行命令的方法超时，将会触发fallback。
     */
    static class HelloWorldTimeoutCommand extends HystrixCommand<String> {
        private final int n;

        public HelloWorldTimeoutCommand(int n) {
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldTimeoutGroup"))
                    .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                            /*
                             * execution.isolation.thread.timeoutInMilliseconds
                             * 设置调用者等待命令执行的超时限制，超过此时间，HystrixCommand被标记为TIMEOUT，
                             * 并执行回退逻辑。
                             * 默认值：1000（毫秒）
                             */
                            .withExecutionTimeoutInMilliseconds(500)
                            /*
                             * execution.timeout.enabled
                             * 设置HystrixCommand的执行是否有超时限制。
                             * 默认值：true
                             */
                            .withExecutionTimeoutEnabled(true)
                            /*
                             * execution.isolation.thread.interruptOnTimeout
                             * 设置HystrixCommand的执行是否在超时发生时被中断。
                             * 使用线程隔离时，是否对命令执行超时的线程调用中断（Thread.interrupt()）操作。
                             * 默认值：true
                             */
                            .withExecutionIsolationThreadInterruptOnTimeout(false)
                    )
            );
            this.n = n;
        }

        @Override
        protected String run() throws Exception {
            log.info(n + "-HelloWorldTimeoutCommand A--> " + Thread.currentThread().getName());
            if (n == 0) {
                // 设置超时
                Thread.sleep(1000);
            }
            // 设置withExecutionIsolationThreadInterruptOnTimeout(false)后面代码将会继续执行
            log.info(n + "-HelloWorldTimeoutCommand B--> " + Thread.currentThread().getName());
            return n + "执行成功";
        }

        @Override
        protected String getFallback() {
            log.error("超时降级! C--> " + Thread.currentThread().getName());
            return n + "执行失败";
        }
    }

    /**
     * 触发熔断器熔断
     * 熔断器开启：当熔断器处于开启的状态，将会触发fallback。
     */
    static class HelloWorldBreakerCommand extends HystrixCommand<String> {
        private final int n;

        public HelloWorldBreakerCommand(int n) {
            //最小配置,指定groupKey
            super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldBreakerGroup"))
                    .andCommandPropertiesDefaults(
                            HystrixCommandProperties.Setter()
                                    /*
                                     * execution.isolation.thread.timeoutInMilliseconds
                                     * 设置调用者等待命令执行的超时限制，超过此时间，HystrixCommand被标记为TIMEOUT，
                                     * 并执行回退逻辑。
                                     * 默认值：1000（毫秒）
                                     */
                                    .withExecutionTimeoutInMilliseconds(1000)
                                    /*
                                     * execution.isolation.thread.interruptOnTimeout
                                     * 设置HystrixCommand的执行是否在超时发生时被中断。
                                     * 使用线程隔离时，是否对命令执行超时的线程调用中断（Thread.interrupt()）操作。
                                     * 默认值：true
                                     */
                                    .withExecutionIsolationThreadInterruptOnTimeout(true)
                                    // ** 断路器（Circuit Breaker）属性配置 **
                                    /*
                                     * circuitBreaker.enabled
                                     * 设置断路器是否生效
                                     * 默认值：true
                                     */
                                    .withCircuitBreakerEnabled(true)
                                    /*
                                     * circuitBreaker.requestVolumeThreshold
                                     * 设置在一个滚动窗口中，打开断路器的最少请求数。比如：如果值是20，在一个窗口内（比如10秒），收到19个请求，即使这19个请求都失败了，断路器也不会打开。
                                     * 默认值：20
                                     */
                                    .withCircuitBreakerRequestVolumeThreshold(6)
                                    /*
                                     * circuitBreaker.sleepWindowInMilliseconds
                                     * 设置在断路器被打开，拒绝请求到再次尝试请求的时间间隔。
                                     * 默认值：5000（毫秒）
                                     */
                                    .withCircuitBreakerSleepWindowInMilliseconds(3000)
                                    /*
                                     * circuitBreaker.errorThresholdPercentage
                                     * 设置打开断路器并启动回退逻辑的错误比率。
                                     * （这个参数的效果受到circuitBreaker.requestVolumeThreshold和滚动时间窗口的时间长度影响）
                                     * 默认值：50(%)
                                     */
                                    .withCircuitBreakerErrorThresholdPercentage(50)
                    )
                    //设置核心线程池的大小
                    .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20))
            );
            this.n = n;
        }

        @Override
        protected String run() throws Exception {
            log.info(n + "-HelloWorldTimeoutCommand A--> " + Thread.currentThread().getName());
            if (n > 0) {
                // 设置超时
                Thread.sleep(1000);
            }
            // 设置withExecutionIsolationThreadInterruptOnTimeout(true)后面代码将中断执行
            log.info(n + "-HelloWorldTimeoutCommand B--> " + Thread.currentThread().getName());
            return n + "执行成功";
        }

        @Override
        protected String getFallback() {
            log.error("熔断降级! C--> " + Thread.currentThread().getName());
            return n + "执行失败";
        }
    }

    /**
     * 线程池/信号量已满
     * 当线程池/信号量已满的状态，将会触发fallback。
     */
    static class HelloWorldThreadPoolCommand extends HystrixCommand<String> {
        private final int n;

        public HelloWorldThreadPoolCommand(int n) {
            super(Setter
                    // *** 基础属性配置 ***
                    /*
                     * CommandGroup是每个命令最少配置的必选参数，在不指定ThreadPoolKey的情况下，
                     * 字面值用于对不同依赖的线程池/信号区分，也就是在不指定ThreadPoolKey的情况下,
                     * CommandGroup用于指定线程池的隔离。命令分组用于对依赖操作分组，便于统计、汇总等。
                     */
                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldThreadPoolGroup"))
                    /*
                     * CommandKey是作为依赖命名，一般来说每个CommandKey代表一个依赖抽象，
                     * 相同的依赖要使用相同的CommandKey名称。依赖隔离的根本就是对相同CommandKey的依赖做隔离。
                     * 不同的依赖隔离最好使用不同的线程池（定义不同的ThreadPoolKey）。
                     * 从HystrixCommand源码的注释也可以看到CommandKey也用于对依赖操作统计、汇总等。
                     */
                    .andCommandKey(HystrixCommandKey.Factory.asKey("helloWorldThreadPoolCommand"))
                    /*
                     * ThreadPoolKey简单来说就是依赖隔离使用的线程池的键值。
                     * 当对同一业务依赖做隔离时使用CommandGroup做区分，
                     * 但是对同一依赖的不同远程调用如(一个是redis 一个是http)，可以使用HystrixThreadPoolKey做隔离区分。
                     * 虽然在业务上都是相同的组，但是需要在资源上做隔离时，可以使用HystrixThreadPoolKey区分。
                     * （对于每个不同的HystrixThreadPoolKey建议使用不同的CommandKey）
                     */
                    .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("helloWorldThreadPool"))
                    // *** 设置命令属性配置默认值 ***
                    .andCommandPropertiesDefaults(
                            HystrixCommandProperties.Setter()
                                    // ** 执行属性 **
                                    /*
                                     * execution.isolation.strategy
                                     * 用于设置HystrixCommand执行的隔离策略，有两种选项：
                                     * THREAD —— 在固定大小线程池中，以单独线程执行，并发请求数受限于线程池大小。
                                     * SEMAPHORE —— 在调用线程中执行，通过信号量来限制并发量。
                                     * 默认值：THREAD（ExecutionIsolationStrategy.THREAD）可选值：THREAD，SEMAPHORE
                                     */
                                    .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
                                    /*
                                     * execution.isolation.thread.timeoutInMilliseconds
                                     * 设置调用者等待命令执行的超时限制，超过此时间，HystrixCommand被标记为TIMEOUT，
                                     * 并执行回退逻辑。
                                     * 默认值：1000（毫秒）
                                     */
                                    .withExecutionTimeoutInMilliseconds(1000)
                                    /*
                                     * execution.timeout.enabled
                                     * 设置HystrixCommand的执行是否有超时限制。
                                     * 默认值：true
                                     */
                                    .withExecutionTimeoutEnabled(false)
                                    /*
                                     * execution.isolation.thread.interruptOnTimeout
                                     * 设置HystrixCommand的执行是否在超时发生时被中断。
                                     * 使用线程隔离时，是否对命令执行超时的线程调用中断（Thread.interrupt()）操作。
                                     * 默认值：true
                                     */
                                    .withExecutionIsolationThreadInterruptOnTimeout(true)
                                    /*
                                     * execution.isolation.semaphore.maxConcurrentRequests
                                     * 设置当使用ExecutionIsolationStrategy.SEMAPHORE时，HystrixCommand执行方法允许的最大请求数。
                                     * 如果达到最大并发数时，后续请求会被拒绝。
                                     * 信号量应该是容器（比如Tomcat）线程池一小部分，不能等于或者略小于容器线程池大小，否则起不到保护作用。
                                     * 默认值：10
                                     */
                                    .withExecutionIsolationSemaphoreMaxConcurrentRequests(10)
                                    // ** 回退属性 **
                                    // 下面的属性控制HystrixCommand.getFallback()执行。
                                    // 这些属性对ExecutionIsolationStrategy.THREAD和ExecutionIsolationStrategy.SEMAPHORE都有效。
                                    /*
                                     * fallback.isolation.semaphore.maxConcurrentRequests
                                     * 设置调用线程产生的HystrixCommand.getFallback()方法的允许最大请求数目。
                                     * 如果达到最大并发数目，后续请求将会被拒绝，如果没有实现回退，则抛出异常。
                                     * (这里需要注意一点，这个变量的命名不是很规范，它实际上对THREAD和SEMAPHORE两种隔离策略都生效)
                                     * 默认值：10
                                     */
                                    .withFallbackIsolationSemaphoreMaxConcurrentRequests(10)
                                    /*
                                     * fallback.enabled
                                     * 该属性决定当前的调用故障或者拒绝发生时，是否调用HystrixCommand.getFallback()。
                                     * 默认值：true
                                     */
                                    .withFallbackEnabled(true)
                                    // ** 断路器（Circuit Breaker）属性配置 **
                                    /*
                                     * circuitBreaker.enabled
                                     * 设置断路器是否生效
                                     * 默认值：true
                                     */
                                    .withCircuitBreakerEnabled(true)
                                    /*
                                     * circuitBreaker.requestVolumeThreshold
                                     * 设置在一个滚动窗口中，打开断路器的最少请求数。比如：如果值是20，在一个窗口内（比如10秒），收到19个请求，即使这19个请求都失败了，断路器也不会打开。
                                     * 默认值：20
                                     */
                                    .withCircuitBreakerRequestVolumeThreshold(5)
                                    /*
                                     * circuitBreaker.sleepWindowInMilliseconds
                                     * 设置在断路器被打开，拒绝请求到再次尝试请求的时间间隔。
                                     * 默认值：5000（毫秒）
                                     */
                                    .withCircuitBreakerSleepWindowInMilliseconds(5000)
                                    /*
                                     * circuitBreaker.errorThresholdPercentage
                                     * 设置打开断路器并启动回退逻辑的错误比率。
                                     * （这个参数的效果受到circuitBreaker.requestVolumeThreshold和滚动时间窗口的时间长度影响）
                                     * 默认值：50(%)
                                     */
                                    .withCircuitBreakerErrorThresholdPercentage(50)
                                    /*
                                     * circuitBreaker.forceOpen
                                     * 如果该属性设置为true，强制断路器进入打开状态，将会拒绝所有的请求。
                                     * 该属性优先级比circuitBreaker.forceClosed高
                                     * 默认值：false
                                     */
                                    .withCircuitBreakerForceOpen(false)
                                    /*
                                     * circuitBreaker.forceClosed
                                     * 如果该属性设置为true，强制断路器进入关闭状态，将会允许所有的请求，无视错误率。
                                     * 默认值：false
                                     */
                                    .withCircuitBreakerForceClosed(false)
                                    // ** 请求上下文属性配置 **
                                    /*
                                     * requestCache.enabled
                                     * 设置HystrixCommand.getCacheKey()是否启用，由HystrixRequestCache通过请求缓存提供去重复数据功能。
                                     * （请求结果缓存需要配合HystrixRequestContext使用，具体应用可以自行查阅）
                                     * 默认值：true
                                     */
                                    .withRequestCacheEnabled(true)
                                    /*
                                     * requestLog.enabled
                                     * 设置HystrixCommand执行和事件是否要记录日志到HystrixRequestLog。
                                     * 默认值：true
                                     */
                                    .withRequestLogEnabled(true)
                    )
                    // *** 设置线程池属性配置默认值 ***
                    .andThreadPoolPropertiesDefaults(
                            HystrixThreadPoolProperties.Setter()
                                    /*
                                     * coreSize
                                     * 设置核心线程池的大小（这个值和ThreadPoolExecutor的coreSize的含义不一样）
                                     * 默认值：10
                                     */
                                    .withCoreSize(10)
                                    /*
                                     * maximumSize
                                     * 1.5.9新增属性，设置线程池最大值。
                                     * 这个是在不开始拒绝HystrixCommand的情况下支持的最大并发数。
                                     * 这个属性起作用的前提是设置了allowMaximumSizeToDivergeFromCoreSize。
                                     * 1.5.9之前，核心线程池大小和最大线程池大小总是相同的。
                                     * 默认值：10
                                     */
                                    .withMaximumSize(10)
                                    /*
                                     * maxQueueSize
                                     * 设置BlockingQueue最大的队列值。如果设置为-1，
                                     * 那么使用SynchronousQueue，否则正数将会使用LinkedBlockingQueue。
                                     * 如果需要去除这些限制，允许队列动态变化，可以参考queueSizeRejectionThreshold属性。
                                     * 修改SynchronousQueue和LinkedBlockingQueue需要重启。
                                     * 默认值：-1
                                     */
                                    .withMaxQueueSize(-1)
                                    /*
                                     * queueSizeRejectionThreshold
                                     * 设置队列拒绝的阈值—-一个人为设置的拒绝访问的最大队列值，即使当前队列元素还没达到maxQueueSize。
                                     * 当将一个线程放入队列等待执行时，HystrixCommand使用该属性。
                                     * 注意：如果maxQueueSize设置为-1，该属性不可用。
                                     * 默认值：5
                                     */
                                    .withQueueSizeRejectionThreshold(5)
                                    /*
                                     * keepAliveTimeMinutes
                                     * 设置存活时间，单位分钟。如果coreSize小于maximumSize，那么该属性控制一个线程从实用完成到被释放的时间。
                                     * 默认值：1
                                     */
                                    .withKeepAliveTimeMinutes(1)
                                    /*
                                     * allowMaximumSizeToDivergeFromCoreSize
                                     * 在1.5.9中新增的属性。该属性允许maximumSize起作用。属性值可以等于或者大于coreSize值，
                                     * 设置coreSize小于maximumSize的线程池能够支持maximumSize的并发数，
                                     * 但是会将不活跃的线程返回到系统中去。
                                     * 默认值：false
                                     */
                                    .withAllowMaximumSizeToDivergeFromCoreSize(false)
                                    /*
                                     * metrics.rollingStats.timeInMilliseconds
                                     * 设置统计的滚动窗口的时间段大小。该属性是线程池保持指标时间长短。
                                     * 默认值：10000（毫秒）
                                     */
                                    .withMetricsRollingStatisticalWindowInMilliseconds(10000)
                                    /*
                                     * metrics.rollingStats.numBuckets
                                     * 设置滚动的统计窗口被分成的桶（bucket）的数目。
                                     * 注意：”metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0”必须为true，否则会抛出异常。
                                     * 默认值：10
                                     */
                                    .withMetricsRollingStatisticalWindowBuckets(10)
                    )
            );
            this.n = n;
        }

        @Override
        protected String run() throws Exception {
            log.info(n + "-HelloWorldThreadPoolCommand A--> " + Thread.currentThread().getName());
            Thread.sleep(10000);
            log.info(n + "-HelloWorldThreadPoolCommand B--> " + Thread.currentThread().getName());
            return n + "执行完毕";
        }

        @Override
        protected String getFallback() {
            log.error("线程池/信号量已满 FALLBACK --> !");
            return "FALLBACK";
        }

        static class ThreadRunner extends Thread {
            private int i;

            public ThreadRunner(int i) {
                this.i = i;
            }

            @Override
            public void run() {
                log.info(new HelloWorldThreadPoolCommand(i).execute());
            }
        }
    }

    static void test1() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (i == 3) {
                i = 0;
            }

            HelloWorldExceptionCommand command = new HelloWorldExceptionCommand(i);
            log.info(command.execute());
            Thread.sleep(1000);
        }
    }

    static void test2() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (i == 5) {
                i = 0;
            }
            HelloWorldTimeoutCommand command = new HelloWorldTimeoutCommand(i);
            // 超时执行getFallback
            log.info(command.execute());

            Thread.sleep(1000);
        }
    }

    public static void test3() throws Exception {
        for (int i = 0; i < 100; i++) {
            try {
                if (i == 10) {
                    i = 0;
                    Thread.sleep(1000);
                }
                log.info(new HelloWorldBreakerCommand(i).execute());
            } catch (Exception e) {
                log.error("异常:" + e.getMessage(), e);
            }
        }
    }

    static void test4() throws Exception {
        // 关闭HystrixCommand执行的超时限制，设置withExecutionTimeoutEnabled(false)
        // withCoreSize(10)
        for (int i = 0; i < 100; i++) {
            Thread thread = new HelloWorldThreadPoolCommand.ThreadRunner(i);
            thread.start();
            Thread.sleep(500);
        }
    }

    public static void main(String[] args) throws Exception {
        //test1();
        System.out.println("---------------------------------------------");
        //test2();
        System.out.println("---------------------------------------------");
        test3();
        System.out.println("---------------------------------------------");
        //test4();
    }
}
