资料参考:《Spring Cloud 微服务实战》
目录
1-同步执行、异步执行——自定义HystrixCommand
2-同步执行、异步执行——我们也可以通过 @HystrixCommand 注解实现同步执行、异步执行
3-响应式执行方式——除了传统的同步执行之外,我们还可以将 HystrixCommand 通过 Observable 来实现响应式执行方式。
在上篇文章中,我们介绍了断路器Hystrix的一个简单使用,主要是通过注解来实现断路器的功能的,不过对于Hystrix的使用,除了注解,我们也可以使用继承类的方式来实现,本文我们就来看看另一种Hystrix的使用方式。
-
1-同步执行、异步执行——自定义HystrixCommand
我们除了使用@HystrixCommand注解,也可以自定义类继承自HystrixCommand,如下:
import com.netflix.hystrix.HystrixCommand;
import org.springframework.web.client.RestTemplate;
public class MyCommand extends HystrixCommand<String> {
private RestTemplate restTemplate;
protected MyCommand(Setter setter, RestTemplate restTemplate) {
super(setter);
this.restTemplate = restTemplate;
}
@Override
protected String getFallback() {
return "调用出异常啦,启用熔断器";
}
@Override
protected String run() throws Exception {
return restTemplate.getForEntity("http://PROVIDER-EUREKA/index",String.class).getBody();
}
}
在MyCommand中注入RestTemplate,然后重写两个方法:一个是getFallback,这个方法将在服务调用失败时回调;另一个是run方法,执行请求时调用。构造方法的第一个参数主要用来保存一些分组信息。
通过上面实现的MyCommand,我们既可以实现同步执行,也可以实现异步执行。
- 同步执行:
String str2 = myCommand.execute();
- 异步执行:
Future<String> queue = myCommand.queue(); 异步执行的时候,可以通过对返回的Future 对象调用get方法获取结果。
自定义command调用
@RequestMapping(value = "/consume/service2",method = RequestMethod.GET)
public String hello2() throws ExecutionException, InterruptedException {
MyCommand myCommand = new MyCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),restTemplate);
//同步调用
String str2 = myCommand.execute();
//异步调用
Future<String> queue = myCommand.queue();
String str2 = queue.get();
return str2;
}
-
2-同步执行、异步执行——另外,我们也可以通过 @HystrixCommand 注解来更优雅的实现 Hystrix 命令的定义
比如:
@HystrixCommand(fallbackMethod = "helloFallback")
public String helloService1(){
long beginTime = System.currentTimeMillis();
String body = restTemplate.getForEntity("http://PROVIDER-EUREKA/index",
String.class).getBody();
long endTime = System.currentTimeMillis();
System.out.println("Spend Time : "+ (endTime - beginTime));
return body;
}
public String helloFallback(){
return "error";
}
虽然 @HystrixCommand 注解可以非常优雅的定义 Hystrix 命令的实现,但是定义的方式只是同步执行的实现,如果需要异步执行还需要另外的定义,比如:
@HystrixCommand(fallbackMethod = "helloFallback")
public Future<String> helloService3(){
return new AsyncResult<String>() {
@Override
public String invoke() {
long beginTime = System.currentTimeMillis();
String body = restTemplate.getForEntity("http://PROVIDER-EUREKA/index",
String.class).getBody();
long endTime = System.currentTimeMillis();
System.out.println("Spend Time : "+ (endTime - beginTime));
return body;
}
};
}
-
3-响应式执行方式——除了传统的同步执行之外,我们还可以将 HystrixCommand 通过 Observable 来实现响应式执行方式。
通过 observe() 和 toObservable() 方法可以返回 Observable 对象,比如:
Observable<String> a = new MyCommand(restTemplate).observe();
Observable<String> b = new MyCommand(restTemplate).toObservable();
observe() 和 toObservable() 虽然都返回了 Observable 对象,但他们有不同,前者返回的是一个 Hot Observable ,该命令会在调用 observe() 的时候理解去执行,当 Observable 每次被订阅的时候会重放他的行为。而后者返回的是一个 Cold Observable。toObservable() 执行之后,命令不会被立即执行,当所有订阅者订阅了他之后才会去执行。
那么通过以上两种方式都能获得Observable对象,接下来该如何处理才能获得你想要的User对象呢?
原理分析在分析命令执行的时候,提到在Hystrix的底层实现中大量的使用了RxJava响应式编程(观察者---订阅者),
- 对于第一种observe()返回Observable对象,我们可以这样取得结果:
List<String> list=new ArrayList<>();
//注意:因为执行是异步的,所以要想看到输出结果这里就要阻塞一下
Thread.sleep(3000);
Observable<String> a = new MyCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),restTemplate).observe();
a.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println(list.toString());
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace();
}
@Override
public void onNext(String s) {
list.add(s);
}
});
- 对于第二种toObservable()返回Observable对象,我们参考关系图,并根据上一章的第9步中返回成功的响应中提到的:
execute()、queue()也都使用了RxJava来实现,并且queue()是通过toObservable()来获得一个Cold Observable,并且通过toBlocking()将该Observable转换成BlockingObservable,它可以把数据以阻塞的方式发出来,而toFuture方法则是把BlockingObservable转换成一个Future,该方法只是创建一个Future返回,并不会阻塞,这使得消费者可以自己决定如何处理异步操作。所以第二种可以这样取得结果对象:
Observable<String> b = new MyCommand(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")),restTemplate).toObservable();
BlockingObservable<String> blockingObservable = b.toBlocking();
Future<String> future=blockingObservable.toFuture();
//注意捕获异常
String re=future.get();
三种模式使用区别
- 同步执行:当执行到注解方法时,程序会顺序执行。
- 异步执行:当执行到注解方法时,会并发异步执行,返回一个Future对象,后面使用.get()方法来阻塞拿到结果。如果有多个方法时,执行时间就是其中最长的一个服务的执行时间。
- 反应执行:当执行到注解方法时,返回一个观察者。支持EAGER和LAZY模式。和同步异步执行的区别是,当对多个方法之间的返回结果不需要做合并而是希望当多个方法返回时触发一些事件时比较适合使用该模式。
反应执行没太明白,如果需要了解可以先参考下这个https://mcxiaoke.gitbooks.io/rxdocs/content/Intro.html