下面是在HystrixCommand中使用的常用模式
快速失败
最常用的基础执行是只做单个的事情,没有回退行为。 假如有任何的失败,将抛出例外
public class CommandThatFailsFast extends HystrixCommand<String> {
private final boolean throwException;
public CommandThatFailsFast(boolean throwException) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.throwException = throwException;
}
@Override
protected String run() {
if (throwException) {
throw new RuntimeException("failure from CommandThatFailsFast");
} else {
return "success";
}
}
单元测试如下:
@Test
public void testSuccess() {
assertEquals("success", new CommandThatFailsFast(false).execute());
}
@Test
public void testFailure() {
try {
new CommandThatFailsFast(true).execute();
fail("we should have thrown an exception");
} catch (HystrixRuntimeException e) {
assertEquals("failure from CommandThatFailsFast", e.getCause().getMessage());
e.printStackTrace();
}
}
无声失败
无声的失败等同于返回一个空的响应或者删除功能,它通过返回null,空的map对象,空的list或者其他类似的响应实现。 通常通过HystrixCommand实例中的getFallback() 方法实现
public class CommandThatFailsSilently extends HystrixCommand<String> {
private final boolean throwException;
public CommandThatFailsSilently(boolean throwException) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.throwException = throwException;
}
@Override
protected String run() {
if (throwException) {
throw new RuntimeException("failure from CommandThatFailsFast");
} else {
return "success";
}
}
@Override
protected String getFallback() {
return null;
}
}
@Test
public void testSuccess() {
assertEquals("success", new CommandThatFailsSilently(false).execute());
}
@Test
public void testFailure() {
try {
assertEquals(null, new CommandThatFailsSilently(true).execute());
} catch (HystrixRuntimeException e) {
fail("we should not get an exception as we fail silently with a fallback");
}
}
另外的返回空list的实现如下
@Override
protected List<String> getFallback() {
return Collections.emptyList();
}
回退:静态的
一些回退能返回在代码中硬编码的值。它不能引起特性或将被移除服务(如同无声失败经常处理的方法),但是执行默认的行为逻辑。
如:假如一个command基于用户的凭证返回一个true/false值,但是如果command执行失败,它将默认返回true
@Override
protected Boolean getFallback() {
return true;
}
回退:存根化
一个存根回退典型的被用于包含多个字段的一个组合对象被返回时。它们其中的一部分能被其它请求状态来决定。当其它字段被设置为默认值。
能使用的这些存根值得示例有:
cookies
请求参数和头
从先于目前失败请求的以前的服务请求结果
存根值能被静态的从请求范围内取到,但是典型的它建议在command初始化时注入使用。下面例子列举了countryCodeFromGeoLookup注入的过程
public class CommandWithStubbedFallback extends HystrixCommand<UserAccount> {
private final int customerId;
private final String countryCodeFromGeoLookup;
/**
* @param customerId
* The customerID to retrieve UserAccount for
* @param countryCodeFromGeoLookup
* The default country code from the HTTP request geo code lookup used for fallback.
*/
protected CommandWithStubbedFallback(int customerId, String countryCodeFromGeoLookup) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.customerId = customerId;
this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
}
@Override
protected UserAccount run() {
// fetch UserAccount from remote service
// return UserAccountClient.getAccount(customerId);
throw new RuntimeException("forcing failure for example");
}
@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);
}
public static class UserAccount {
private final int customerId;
private final String name;
private final String countryCode;
private final boolean isFeatureXPermitted;
private final boolean isFeatureYPermitted;
private final boolean isFeatureZPermitted;
UserAccount(int customerId, String name, String countryCode,
boolean isFeatureXPermitted,
boolean isFeatureYPermitted,
boolean isFeatureZPermitted) {
this.customerId = customerId;
this.name = name;
this.countryCode = countryCode;
this.isFeatureXPermitted = isFeatureXPermitted;
this.isFeatureYPermitted = isFeatureYPermitted;
this.isFeatureZPermitted = isFeatureZPermitted;
}
}
}
下面单元测试证明了这种结果
@Test
public void test() {
CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "ca");
UserAccount account = command.execute();
assertTrue(command.isFailedExecution());
assertTrue(command.isResponseFromFallback());
assertEquals(1234, account.customerId);
assertEquals("ca", account.countryCode);
assertEquals(true, account.isFeatureXPermitted);
assertEquals(true, account.isFeatureYPermitted);
assertEquals(false, account.isFeatureZPermitted);
}
回退:通过网络缓存
有时一个后端的服务失败时,一个旧版本的数据能够从缓存服务(如memcached)中取得。
由于回退如果重掉网络可能导致另外的失败,因此需要通过另外的HystrixCommand转换。
另外重要的是,回退command应当在独立的线程池中执行。如果两个command共享相同的线程池,会导致主command将变的延迟并且占用整个的线程池,从而阻止回退。
下面代码显示了CommandWithFallbackViaNetwork在getFallback() 方法中如何执行FallbackViaNetwork
同时也注意如i果回退也失败后如何处理,它同样有一个回退实现,它做了一个“无声失败”的返回null的解决方案。
为了通过配置让FallbackViaNetwork command运行在一个不同的线程池,默认的“'RemoteServiceX' 从HystrixCommandGroupKey中继承。它在构造函数中注入了HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")
这种方法CommandWithFallbackViaNetwork 将运行在一个名叫"RemoteServiceX" 的线程池并且FallbackViaNetwork 将运行在一个名叫 ”RemoteServiceXFallback“的线程池。
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;
}
}
}