hystrix初识

Hystrix熔断框架初识

熔断的背景主要是现如今服务拆分的大体趋势下,如果一个服务A依赖于服务B,C,D,那么称服务B,C,D为服务A的下游,考虑一些情况,在某些原因(突发大流量打垮了下游服务B),因为A服务依赖于服务B,服务B的突然宕机将导致服务A也变得不可用,从而拖垮一整条业务线,为了避免这种极端情况出现,就需要某种机制在下游服务出现异常时切断下游,进行主动降级。
Hystrix框架解决的就是 什么时候熔断,什么时候探测服务是否可用,当依赖恢复正常时,会将原本熔断的应用重新启用,Hystrix通过添加延迟容错和失败容错的逻辑来帮助处理服务之间的交互,使得正常情况下各个服务之间的正常调用,多个微服务组合成一个大服务,极端情况下(大流量情况下)如果有服务超时或者调用失败次数过多,熔断异常服务,保证主服务至少能够正常调用。

Hystrix主要解决的问题

对外以来包括第三方类库的依赖提供延迟和失败保护
阻断传递失败,防止雪崩
快速失败并及时恢复
合理的fallback和优雅降级
提供近实时的监控,告警和操作控制

问题根源:
一个服务的延迟会导致单位时间内资源一直被占用,应用的其他请求也会延迟,然后队列堆积,线程和系统资源部释放,可能引发整个系统的级联雪崩。

Hystrix实现的方法

·hystrix把每个依赖都进行隔离,对依赖的调用全部包装成HystrixCommand 或者 HystrixObservableCommand
·对依赖的调用耗时设置阈值,如果超过阈值直接判定超时
·对每个依赖维护一个连接池,如果连接池满直接拒绝访问
·hystrix评估调用失败,调用超时,线程拒绝,调用成功四种情况的占比,如果超过制定的阈值直接熔断处理,对依赖的访问走fallback逻辑
·熔断生效之后,会在设定的时间内放出一个请求来探测依赖是否恢复,依赖的应用恢复后关闭熔断
·修改hystrix配置近实时生效

Hystrix Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package Demo;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class CommandHelloWorld extends HystrixCommand<String> {

private final String name;

public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}

@Override
protected String run() {
return "Hello " + name + "!";
}
}

Hystrix把执行都包装成一个HystrixCommand 然后用线程池实现多个依赖执行的隔离
上述HelloWord的Demo 实现了类似分组key的构造方法;
实际上Hystrix每一个command都有对应的commandKey作为command组内的唯一性标记,默认是当前类的名字,每一个command也有一个归属分组,归属分组和commandKey都是为了方便hystrix进行监控,报警。hystrixCommand使用的线程池也有线程池key,以及相关配置

Hystrix配置

hystrix配置在实现HystrixCommand这个抽象类的具体类(也就是我们自定义的执行类)构造函数中由super方式添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public CommandHelloWorld(){
super(Setter
//分组key
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("helloWorldGroup"))
//commandKey
.andCommandKey(HystrixCommandKey.Factory.asKey("commandHelloWorld"))
//command属性配置
.andCommandPropertiesDefaults(HystrixPropertiesCommandDefault.Setter().withCircuitBreakerEnabled(true).withCircuitBreakerForceOpen(true))
//线程池key
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("helloWorld_Poll"))
//线程池属性配置
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20).withMaxQueueSize(25))
);
}

使用缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class CachedCommand extends HystrixCommand<String> {

private String key;

private static final HystrixCommandKey COMMANDKEY = HystrixCommandKey.Factory.asKey("CachedCommand_cmd");

public CachedCommand(String key){
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("CachedCommand"))
.andCommandKey(COMMANDKEY));
this.key = key;
}

@Override
protected String getCacheKey() {
return this.key;
}

public static void flushCache(String key) {
HystrixRequestCache.getInstance(COMMANDKEY,
HystrixConcurrencyStrategyDefault.getInstance()).clear(key);
}

@Override
protected String run() throws Exception {
return "hello "+ key +" !";
}
}


String hahahah = "hahahah";
CachedCommand cachedCommand = new CachedCommand(hahahah);
CachedCommand cachedCommand2 = new CachedCommand(hahahah);
CachedCommand cachedCommand3 = new CachedCommand(hahahah);
cachedCommand.execute();
System.out.println("来自缓存:" + cachedCommand.isResponseFromCache() );
cachedCommand2.execute();
System.out.println("来自缓存:" + cachedCommand2.isResponseFromCache() );
//清除缓存
cachedCommand2.flushCache(hahahah);

System.out.println("来自缓存:" + cachedCommand3.isResponseFromCache() + " "+ cachedCommand3.execute());
context.shutdown();

/**
* Result:
* 来自缓存:false
* 来自缓存:true
* 来自缓存:false
*/

上述CachedCommand 封装了HytrixCommand方法,并且实现了getCachekey()方法开启请求缓存
在同一个上下文中,多次请求同一个Command,返回值不会发生变化的时候可以使用

fallback

1_ 单个fallBack
fallBack为HystrixCommand执行失败的时候走的备用逻辑,通过实现HystrixCommand的fallback方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package Demo;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class CommandWithFallBack extends HystrixCommand<String> {

private final boolean throwException;

public CommandWithFallBack(boolean throwException) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.throwException = throwException;
}

@Override
protected String run() {
if (throwException) {
throw new RuntimeException("failure from CommandWithFallBack");
} else{
return "success";
}
}

@Override
protected String getFallback(){
return "I'm fallBack";
}

}

当主方法run()异常,或者抛入RuntimeException时,Hystrix框架会主动调用fallback方法,前提是实现了getFallback方法

多级fallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package Demo;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;

public class CascadeFallBack extends HystrixCommand<String> {

private final boolean cascadeFallBack;

public CascadeFallBack(boolean cascadeFallBack){
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.cascadeFallBack = cascadeFallBack;
}

@Override
public String run(){
if(cascadeFallBack){
throw new RuntimeException("failure CascadeFallBack");
}else{
return "success";
}
}

@Override
public String getFallback(){
return new CommandWithFallBack(true).execute();
}

}

Hystrix的优点

1.多业务依赖隔离,不会相互影响,并可以根据需要给不同的依赖分不同的线程资源
2.业务依赖fail-fast
3.依赖服务恢复,能合理感知并恢复对服务的依赖
4.对依赖服务限流,Hystrix对每个业务的依赖都包装成了一个command,并分配线程池,线程池的容量也就是能下发请求的能力,防止雪崩

HystrixCommand探测超时

待补充

##