1. 程式人生 > >【Hystrix】四種fallback條件 例子

【Hystrix】四種fallback條件 例子

Hystrix會在以下四種情況下觸發fallback函式:

1.任務超過了"withExecutionTimeoutInMilliseconds"定義的超時時間;

2.任務丟擲了非HystrixBadRequestException的異常;

3.超過了執行緒池執行緒數目;

4.熔斷器開啟;

下面一一舉例。

1.超時

public class HelloCommand extends HystrixCommand<String> {
    protected HelloCommand() {
        super(
                Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("example"))
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(1000))
        );
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(2000);
        return "hello";
    }

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

main:

public class App 
{
    public static void main( String[] args )
    {
        try {
            System.out.println(new HelloCommand().execute());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

2.拋異常:

public class HelloCommand extends HystrixCommand<String> {
    protected HelloCommand() {
        super(
                Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("example"))
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(1000))
        );
    }

    @Override
    protected String run() throws Exception {
        throw new NullPointerException();
    }

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

main:

public class App 
{
    public static void main( String[] args )
    {
        try {
            System.out.println(new HelloCommand().execute());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3.超過執行緒池執行緒數目

public class HelloCommand extends HystrixCommand<String> {
    protected HelloCommand() {
        super(
                Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("example"))
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(2000))
                        .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(3))
        );
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(1000);
        return "hello";
    }

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

main: 

public class App 
{
    public static void main( String[] args )
    {
        try {
            for (int i = 0 ; i < 4; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(new HelloCommand().execute());
                    }
                }).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

結果會列印:

failed
hello
hello
hello

可以看到第四個任務先列印,說明在前三個執行時,第四個被直接判定為fail,第一個打印出來。原因就是執行緒池只配置了三個執行緒,所以第四個任務沒有被執行,直接fail。

當然執行緒池的maxQueueSize引數預設是-1,表示執行緒池的等待佇列為0,只要核心執行緒池沒有空閒,就fail。

4.熔斷器開啟。

那麼就得知道什麼時候熔斷器會開啟。

如果在10s有多於circuitBreakerSleepWindowInMilliseconds個請求到達,而且有多於circuitBreakerErrorThresholdPercentage百分比的請求失敗,則會導致熔斷器開啟,後續的circuitBreakerSleepWindowInMilliseconds時間段內的請求會直接fail。

public class HelloCommand extends HystrixCommand<String> {
    protected HelloCommand() {
        super(
                Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("example"))
                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500)
                                .withCircuitBreakerRequestVolumeThreshold(3)
                                .withCircuitBreakerErrorThresholdPercentage(75))
                        .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(100))
        );
    }

    @Override
    protected String run() throws Exception {
        Thread.sleep(1000);
        return "hello";
    }

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

main:

public class App 
{
    public static void main( String[] args )
    {
        try {
            for (int i = 0 ; i < 4; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(new HelloCommand().execute());
                    }
                }).start();
                Thread.sleep(1000);
            }
            for (int i = 0 ; i < 4; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(new HelloCommand().execute());
                    }
                }).start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

結果:

failed
failed
failed
failed
failed
failed
failed
failed

總共會有8個failed列印,因為總共8個任務。

但是前四個是間隔一定時間列印一個,後面四個是同時秒打出來。

因為配置了circuitBreakerSleepWindowInMilliseconds為3。第一個for迴圈裡的四個任務正好保證了每10秒內有大於3個任務達到,那麼會觸發熔斷器監控邏輯,這個四個任務執行時間是1s,而超時閾值是0.5s,那麼每一個任務最重都會超時。所以第四個任務fail之後,熔斷器已經打開了。這四個任務會執行到run方法,所以是執行了0.5s才打印的。

第二個for迴圈裡的任務根本不會執行,因為執行時熔斷器打開了,沒有sleep直到超時,而是直接秒fail,所以同時列印。