一、降級
所謂降級,就是指在在Hystrix執(zhí)行非核心鏈路功能失敗的情況下芹血,我們如何處理,比如我們返回默認值等楞慈。如果我們要回退或者降級處理幔烛,代碼上需要實現(xiàn)HystrixCommand.getFallback()方法或者是HystrixObservableCommand. HystrixObservableCommand()。
public class CommandHelloFailure extends HystrixCommand<String> {
private final String name;
public CommandHelloFailure(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
throw new RuntimeException("this command always fails");
}
@Override
protected String getFallback() {
return "Hello Failure " + name + "!";
}
}
二囊蓝、Hystrix的降級回退方式
Hystrix一共有如下幾種降級回退模式:
1饿悬、Fail Fast 快速失敗
@Override
protected String run() {
if (throwException) {
throw new RuntimeException("failure from CommandThatFailsFast");
} else {
return "success";
}
}
如果我們實現(xiàn)的是HystrixObservableCommand.java則 重寫 resumeWithFallback方法
@Override
protected Observable<String> resumeWithFallback() {
if (throwException) {
return Observable.error(new Throwable("failure from CommandThatFailsFast"));
} else {
return Observable.just("success");
}
}
2、Fail Silent 無聲失敗
返回null聚霜,空Map乡恕,空List
@Override
protected String getFallback() {
return null;
}
@Override
protected List<String> getFallback() {
return Collections.emptyList();
}
@Override
protected Observable<String> resumeWithFallback() {
return Observable.empty();
}
3、Fallback: Static 返回默認值
回退的時候返回靜態(tài)嵌入代碼中的默認值俯萎,這樣就不會導致功能以Fail Silent的方式被清楚傲宜,也就是用戶看不到任何功能了。而是按照一個默認的方式顯示夫啊。
@Override
protected Boolean getFallback() {
return true;
}
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.just( true );
}
4函卒、Fallback: Stubbed 自己組裝一個值返回
當我們執(zhí)行返回的結果是一個包含多個字段的對象時,則會以Stubbed 的方式回退撇眯。Stubbed 值我們建議在實例化Command的時候就設置好一個值报嵌。以countryCodeFromGeoLookup為例,countryCodeFromGeoLookup的值熊榛,是在我們調用的時候就注冊進來初始化好的锚国。CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "china");主要代碼如下:
public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> {
protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.customerId = customerId;
this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
}
@Override
protected UserAccount getFallback() {
/**
* Return stubbed fallback with some static defaults, placeholders,
* and an injected value 'countryCodeFromGeoLookup' that we'll use
* instead of what we would have retrieved from the remote service.
*/
return new UserAccount(customerId, "Unknown Name",
countryCodeFromGeoLookup, true, true, false);
}
5、Fallback: Cache via Network 利用遠程緩存
通過遠程緩存的方式玄坦。在失敗的情況下再發(fā)起一次remote請求血筑,不過這次請求的是一個緩存比如redis绘沉。由于是又發(fā)起一起遠程調用,所以會重新封裝一次Command豺总,這個時候要注意车伞,執(zhí)行fallback的線程一定要跟主線程區(qū)分開,也就是重新命名一個ThreadPoolKey喻喳。
public class CommandWithFallbackViaNetwork extends HystrixCommand<String> {
private final int id;
protected CommandWithFallbackViaNetwork(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand")));
this.id = id;
}
@Override
protected String run() {
// RemoteServiceXClient.getValue(id);
throw new RuntimeException("force failure for example");
}
@Override
protected String getFallback() {
return new FallbackViaNetwork(id).execute();
}
private static class FallbackViaNetwork extends HystrixCommand<String> {
private final int id;
public FallbackViaNetwork(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand"))
// use a different threadpool for the fallback command
// so saturating the RemoteServiceX pool won't prevent
// fallbacks from executing
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")));
this.id = id;
}
@Override
protected String run() {
MemCacheClient.getValue(id);
}
@Override
protected String getFallback() {
// the fallback also failed
// so this fallback-of-a-fallback will
// fail silently and return null
return null;
}
}
}
6另玖、Primary + Secondary with Fallback 主次方式回退(主要和次要)
這個有點類似我們日常開發(fā)中需要上線一個新功能,但為了防止新功能上線失敗可以回退到老的代碼表伦,我們會做一個開關比如使用zookeeper做一個配置開關谦去,可以動態(tài)切換到老代碼功能。那么Hystrix它是使用通過一個配置來在兩個command中進行切換蹦哼。
/**
* Sample {@link HystrixCommand} pattern using a semaphore-isolated command
* that conditionally invokes thread-isolated commands.
*/
public class CommandFacadeWithPrimarySecondary extends HystrixCommand<String> {
private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary", true);
private final int id;
public CommandFacadeWithPrimarySecondary(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand"))
.andCommandPropertiesDefaults(
// we want to default to semaphore-isolation since this wraps
// 2 others commands that are already thread isolated
// 采用信號量的隔離方式
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)));
this.id = id;
}
//通過DynamicPropertyFactory來路由到不同的command
@Override
protected String run() {
if (usePrimary.get()) {
return new PrimaryCommand(id).execute();
} else {
return new SecondaryCommand(id).execute();
}
}
@Override
protected String getFallback() {
return "static-fallback-" + id;
}
@Override
protected String getCacheKey() {
return String.valueOf(id);
}
private static class PrimaryCommand extends HystrixCommand<String> {
private final int id;
private PrimaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand"))
.andCommandPropertiesDefaults(
// we default to a 600ms timeout for primary
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));
this.id = id;
}
@Override
protected String run() {
// perform expensive 'primary' service call
return "responseFromPrimary-" + id;
}
}
private static class SecondaryCommand extends HystrixCommand<String> {
private final int id;
private SecondaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand"))
.andCommandPropertiesDefaults(
// we default to a 100ms timeout for secondary
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));
this.id = id;
}
@Override
protected String run() {
// perform fast 'secondary' service call
return "responseFromSecondary-" + id;
}
}
public static class UnitTest {
@Test
public void testPrimary() {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
//將屬性"primarySecondary.usePrimary"設置為true鳄哭,則走PrimaryCommand;設置為false翔怎,則走SecondaryCommand
ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", true);
assertEquals("responseFromPrimary-20", new CommandFacadeWithPrimarySecondary(20).execute());
} finally {
context.shutdown();
ConfigurationManager.getConfigInstance().clear();
}
}
@Test
public void testSecondary() {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
//將屬性"primarySecondary.usePrimary"設置為true,則走PrimaryCommand杨耙;設置為false赤套,則走SecondaryCommand
ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", false);
assertEquals("responseFromSecondary-20", new CommandFacadeWithPrimarySecondary(20).execute());
} finally {
context.shutdown();
ConfigurationManager.getConfigInstance().clear();
}
}
}
}
三、總結
降級的處理方式珊膜,返回默認值容握,返回緩存里面的值(包括遠程緩存比如redis和本地緩存比如jvmcache)。
但回退的處理方式也有不適合的場景:
1车柠、寫操作
2剔氏、批處理
3、計算
以上幾種情況如果失敗竹祷,則程序就要將錯誤返回給調用者谈跛。
轉載請注明出處,并附上鏈接http://www.reibang.com/p/266cba713eb1
參考資料:https://github.com/Netflix/Hystrix/wiki