spring cloud微服务架构(四):Hystrix的使用之

2018-08-03  本文已影响0人  mxjsxz

第二篇文章中提到的熔断机制,是Hystrix解决“雪崩”的方式之一,本文中内容:

  1. 熔断器的基本原理
  2. Hystrix对熔断器的实现与使用

1 熔断器的开启

熔断器是和上一篇文章中的命令(每一个命令对应一个熔断器,是熔断的最小单元,我也不知道这样理解对不对...)相对应的,熔断器同样是使用在客户端。

一个命令的熔断器的开启,需要满足下面两个条件(默认情况下):

  1. 该命令10秒内超过20次请求
  2. 满足第一个条件的情况下,如果请求的错误百分比大于50%,则打开熔断器

下面通过实验来证明。

创建一个spring boot项目,pom依赖如下:

<dependencies>
        <dependency>
            <groupId>com.netflix.hystrix</groupId>
            <artifactId>hystrix-core</artifactId>
            <version>1.5.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <version>1.7.25</version>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
    </dependencies>

下面创建一个调用服务的命令,在这个命令中设置了超时的时间为500ms,设置了一个是否超时标志isTimeout,使用该标志控制命令的执行是否超时,如下:

static class TestCommand extends HystrixCommand<String> {
        
        private boolean isTimeout;
        
        public TestCommand(boolean isTimeout) {
            super(Setter.withGroupKey(
                    HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
                    .andCommandPropertiesDefaults(
                            HystrixCommandProperties.Setter()
                                    .withExecutionTimeoutInMilliseconds(500)));
            this.isTimeout = isTimeout;
        }

        @Override
        protected String run() throws Exception {
            if(isTimeout) {
                Thread.sleep(800);
            } else {
                Thread.sleep(200);
            }           
            return "";
        }

        @Override
        protected String getFallback() {
            return "fallback";
        }
    }

可以通过 netflix.config.ConfigurationManager 配置类来将第一个条件的20次改为3次,这样更容易测试。然后同一个命令调用10次(并且在10秒内)

public static void main(String[] args) throws Exception {
        // 10秒内大于3次请求,满足第一个条件
        ConfigurationManager
                .getConfigInstance()
                .setProperty(
                        "hystrix.command.default.circuitBreaker.requestVolumeThreshold",
                        3);
        boolean isTimeout = true;
        for(int i = 0; i < 10; i++) {
            TestCommand c = new TestCommand(isTimeout);
            c.execute();
            HealthCounts hc = c.getMetrics().getHealthCounts();
            System.out.println("断路器状态:" + c.isCircuitBreakerOpen() + ", 
            请求数量:" + hc.getTotalRequests());
            //if(c.isCircuitBreakerOpen()) {
                //isTimeout = false;
                //System.out.println("============  断路器打开了,等待休眠期结束");
                //Thread.sleep(6000);
            //}
        }
    }

下面来看下执行的结果,10秒内请求次数3次满足第一个条件;3次皆为失败,则熔断器打开。

断路器状态:false, 请求数量:0
断路器状态:false, 请求数量:1
断路器状态:false, 请求数量:2
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3
断路器状态:true, 请求数量:3

2 熔断器的关闭

该命令的熔断器打开后,<font color='red'>则该命令默认会有5秒的睡眠时间,在这段时间内,之后的请求直接执行回退方法;5秒之后,会尝试执行一次命令,如果成功则关闭熔断器;否则,熔断器继续打开。</font>

将注释掉的代码是放开,

if(c.isCircuitBreakerOpen()) {
    isTimeout = false;
    System.out.println("============  断路器打开了,
    等待休眠期结束");
    Thread.sleep(6000);
}

查看执行结果:

断路器状态:false, 请求数量:0
断路器状态:false, 请求数量:1
断路器状态:false, 请求数量:2
断路器状态:true, 请求数量:3
============  断路器打开了,等待休眠期结束
断路器状态:false, 请求数量:0
断路器状态:false, 请求数量:0
断路器状态:false, 请求数量:0
断路器状态:false, 请求数量:3
断路器状态:false, 请求数量:3
断路器状态:false, 请求数量:5

断路器关闭之后,请求数量感觉有点问题,还需要深入理解?

3 线程池隔离

在Hystrix执行的流程中,除了要经过熔断器外,还需要过一关:执行命令的线程池或者信号量是否满载。如果满载,命令就不会执行,直接出发回退逻辑。

线程池针对的最小单元也是命令

创建一个spring boot项目,pom文件内容和上一文相同。首先创建我们调用服务的命令:

public class MyCommand extends HystrixCommand<String> {
    
    private int index;

    public MyCommand(int index) {
        super(Setter.withGroupKey(
        HystrixCommandGroupKey.Factory
        .asKey("TestGroupKey")));
        this.index = index;
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(500);
        System.out.println("执行方法,当前索引:" 
        + index);
        return "";
    }

    @Override
    protected String getFallback() {
        + index);
        return "";
    }
}

首先将线程次并发数量改为4次,然后通过queue方法异步执行命令。4次执行命令成功,2次执行了回退方法。

public class ThreadMain {

    public static void main(String[] args) throws Exception {
        ConfigurationManager.getConfigInstance().
        setProperty(default.coreSize, 4);
        for(int i = 0; i < 6; i++) {
            MyCommand c = new MyCommand(i);
            c.queue();
        }
        Thread.sleep(5000);
    }

}
执行回退,当前索引:4
执行回退,当前索引:5
执行方法,当前索引:2
执行方法,当前索引:0
执行方法,当前索引:1
执行方法,当前索引:3

4 信号量隔离

Hystrix默认是线程池隔离。信号量隔离就是每个命令的并发执行数量,当并发数高于阈值时,就不再执行命令。

public class SemaphoreMain {

    public static void main(String[] args) throws Exception {
        ConfigurationManager
        .getConfigInstance().setProperty(
                "hystrix.command.default.
                execution.isolation.strategy", ExecutionIsolationStrategy.SEMAPHORE);
.getConfigInstance().setProperty(
                "hystrix.command.default
                .execution.isolation.semaphore.maxConcurrentRequests", 3);
        for(int i = 0; i < 6; i++) {
            final int index = i;
            Thread t = new Thread() {
                public void run() {
                    MyCommand c = new MyCommand(index);
                    c.execute();
                }
            };
            t.start();
        }
        Thread.sleep(5000);
    }

}
执行回退,当前索引:3
执行回退,当前索引:5
执行回退,当前索引:0
执行方法,当前索引:2
执行方法,当前索引:4
执行方法,当前索引:1
上一篇下一篇

猜你喜欢

热点阅读