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,所以同时打印。