基本的介绍和基本的演示都已经在11.天气预报系统-熔断机制中说明和演示了。下面来说点不一样的东西吧!

一、前言

比如拿我上一章中的代码,我们将spring-eureka-ribbon升级为spring-cloud-ribbon-hystrix:

无非是引入依赖,主函数添加一个允许熔断的声明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Service
public class HelloServiceImpl implements IHelloServie {

@Autowired
RestTemplate restTemplate;

@Override
@HystrixCommand(fallbackMethod = "helloFallBack")
public String hiService(String name) {
return restTemplate.getForEntity("http://SERVICE-HI/hello?name="+name,String.class).getBody();
}

public String helloFallBack(String name){
return "<font color='red'>error</font>";
}
}

注意,我们的fallBack方法的参数必须与被注解的方法的参数一致,否则会报错。

启动项目:spring-cloud-eureka-server-1,spring-cloud-eureka-server-2,spring-cloud-eureka-client-1,spring-cloud-eureka-client-2,spring-cloud-ribbon-hystrix.

然后关闭一个client,看是否触发服务降级,显示红色的error.

二、用法进阶-实现异步消费

我们不用@HystrixCommand注解来实现上面个功能。代码见spring-cloud-eureka-ribbon-hystrix-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class HelloController {

@Autowired
private RestTemplate restTemplate;

@RequestMapping("hello")
public String hello(){
HelloServiceCommand command = new HelloServiceCommand("hello",restTemplate);
String res = command.execute();
return res;
}

}

其中核心的HelloServiceCommand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class HelloServiceCommand extends HystrixCommand<String> {

private RestTemplate restTemplate;

public HelloServiceCommand(String commandGroupKey,RestTemplate restTemplate) {
super(HystrixCommandGroupKey.Factory.asKey(commandGroupKey));
this.restTemplate = restTemplate;
}

@Override
protected String run() throws Exception {
return restTemplate.getForEntity("http://SERVICE-HI/hello?name='swg'",String.class).getBody();
}

@Override
protected String getFallback() {
return "<font color='red'>error</font>";
}
}

这样就可以用代码实现上面的注解的功能。

我们可以发现,run()方法里调用其他的服务(多个),如果是串行执行,那么时间是所有服务执行时间之和。那么,有没有办法使他并行执行呢?达到一种NIO的效果。

NIO的两个实现方式:Future将来式和Callable回调式,这里使用将来式。

其实核心就是Future<String> future = command.queue();,让这个方法自己另开一个线程去默默执行,本线程还继续往下,等我想到结果的时候,再去调用String res = future.get();阻塞地获取结果,如果结果已经准备好了,那么就直接拿到。

在代码spring-cloud-eureka-ribbon-hystrix-03中的controller中进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RequestMapping("hello")
public String hello() throws ExecutionException, InterruptedException {
HelloServiceCommand command = new HelloServiceCommand("hello",restTemplate);
long now = System.currentTimeMillis();
Future<String> future = command.queue();
System.out.println("start");
long end = System.currentTimeMillis();
System.out.println(end - now);
String res = future.get();
long last = System.currentTimeMillis()-end;
System.out.println(last);
return res;
}

测试的目的就是看调用command.queue()之后会不会阻塞本线程的执行,我们让被restTemplate调用的方法睡眠一会:

1
2
3
4
5
6
7
@RequestMapping("hello")
public String hello(@RequestParam("name")String name) throws InterruptedException {
System.out.println("方法开始执行。。。");
Thread.sleep(800);
System.out.println("方法执行结束。。。");
return "hi "+ name +",you are from " + port;
}

结果打印:

1
2
3
start
1
817

就是说将执行其他服务这个操作异步到了另外一个线程中执行,本线程立即执行下面的逻辑。这样,提高了效率。

好了,用非注解的方式来实现了一下NIO的实现方式,那么肯定还是使用注解比较方便,那么基于注解的话,我们如何实现future的方式来执行呢?代码spring-cloud-eureka-ribbon-hystrix-04

我们只需要将原来的HelloServiceImpl中的hiService方法改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;

@Override
@HystrixCommand(fallbackMethod = "helloFallBack")
public String hiService(String name) throws ExecutionException, InterruptedException {
Future<String> future = new AsyncResult<String>() {
@Override
public String invoke() {
return restTemplate.getForEntity("http://SERVICE-HI/hello?name="+name,String.class).getBody();
}
};
return future.get();
}

就可以实现这种异步的方式调用了。

三、观察者模式来实现

image

订阅者来监听自己感兴趣的事件,可以实现多个请求集中处理。

代码不想去搞了…

四、总结

这里主要是实现熔断的功能,一开始的@HystrixCommand+fallback,到后面自己用HystrixCommand代码实现的,再到后来用Future来实现异步消费,到最后介绍的用观察者模式来实现的方式HystrixObserverCommand