SpringCloud(3) 请求熔断、服务降级Hystrix

Hystrix主要功能 :请求熔断,服务降级

例:订单服务去请求库存服务,但是库存服务出问题了,超过了2s没有反应,为了不让挂掉的库存服务影响到订单服务,需要返回一个错误,这个错误具体返回什么需要服务降级来处理,然后再一次请求的时候,就会出发请求熔断,不会直接去请求服务直接返回错误。

其他功能:依赖隔离(舱壁模式 Docker,每个服务调用不同的线程池)、请求缓存(两次请求查询,查询的数据都一样,第二次不需要请求数据库,直接返回第一次的请求结果即可)、请求合并(比如过多次请求数据库,可以给合并到一起去请求数据库)

实现demo

首先在上一节的Ribbon项目中加入依赖:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
在application中添加注解
@EnableCircuitBreaker
然后编写测试类:
@Service
public class hystrixService {

@Resource
private RestTemplate restTemplate;


//fallbackMethod 服务降级
@HystrixCommand(fallbackMethod = "helloFallBack")
public String helloHystrix(){
return restTemplate.getForEntity("http://HELLO-SERVER/hello",String.class).getBody();
}

public String helloFallBack(){
return "error";
}

}
@HystrixCommand(fallbackMethod = "helloFallBack")      这个是请求降级的这里fallbackMethod参数对应一个方法名,由于这个注解里没有写参数,那么对应的方法里也不应该有参数,返回值也是对应的。

编写测试controller
@RestController
public class hystrixController {

@Resource
private hystrixService hystrixService;

@RequestMapping("/hystrix")
public String helloHystrix(){
String s = hystrixService.helloHystrix();
return s;
}

}
然后启动两个Eureka和两个服务加上这个ribbon+hystrix的工程,然后去访问这个服务,当其中的一个服务挂了,并且刚好请求的是挂了的那个服务,这是就会返回错误信息"error"
这种是通过注解来实现的,那么接下里通过自定义hystrixCommand来实现一下:
注意:这里的restTemplate是注入不进去的,不在spring容器的控制范围之内,所以这里重新写了构造方法,controller层去调用的时候直接new这个对象。
利用run方法来请求服务,出问题的话会执行getFallback方法。
public class hystrixServiceCommand extends HystrixCommand<String>{

private RestTemplate restTemplate;

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

@Override
protected String run() throws Exception {
System.out.println(Thread.currentThread().getName());
return restTemplate.getForEntity("http://HELLO-SERVER/hello",String.class).getBody();
}

@Override
protected String getFallback() {
return "error diy";
}
}
cotroller层的修改:
@RequestMapping("/hystrix")
public String helloHystrix(){
hystrixServiceCommand command = new hystrixServiceCommand("hello",restTemplate);
return command.execute();
}
然后重启服务进行测试,测试结果同上。

说明:这里每调用一次请求就是一次网络的io,是阻塞的,如果这个请求的结果没有返回那么后续的代码不会执行,问题:如果这个ribbon中请求了三个服务,那么会阻塞逐一执行,响应时间太长。
IO:一般是指阻塞式IO。
NIO:非阻塞IO。
  Future将来试:多线程的方式去执行,可以使用get方法来获取请求结果。
  Callable回调试:有一个回调方法,当请求执行结束之后会自动调用回调方法。
那么我们将请求改为异步的,具体修改如下:

修改了controller层
@RequestMapping("/hystrix")
public String helloHystrix() throws ExecutionException, InterruptedException {
hystrixServiceCommand command = new hystrixServiceCommand("hello",restTemplate);
long start = System.currentTimeMillis();
Future<String> future = command.queue();
System.out.println("================");
long end = System.currentTimeMillis();
System.out.println(end-start);
String result = future.get();
long last = System.currentTimeMillis()-end;
System.out.println(last);
return result;
}
为了展示效果这里也修改了一个HELLO-SERVER服务,增加了一行sleep
@RequestMapping("/hello")
@ResponseBody
public String hello() throws InterruptedException {
Thread.sleep(800);
return "hello spring cloud 8892";
}
上述是自定义的异步实现,同理,注解方式也可以实现异步
@HystrixCommand(fallbackMethod = "helloFallBack")
public String helloHystrix() throws ExecutionException, InterruptedException {
Future<String> future = new AsyncResult<String>() {
@Override
public String invoke() {
return restTemplate.getForEntity("http://HELLO-SERVER/hello",String.class).getBody();
}
};
return future.get();
}
为了处理特殊需求,这里有个观察者模式,controller对service进行监控,如果出现onNext、onError、onCompleted事件做出响应。

新的service
public class hystrixServiceObserverCommand extends HystrixObservableCommand<String> {

private RestTemplate restTemplate;

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

@Override
protected Observable<String> construct() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try {
if (!subscriber.isUnsubscribed()){
System.out.println("执行请求");
String result = restTemplate.getForEntity("http://HELLO-SERVER/hello",String.class).getBody();
subscriber.onNext(result);
String result1 = restTemplate.getForEntity("http://HELLO-SERVER/hello",String.class).getBody();
subscriber.onNext(result1);
subscriber.onCompleted();
}
} catch (Exception e) {
subscriber.onError(e);
}
}
});
}

/**
* 服务降级
* @return
*/
@Override
protected Observable<String> resumeWithFallback() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try {
if (!subscriber.isUnsubscribed()){
subscriber.onNext("error");
subscriber.onCompleted();
}
} catch (Exception e) {
subscriber.onError(e);
}
}
});
}
}
controller的修改
@RequestMapping("/hystrix")
public String helloHystrix() {
hystrixServiceObserverCommand command = new hystrixServiceObserverCommand("hello",restTemplate);
//热执行 不必等到初始化全部完成在执行
Observable<String> observe = command.observe();
//冷执行 先初始化事件再执行
// Observable<String> observe = command.toObservable();
//用于存放所有结果
ArrayList<String> list = new ArrayList<String>();
observe.subscribe(new Observer<String>() {
@Override
public void onCompleted() {
System.out.println("已完成所有请求");
}

@Override
public void onError(Throwable throwable) {
System.out.println(throwable.toString());
}

@Override
public void onNext(String s) {
System.out.println("初始化事件");
list.add(s);
}
});
return list.toString();
}
posted @ 2018-08-01 17:51  Gggoblin  阅读(545)  评论(0编辑  收藏  举报