Hystrix原生用法——前言
虽说目前大部分都是结合SpringBoot,利用注解加持aop的原理实现Hystrix的应用,但是了解原生的Hystrix单个命令对象的创建,有利于我们深入Hystrix源码的时候做一个基础准备,这样在结合SpringBoot与Hystrix做源码分析的时候就只需要知道命令对象是如何利用Spring创建以及整合http请求就行
Hystrix原生用法——命令类型
简单命令
public class HystrixCommbandTest {
/**
* @(1)命令者类
*/
public static class HelloWorldHystrixCommand extends HystrixCommand<String> {
private final String name;
public HelloWorldHystrixCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
/**
* 利用命令者设计模式中,我们原生的代码被封装了run中
* @return
* @throws Exception
*/
protected String run() throws Exception {
return "业务处理成功";
}
}
/**
* @(2)代码测试
*/
public static void main(String[] args) {
/**
* @同步执行run命令
*/
String result = new HelloWorldHystrixCommand("test").execute();
/**
* @异步执行run命令
* 返回Future对象,然后就可以继续业务处理,在需要用到命令回调的地方调用future.get,但是如果还没返回值get方法也会阻塞一下
*/
Future<String> future = new HelloWorldHystrixCommand("test").queue();
System.out.println(result);
}
}
响应式命令
响应式命令借用了RxJava的api实现了观察对象和被观察对象的编程范式,主要区别在于除了Hystrix支持的熔断器和隔离操作外,还提供了更加丰富的交互回调:中间回调、完成回调、错误回调
@Slf4j
public class HystrixObserverTest {
/**
* @(1)命令者类
* 处理订阅逻辑
*/
public static class HelloWorldHystrixObservableCommand extends HystrixObservableCommand<String> {
private final String name;
protected HelloWorldHystrixObservableCommand(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
protected Observable<String> construct() {
return createNormalObservable();
}
protected Observable<String> resumeWithFallback() {
return createFallBackObservable();
}
/**
* @正常被观察对象——处理逻辑
*/
private Observable<String> createNormalObservable() {
return (Observable<String>) Observable.create(new Observable.OnSubscribe<String>() {
/**
* 当收到订阅事件时调用
* @param observer 发起订阅的订阅者对象
*/
public void call(Subscriber<? super String> observer) {
try {
/**
* 调用订阅者的回调函数,传递数据
*/
if (!observer.isUnsubscribed()) {
/**
* @onCompleted前可以多次
* 调用每个观察者的者的onNext方法
*/
observer.onNext("Hello1" + " thread:" + Thread.currentThread().getName());
observer.onNext("Hello2" + " thread:" + Thread.currentThread().getName());
/**
* @onCompleted嗨皮的方法不能被执行到
*/
observer.onCompleted();
observer.onNext("abc");
}
} catch (Exception e) {
/**
* 异常回调函数
*/
observer.onError(e);
}
}
});
}
/**
* @降级被观察对象——处理逻辑
*/
private Observable<String> createFallBackObservable() {
return (Observable<String>) Observable.create(new Observable.OnSubscribe<String>() {
public void call(Subscriber<? super String> observer) {
try {
if (!observer.isUnsubscribed()) {
observer.onNext("Hello1" + " thread:" + Thread.currentThread().getName());
observer.onNext("Hello2" + " thread:" + Thread.currentThread().getName());
observer.onCompleted();
observer.onNext("abc");
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
/**
* @(2)代码测试
*/
public static void main(String[] args) throws Exception {
Observable<String> observable = new HelloWorldHystrixObservableCommand("test").observe();
/**
* @发起订阅事件(1)
* 在被观察者对象中会调用这些回调函数
* 一般来讲onNext就是我们的业务回调
*/
observable.subscribe(new Observer<String>() {
public void onNext(String msg) {
log.info("Observer————————next————————" + msg);
}
public void onCompleted() {
log.info("Observer————————onCompleted————————");
}
public void onError(Throwable e) {
log.info("Observerr————————onError————————");
}
});
/**
* @发起订阅事件(2)
* 只接受onNext的回调
*/
observable.subscribe(new Action1<String>() {
public void call(String msg) {
log.info("Action1————————call————————" + msg);
}
});
}
}
}
Hystrix原生用法——服务降级
public class HystrixFallBackTest {
private static final Integer TIME_OUT_TIME=1000;
private static final Integer SERVICE_HANDLE_TIME=1000;
/**
* @(1)命令者类
*/
public static class HelloWorldHystrixObservableCommand extends HystrixCommand<String> {
public HelloWorldHystrixObservableCommand(String commandName,String groupName,String threadName) {
super(Setter
/**
* @配置当前命令————————分组配置
* 利用自定义唯一key配置线程池隔离
*/
.withGroupKey(
HystrixCommandGroupKey.Factory.asKey(groupName))//该命令属于哪一个组,可以帮助我们更好的组织命令
.andCommandKey(HystrixCommandKey.Factory.asKey(commandName))//该命令的名称
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(threadName))
/**
*@配置当前命令————————线程池配置
*/
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter()
.withCoreSize(200)) // 配置线程池里的线程数,设置足够多线程,以防未熔断却打满threadpool
/**
*@配置当前命令————————熔断器默认配置
*/
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(TIME_OUT_TIME)//超时配置
.withCircuitBreakerEnabled(true)
.withCircuitBreakerRequestVolumeThreshold(3)
.withCircuitBreakerErrorThresholdPercentage(80))
);
}
/**
* 正常业务返回
* @return
* @throws Exception
*/
protected String run() throws Exception {
Thread.sleep(SERVICE_HANDLE_TIME);
return "业务处理成功";
}
/**
* 服务降级返回
* @return
*/
protected String getFallback() {
return "服务器繁忙";
}
}
/**
* @(2)代码测试
*/
public static void main(String[] args) {
try {
/**
* 创建并直接执行命令对象
*/
String serviceResult = new HelloWorldHystrixObservableCommand(
"myCommand",
"myGroup",
"myThreadPool")
.execute();
System.out.println(serviceResult);
}catch (Exception e){
e.printStackTrace();
}
}
}
Hystrix原生用法——服务配置
在上述代码中我们明确知道利用Setter方法可以轻松建造出Hystrix命令的配置内容,那我们接下来详细介绍一下这些配置
.(1)首先进入Setter内部类
final public static class Setter {
protected final HystrixCommandGroupKey groupKey;//该命令属于哪一个组,可以帮助我们更好的组织命令
protected HystrixCommandKey commandKey;//该命令的名称
protected HystrixThreadPoolKey threadPoolKey;//该命令所属线程池的名称,同样配置的命令会共享同一线程池,若不配置,会默认使用GroupKey作为线程池名称。
protected HystrixCommandProperties.Setter commandPropertiesDefaults;//命令的一些设置,包括断路器的配置,隔离策略,降级设置,以及一些监控指标等
protected HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults;//关于线程池的配置,包括线程池大小,排队队列的大小等。
}
(2)命令配置:HystrixCommandProperties
//使用命令调用隔离方式,默认:采用线程隔离,ExecutionIsolationStrategy.THREAD
private final HystrixProperty<ExecutionIsolationStrategy> executionIsolationStrategy;
//使用线程隔离时,调用超时时间,默认:1秒
private final HystrixProperty<Integer> executionIsolationThreadTimeoutInMilliseconds;
//线程池的key,用于决定命令在哪个线程池执行
private final HystrixProperty<String> executionIsolationThreadPoolKeyOverride;
//使用信号量隔离时,命令调用最大的并发数,默认:10
private final HystrixProperty<Integer> executionIsolationSemaphoreMaxConcurrentRequests;
//使用信号量隔离时,命令fallback(降级)调用最大的并发数,默认:10
private final HystrixProperty<Integer> fallbackIsolationSemaphoreMaxConcurrentRequests;
//是否开启fallback降级策略 默认:true
private final HystrixProperty<Boolean> fallbackEnabled;
// 使用线程隔离时,是否对命令执行超时的线程调用中断(Thread.interrupt())操作.默认:true
private final HystrixProperty<Boolean> executionIsolationThreadInterruptOnTimeout;
// 统计滚动的时间窗口,默认:5000毫秒circuitBreakerSleepWindowInMilliseconds
private final HystrixProperty<Integer> metricsRollingStatisticalWindowInMilliseconds;
// 统计窗口的Buckets的数量,默认:10个,每秒一个Buckets统计
private final HystrixProperty<Integer> metricsRollingStatisticalWindowBuckets; // number of buckets in the statisticalWindow
//是否开启监控统计功能,默认:true
private final HystrixProperty<Boolean> metricsRollingPercentileEnabled;
// 是否开启请求日志,默认:true
private final HystrixProperty<Boolean> requestLogEnabled;
//是否开启请求缓存,默认:true
private final HystrixProperty<Boolean> requestCacheEnabled; // Whether request caching is enabled.
(3)熔断器配置:Circuit Breaker
// 熔断器在整个统计时间内是否开启的阀值,默认20秒。也就是10秒钟内至少请求20次,熔断器才发挥起作用
private final HystrixProperty<Integer> circuitBreakerRequestVolumeThreshold;
//熔断器默认工作时间,默认:5秒.熔断器中断请求5秒后会进入半打开状态,放部分流量过去重试
private final HystrixProperty<Integer> circuitBreakerSleepWindowInMilliseconds;
//是否启用熔断器,默认true. 启动
private final HystrixProperty<Boolean> circuitBreakerEnabled;
//默认:50%。当出错率超过50%后熔断器启动.
private final HystrixProperty<Integer> circuitBreakerErrorThresholdPercentage;
//是否强制开启熔断器阻断所有请求,默认:false,不开启
private final HystrixProperty<Boolean> circuitBreakerForceOpen;
//是否允许熔断器忽略错误,默认false, 不开启
private final HystrixProperty<Boolean> circuitBreakerForceClosed;
(4)命令合并配置:Collapser
//请求合并是允许的最大请求数,默认: Integer.MAX_VALUE
private final HystrixProperty<Integer> maxRequestsInBatch;
//批处理过程中每个命令延迟的时间,默认:10毫秒
private final HystrixProperty<Integer> timerDelayInMilliseconds;
//批处理过程中是否开启请求缓存,默认:开启
private final HystrixProperty<Boolean> requestCacheEnabled;
(5)线程池配置:ThreadPool
/**
配置线程池大小,默认值10个.
建议值:请求高峰时99.5%的平均响应时间 + 向上预留一些即可
*/
HystrixThreadPoolProperties.Setter().withCoreSize(int value)
/**
配置线程值等待队列长度,默认值:-1
建议值:-1表示不等待直接拒绝,测试表明线程池使用直接决绝策略+ 合适大小的非回缩线程池效率最高.所以不建议修改此值。
当使用非回缩线程池时,queueSizeRejectionThreshold,keepAliveTimeMinutes 参数无效
*/
HystrixThreadPoolProperties.Setter().withMaxQueueSize(int value)
Hystrix原生用法——最终奥义
最后Hystrix其实就是两个应用,一个是aop注解应用实现代理对象的创建,来生成命令调用原生api;
第二就是很多框架实现了HystrixCommand接口,我们可以自己深入去看看
Hystrix原理——执行流程
1) 快速模式
如果调用服务失败了,那么立即失败并返回。
2) 故障转移
如果调用服务失败了,那么调用备用服务,因为备用服务也可能失败,所以也可能有再下一级的备用服务,如此形成一个级联。例如:如果服务提供者不响应,则从缓存中取默认数据。
3) 主次模式
举个例子:开发中需要上线一个新功能,但为了防止新功能上线失败可以回退到老的代码,我们会做一个开关比做一个配置开关,可以动态切换到老代码功能。那么Hystrix它是使用通过一个配置来在两个command中进行切换。
整体流程
Hystrix原生用法——结果缓存(待完善)
.
Hystrix原生用法——合并请求(待完善)
合并请求存在额外开销,所以需要根据依赖服务调用的实际情况决定是否使用此功能
主要考虑下面两个方面:
a) 请求命令本身的延迟
对于单次请求而言,如果[单次请求平均时间/时间窗口]越小,对于单次请求的性能形象越小。如果依赖服务的请求命令本身是一个高延迟的命令,那么可以使用请求合并器,因为延迟时间窗的时间消耗显得微不足道了。
b) 并发量
时间窗口内并发量越大,合并求情的性能提升越明显。如果一个时间窗内只有少数几个请求,那么就不适合使用请求合并器。相反,如果一个时间窗内具有很高的并发量,那么使用请求合并器可以有效减少网络连接数量并极大提升系统吞吐量,此时延迟时间窗所增加的消耗就可以忽略不计了。
.