SpringCloud实战(三)——Hystrix请求熔断服务降级

本篇文章将讲Hystrix,该组件的核心功能就是请求熔断,服务降级。当然还有其他的功能:依赖隔离、请求缓存、请求合并。

Hystrix特性:

1.请求熔断: 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN).

    这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力.

  2.服务降级:Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存.告知后面的请求服务不可用了,不要再来了。

       3.依赖隔离(采用舱壁模式,Docker就是舱壁模式的一种):在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池.比如说,一个服务调用两外两个服务,你如果调用两个服务都用一个线程池,那么如果一个服务卡在哪里,资源没被释放,后面的请求又来了,导致后面的请求都卡在哪里等待,导致你依赖的A服务把你卡在哪里,耗尽了资源,也导致了你另外一个B服务也不可用了。这时如果依赖隔离,某一个服务调用A B两个服务,如果这时我有100个线程可用,我给A服务分配50个,给B服务分配50个,这样就算A服务挂了,我的B服务依然可以用。

  4.请求缓存:比如一个请求过来请求我userId=1的数据,你后面的请求也过来请求同样的数据,这时我不会继续走原来的那条请求链路了,而是把第一次请求缓存过了,把第一次的请求结果返回给后面的请求。

  5.请求合并:我依赖于某一个服务,我要调用N次,比如说查数据库的时候,我发了N条请求发了N条SQL然后拿到一堆结果,这时候我们可以把多个请求合并成一个请求,发送一个查询多条数据的SQL的请求,这样我们只需查询一次数据库,提升了效率。

为什么需要有请求熔断呢?

例如:订单系统请求库存系统,结果一个请求发过去,库存系统服务挂掉或者是因为网络等问题很久没有反应,而这个时候更多的请求来到库存系统,就不断的创建线程,因为没有返回,线程得不到释放,导致服务崩溃。这时,Hystrix就可以解决这个问题。当订单系统请求库存系统时,库存系统两秒钟没有反应,就直接返回一个错误,这个错误具体返回什么就是服务降级里面的操作了。

如下图所示:

SpringCloud实战(三)——Hystrix请求熔断服务降级_第1张图片

 Hystrix工作流程:

SpringCloud实战(三)——Hystrix请求熔断服务降级_第2张图片

整个流程可以大致归纳为如下几个步骤:

  1. 创建HystrixCommand或则HystrixObservableCommand对象
  2. 调用HystrixCommand或则HystrixObservableCommand方法执行Command
  3. 根据依赖调用的结果缓存情况进行相应的处理
  4. 根据该类依赖请求熔断器的打开状态进行相应的处理
  5. 根据该类依赖请求的总量进行相应的处理
  6. 执行对外部依赖的请求逻辑
  7. 计算统计熔断器数据值
  8. 调用降级方法或则返回依赖请求的真正结果

下面代码实操一下。因为请求都是从Ribbon发起的,所以就在之前的Ribbon工程中进行操作。

一、引入Hystrix依赖:

下面的dashboard可以不用:仪表盘,对服务进行监控

         
			org.springframework.cloud
			spring-cloud-starter-hystrix
			1.4.0.RELEASE
		
		
			org.springframework.cloud
			spring-cloud-starter-hystrix-dashboard
			1.4.0.RELEASE
		

二、在启动类中加@EnableCircuitBreaker注解,代表允许断路器

@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

    @Bean
	@LoadBalanced
	RestTemplate restTemplate(){
		return new RestTemplate();
	}


}

三、创建HelloService类

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.command.AsyncResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import rx.Observable;
import rx.Subscriber;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Service
public class HelloService {
    @Autowired
    private RestTemplate restTemplate;


    /**
      *@Author ZNX
      *@Date 2018/8/4 22:13
      *@Description:@HystrixCommand:代表调用下面的方法发送请求
     * fallbackMethod:请求降级:由我们控制,降级之后进行什么逻辑
     * 内部参数相当于方法名
      */

    @HystrixCommand(fallbackMethod = "helloFallBack")
    public String helloService(){

        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
    }


//降级后执行该方法
    public String helloFallBack(Throwable throwable){
        return "error";
    }



}

