SpringCloud详解 第三章 服务容错保护 Hystrix(二)
本章节详细介绍 Hystrix 各接口和注解的使用方法
一、HystrixCommand
上文中我们通过Hystrix 中的核心注解 @HystrixCommand, 通过它创建了 HystrixCommand 的实现,同时利用 fallback 属性指定了服务降级的实现方法。然而这些还只是 Hystrix 使用的一 小部分,在实现 一 个大型分布式系统时,往往还需要更多高级的配置功能。 接下来我们将详细介绍 Hystrix 各接口和注解的使用方法。创建请求命令:
Hystrix 命令就是我们之前所说的 HystrixCommand, 它用来封装具体的依赖服务调用逻辑。我们可以通过继承的方式来实现, 比如:
public class HelloCommand extends HystrixCommand<String> { private RestTemplate restTemplate; private HashMap map; public HelloCommand(RestTemplate restTemplate, HashMap paramMap) { super(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey( HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000))); this.restTemplate = restTemplate; this.map = paramMap; } @Override protected String run() { return restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); } // 服务降级 @Override protected String getFallback() { return "error-err"; } }
通过上面实现的HelloCommand , 我们既可以实现请求的同步执行也可以实现异步执行。除了传统的同步执行与异步执行之外, 我们还可以将 HystrixComrnand 通过Observable 来实现响应式执行方式。通过调用 observe()和toObservable ()方法可以返回 Observable 对象observe ()和toObservable ()虽然都返回了 Observable, 但是它们略有不同,前者返回的是一 个Hot Observable, 该命令会在 observe ()调用的时候立即执行, 当Observable 每次被订阅的时候会重放它的行为;而后者返回的是一 个Cold Observable,toObservable ()执行之后,命令不会被立即执行,只有当所有订阅者都订阅它之后才会执行。
//继承HystrixCommand的实现 @RequestMapping(value = "/helloCommand") public String helloCommand(Long id) { HashMap map = new HashMap<>(); map.put("id", id); //同步 String result = new HelloCommand(restTemplate, map).execute(); //异步 // Future<String> result = new HelloCommand(restTemplate, map).queue(); //响应式执行方式 // Observable<String> hotObserve = new HelloCommand(restTemplate, map).observe(); // Observable<String> coldObservable = new HelloCommand(restTemplate, map).toObservable(); return result; }
异步执行的时候, 可以通过对返回的 result 调用 get 方法来获取结果。另外, 也可以通过 上文@HystrixCommand 注解来更为优雅地实现 Hystrix 命令的定义,虽然 @HystrixCommand 注解可以非常优雅地定义 Hystrix 命令的实现, 但是如上定义的 get 方式只是同步执行的实现,若要实现异步执行则还需另外定义,比如:
//异步 @HystrixCommand(fallbackMethod = "getByidAsyncFailed")//熔断机制 @RequestMapping(value = "/getByidAsync") public String getUserByidAsync(String id) { HashMap map = new HashMap<>(); map.put("id", id); AsyncResult<String> asyncResult = new AsyncResult<String>() { @Override public String invoke() { return restTemplate.getForObject(REST_URL_PREFIX + "/hello?id={id}", String.class, map); } @Override public String get() { return invoke(); } }; return asyncResult.get(); }
二、HystrixObservableCommand
虽然 HystrixCornrnand 具备了 observe ()和toObservable() 的功能,但是它的实现有 一 定的局限性,它返回的 Observable 只能发射 一 次数据,所以 Hystrix 还提供了另外 一 个特殊命令封装 HystrixObservableCornrnand, 通过它实现的命令可以获取能发射多次的 Observable 。如果使用 HystrixObservableCornrnand 来实现命令封装,需要将命令的执行逻辑在construct 方法中重载,这样 Hystrix 才能将具体逻辑包装到 Observable 内,如下所示:
public class HelloObservableCommand extends HystrixObservableCommand<String> { private RestTemplate restTemplate; private HashMap map; public HelloObservableCommand(RestTemplate restTemplate, HashMap paramMap) { super(com.netflix.hystrix.HystrixObservableCommand.Setter.withGroupKey( HystrixCommandGroupKey.Factory.asKey("")).andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(5000))); this.restTemplate = restTemplate; this.map = paramMap; } @Override protected Observable<String> construct() { return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { String string = restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); observer.onNext(string); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } }); } }
而对此的注解实现依然是使用 @HystrixCommand, 只是方法定义需要做 一 些变化,具体内容与 construct ()的实现类似,如下所示:
//HystrixObservableCommand //EAGER 是该参数的模式值, 表示使用 observe ()执行方式。 @HystrixCommand(fallbackMethod = "getByidAsyncFailed", observableExecutionMode = ObservableExecutionMode.EAGER) // //表示使用 toObservable() 执行方式。 // @HystrixCommand(fallbackMethod = "getByidAsyncFailed",observableExecutionMode = ObservableExecutionMode.LAZY) @RequestMapping(value = "/helloHystrixObservableCommand") public Observable<String> helloHystrixObservableCommand(String id) { HashMap map = new HashMap<>(); map.put("id", id); return Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> observer) { try { if (!observer.isUnsubscribed()) { String string = restTemplate.getForObject("http://cloud-provider/hello?id={id}", String.class, map); observer.onNext(string); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } }); }