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就可以解决这个问题。当订单系统请求库存系统时,库存系统两秒钟没有反应,就直接返回一个错误,这个错误具体返回什么就是服务降级里面的操作了。

如下图所示:

 Hystrix工作流程:

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

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

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

一、引入Hystrix依赖:

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

         <dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
			<version>1.4.0.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
			<version>1.4.0.RELEASE</version>
		</dependency>

二、在启动类中加@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();

    }

}

五、执行程序

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

 这时关掉一个服务,

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

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

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

1.新建一个HelloServiceCommond类:

想实现HystrixCommand必须继承HystrixCommand类

public class HelloServiceCommand extends HystrixCommand<String>{

    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.启动项目,访问成功

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

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

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

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

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

        Future<String> future  =  command.queue();
        return future.get();

    }

 重启项目,访问成功。

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

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

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

        long now = System.currentTimeMillis();
        Future<String> 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";
    }


}

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

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

修改HelloService:

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

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

        return future.get();
    }

 

这样也是可以成功的。

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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Spring Cloud Hystrix是一个开源的熔断器框架,它能够帮助开发者有效地处理服务依赖中的延迟和故障。熔断器的主要目的是在出现故障时提供一种优雅的降级机制,以避免整个系统的崩溃。 熔断和降级是Hystrix中两个重要的概念。 熔断(Circuit Breaker)指的是在服务出现故障或错误率过高时,自动地切换到指定的备用服务或返回事先定义好的错误结果,起到保护系统免受故障传播的影响的作用。当服务不可用或响应时间过长时,熔断打开,拒绝后续请求的访问,并尝试通过执行降级逻辑来快速响应客户端。一旦后续请求不再出现故障,熔断器将进入半开状态,允许少量的请求通过以检测服务是否恢复正常。 降级(Degradation)指的是在系统资源不足或者高访问量时,服务降级关闭一些不重要的功能,以保证系统核心功能的可用性和稳定性。降级可以通过阻止非必要的调用、减少资源的消耗以及返回默认值或缓存结果来实现。降级需要提前定义好一些备用的逻辑,一旦系统资源紧张,就可以立即启用降级逻辑来保障系统的可用性。 总而言之,熔断和降级都是为了保护系统免受故障的影响。熔断主要是针对服务故障和错误率过高的情况,通过切换到备用服务或返回错误结果来保护系统。降级主要是在系统资源紧张或高访问量的情况下,关闭一些不重要的功能来保证核心功能的可用性和稳定性。两者都是通过提前定义备用逻辑来保障系统的正常运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值