四:在Controller中注入Service并调用方法:

@RestController
public class ConsumerController {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private HelloService helloService;


    @RequestMapping("/consumer")
    public String  helloConsumer() throws ExecutionException, InterruptedException {

        return helloService.helloService();

    }

}

五、执行程序

正常状态下:可以访问(两个服务轮询访问)

SpringCloud实战(三)——Hystrix请求熔断服务降级_第3张图片

 这时关掉一个服务,

SpringCloud实战(三)——Hystrix请求熔断服务降级_第4张图片

 以上只是简单的演示,接下来进行深入的挖掘!

------------------------------------------------------------分割线------------------------------------------------------------------------------------

一、抛弃@HystrixCommand注解,在代码层面实现。

1.新建一个HelloServiceCommond类:

想实现HystrixCommand必须继承HystrixCommand类

public class HelloServiceCommand extends HystrixCommand{

    private RestTemplate restTemplate;

    protected HelloServiceCommand(String commandGroupKey,RestTemplate restTemplate) {

        super(HystrixCommandGroupKey.Factory.asKey(commandGroupKey));
        this.restTemplate=restTemplate;
    }

    /**
      *@Author ZNX
      *@Date 2018/8/4 22:28
      *@Description:要执行的逻辑,要放在这个run()方法中执行
      */
    @Override
    protected String run() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
    }

    @Override
    protected String getFallback() {
        return "error";
    }

    @Override
    protected String getCacheKey() {
        return "hello";
    }
}

2.修改Controller:

@RestController
public class ConsumerController {

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Autowired
    private HelloService helloService;


    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/consumer")
    public String  helloConsumer() throws ExecutionException, InterruptedException {

        HelloServiceCommand command = new HelloServiceCommand("hello",restTemplate);

        String result  =  command.execute();
        return result;

    }

}

3.启动项目,访问成功

SpringCloud实战(三)——Hystrix请求熔断服务降级_第5张图片

但是这种方式都是阻塞式的 ,效率低。

那么如何实现一个异步的IO呢?

我们这里只需要改变调用时候的执行方式就可以了。

将之前调用的Excute()方法改为queue()方法.方法改造如下:

 @RequestMapping("/consumer")
    public String  helloConsumer() throws ExecutionException, InterruptedException { 
        
        HelloServiceCommand command = new HelloServiceCommand("hello",restTemplate);

        Future future  =  command.queue();
        return future.get();

    }

 重启项目,访问成功。

SpringCloud实战(三)——Hystrix请求熔断服务降级_第6张图片

 但是这里异步的优势并没有体现出来,接着对项目进行改造,加了几个时间和几个输出语句。

 @RequestMapping("/consumer")
    public String  helloConsumer() throws ExecutionException, InterruptedException {

        HelloServiceCommand command = new HelloServiceCommand("hello",restTemplate);

        long now = System.currentTimeMillis();
        Future future  =  command.queue();
        System.out.println(1111111111);
        long end = System.currentTimeMillis();
        System.out.println(end-now);
        String result = future.get();
        long last = System.currentTimeMillis()-end;
        System.out.println(last);
        return result;
    }

并且给其中一个服务睡眠800毫秒:这里不要睡太久,太久的话熔断器就熔断了——>服务降级

@RestController
public class HelloController {

    @RequestMapping("/hello")
    public  String hello () throws InterruptedException {
        System.out.println("service02");
        Thread.sleep(800);
        return "hello cloud02";
    }


}

 重启工程:访问:查看控制台结果:

SpringCloud实战(三)——Hystrix请求熔断服务降级_第7张图片

上面是在代码层面实现异步。下面将在使用注解时进行异步实现。

修改HelloService:

 @HystrixCommand(fallbackMethod = "helloFallBack")
    public String helloService() throws ExecutionException, InterruptedException {

        Future future = new AsyncResult() {
            @Override
            public String invoke() {
                return restTemplate.getForEntity("http://HELLO-SERVICE/hello",String.class).getBody();
            }
        };

        return future.get();
    }

 

这样也是可以成功的。

初入SpringCloud,总结不到,还望指出。

 

 

你可能感兴趣的:(SpringCloud)