Hystrix的使用(一)
- 将业务逻辑封装在命令中,交给Hystrix去执行,Hystrix则会帮我们保护程序
- 命令如何执行?
- 其中有什么配置?
- 回退有什么相关配置?
1、内部流程
- 从之前的例子看,当服务器出现无响应现象的时候,Hystrix会自动使用容错机制,看似简单,其实有一套较为复杂的执行逻辑
- 第一步:在命令开始执行时,会做一些准备工作(如:为命令创建相应的线程池等)
- 第二步:判断是否打开了缓存,打开了缓存就直接查找缓存并返回结果
- 第三步:判断断路器是否打开,如果打开了,就表示链路不可以用,直接执行回退方法
- 第四步:判断线程池、信号量等条件(如:线程池超负荷,则返回回退方法,否则,就去执行命令的内容)
- 第五步:执行命令,判断是否要对断路器进行处理,执行完成后,如果满足一定条件,则需要开启断路器
- 整个流程最主要的点,就是在于断路器是否被打开
2、命令执行
- toObservable:返回一个最原始的可观察的实例,是一个RxJava的类,执行这个对象可观察命令的执行过程,并且将执行的信息转递给订阅者(命令不会马上执行,只有当返回的可观察实例被订阅之后,才会真正执行)
- observe:调用toObservable获得最原始的可观察实例后,再使用一个replay subject作为原始toObservable的订阅者(会立刻执行)
- queue
- execute:调用queue的get方法,同步
- 回顾
HttpResponse response = httpClient.execute(httpGet);
- 底层使用RxJava执行命令
- 执行的时候返回一个可观察的实例
- 这样,就可以监控命令的执行,execute是最基础的命令执行方法
package com.atm.cloud;
import rx.Observable;
import rx.Observer;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
public class CommandRunMain {
public static void main(String[] args) throws Exception {
RunCommand c1 = new RunCommand("observe method");
c1.observe();
RunCommand c2 = new RunCommand("toObservable method");
// 只返回一个可观察的对象,并不会马上执行
// 只有当Observable真正被订阅之后,才会执行
Observable ob = c2.toObservable();
// 执行订阅,则会开始执行
ob.subscribe(new Observer<String>() {
// 命令执行完成之后
public void onCompleted() {
System.out.println("命令执行完成之后...");
}
// 命令有错误
public void onError(Throwable e) {
}
// 命令执行返回
public void onNext(String t) {
System.out.println("执行返回:" + t);
}
});
Thread.sleep(1000);
}
static class RunCommand extends HystrixCommand<String> {
String msg;
public RunCommand(String message) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.msg = message;
}
@Override
protected String run() throws Exception {
System.out.println(msg);
return "success";
}
}
}
3、Hystrix配置
配置超时时间:
配置key
- https://github.com/netflix
package com.atm.cloud.config;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
public class MyTimeOutConfig extends HystrixCommand<String> {
public MyTimeOutConfig() {
// 第二种方法:
// super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
// 超时时间设置成2s
super(Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionTimeoutInMilliseconds(2000)));
// 第一种方法:设置超时时间(设置成2s)
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(
20000);
}
@Override
protected String run() throws Exception {
// 手动延迟3s
// Thread.sleep(3000);
System.out.println("执行命令...");
return "Success";
}
// 回退方法
@Override
protected String getFallback() {
System.out.println("执行回退...");
// return super.getFallback();
return "Fallback...";
}
}
MyTimeOutConfig c = new MyTimeOutConfig();
String result = c.execute();
System.out.println(result);
3.1、全局超时配置
package com.atm.cloud;
import com.netflix.config.ConfigurationManager;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
/**
* 设置全局超时时间
*
*/
public class PropertyMain {
public static void main(String[] args) {
ConfigurationManager
.getConfigInstance()
.setProperty(
"hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds",
2000);
PropertyCommand c = new PropertyCommand();
String result = c.execute();
System.out.println(result);
}
static class PropertyCommand extends HystrixCommand<String> {
public PropertyCommand() {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory
.asKey("ExampleGroup")));
}
@Override
protected String run() throws Exception {
Thread.sleep(3500);
return "success";
}
@Override
protected String getFallback() {
return "fallback";
}
}
}
3.2、配置key
- 有三个主要名称可为命令/命令组进行命名
- 命令组名称
- 命令名称
- 线程池名称
package com.atm.cloud;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixThreadPoolKey;
public class KeyCommand extends HystrixCommand<String> {
public KeyCommand() {
// 设置组名,每一个命令,Hystrix底层都会分配一个线程池去执行命令,它会使用一个map来维护这些线程池
// 如果不提供线程池名称,则默认是使用组名作为线程池的key:Map<String,Pool>
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GroupKey"))
.andCommandKey(HystrixCommandKey.Factory.asKey("CommandKey"))
.andThreadPoolKey(
HystrixThreadPoolKey.Factory.asKey("ThreadPoolKey")));
}
@Override
protected String run() throws Exception {
return null;
}
}
3.3、回退方法
- 执行超时等失败情况(前面小节一直在测试超时回退)
- 断路器打开(断路器:想象成保险丝)
- 线程池满载
package com.atm.cloud.config;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import com.netflix.hystrix.HystrixCommandProperties;
public class FallbackMain {
public static void main(String[] args) {
FallbackCommand c = new FallbackCommand();
String result = c.execute();
System.out.println(result);
}
static class FallbackCommand extends HystrixCommand<String> {
public FallbackCommand() {
// 设置组名、打开断路器(不推荐代码打开断路器,实际开发中是实现特定逻辑才打开断路器,如频繁请求同一个服务均失败才打开)
super(Setter.withGroupKey(
HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withCircuitBreakerForceOpen(true)));// 强制打开断路器
}
@Override
protected String run() throws Exception {
return "success";
}
@Override
protected String getFallback() {
return "fallback";
}
}
}
- 线程池满载情况回退,暂不做测试,后面小节进行测试
3.4、回退模式
- Hystrix的回退机制比较灵活,可以在A命令中执行B中回退,如果B也执行失败,同样会触发B命令的回退,这样就形成一种链式的命令执行
- 假设一个转账命令包含调用A银行扣款、B银行加款两个命令,其中一个命令失败后,再执行转账命令回退
- 要做到多命令只执行一次回退的效果,CommandA和CommandB,不能有回退方法,如果CommandA命令执行失败,并且该命令有回退方法,此时将不会执行MainCommand的回退方法