SpringCloud-Hystrix服务降级与熔断简介与简单配置


1、前言

作者最近在学习springcloud,本篇文章仅作为学习笔记,如有错误,敬请指正

2、Hystrix是什么

Hystrix是一个用于处理分布式系统的延迟和容错的开源库
,在分布式系统里,许多依赖不可避免的会调用失败,比如
超时、异常等,Hystrix能够保证在一个依赖出问题的情况
下,不会导致整体服务失败,避免级联故障,以提高分布式
系统的弹性。

Hystrix包括服务降级与熔断

使用情景

我们可以模拟高并发情况来测试服务,了解Hystrix的使用场景
1)新建模块,导入依赖

<!--hystrix-->
   <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
           <version>2.2.0.RELEASE</version>
   </dependency>

2)修改appliction.yml配置文件,这里设置为单机Eureka,方便模拟测试

server:
  port: 8001

spring:
  application:
    name: cloud-provider-hystrix-payment

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

3)在启动类上添加注释@EnableEurekaClient,注册服务

@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

4)创建两个service类,里面包含两个方法,其中一个进行线程等待

@Service
public class PaymentService {
    public String paymentInfo_ok(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";

    }
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
    })
    public String paymentInfo_TimeOut(Integer id)
    {

        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗费3秒";
    }

5)创建controller类,调用service方法

@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentService.paymentInfo_ok(id);
        log.info("****result: "+result);
        return result;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")

    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException
    {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result: "+result);
        return result;
    }

6)开启Jmeter,设置两万次请求并启动
在这里插入图片描述
7)我们手动访问http://localhost:8001/payment/hystrix/ok/1,此时会发现这个测试路径竟然也会等待

8)当然,我们一般是使用EurekaClient端服务消
费者来访问的,我们可以创建一个服务来模拟

主要所需依赖如下

<dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>

配置文件如下

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/

启动类如下

@SpringBootApplication
@EnableFeignClients
public class OrderHystrixMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderHystrixMain80.class,args);
    }
}

创建Service类,如下

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

创建Controller类,如下

@RestController
@Slf4j
public class OrderHystirxController
{
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
}

同理,高并发测试http://localhost/consumer/payment/hystrix/ok/31,会发现一样会有等待。其原因是tomcat的线程已经全部被占

9)结论
正因为有上述故障或不佳表现才有服务降解与熔断等技术的诞生

3 服务降级

	服务降级是当服务器压力剧增的情况下,根据当前业务情况
及流量对一些服务和页面有策略的降级,以此释放服务器资
源以保证核心任务的正常运行

降级配置

1)修改上面8001端口的Service类,加上注解@HystrixCommand和@HystrixProperty

@Service
public class PaymentService {
    public String paymentInfo_ok(Integer id){
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";

    }
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1000")
    })
    public String paymentInfo_TimeOut(Integer id)
    {

        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗费3秒";
    }
    public String paymentInfo_TimeOutHandler(Integer id){
        return "/(ㄒoㄒ)/调用支付接口超时或异常:\t"+ "\t当前线程池名字" + Thread.currentThread().getName();
    }

2)在8001端口启动类上添加新注解@EnableCircuitBreaker,激活Hystrix

3)同样进行高并发测试,可以这个时候不会等待,而是会直接执行我们设置的保底方法
在这里插入图片描述

4)同理80端口也可以设置,在80端口的yml配置文件上加上,开启feign上的的Hystrix功能

feign:
  hystrix:
    enabled: true

4)在80端口启动类上添加注解@EnableHystrix,激活Hystrix(@EnableHystrix注解继承了@EnableCircuitBreaker,并对它进行了再封装)
5)修改80端口的Controller类

@RestController
@Slf4j
public class PaymentHystirxController
{
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_OK(id);
        return result;
    }

@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
        @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
{
    String result = paymentHystrixService.paymentInfo_TimeOut(id);
    return result;
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
{
    return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}

}
 

6)结果
在这里插入图片描述
7)问题:这样写fallback方法的话,那么每一个业务方法都需要一个,这会很麻烦,导致代码膨胀

8)解决方法:
在这里插入图片描述
在这里插入图片描述
9)当然,把方法写在业务类里,我觉得不太好,会增加耦合度。解决方法如下,创建一个service实现类
在这里插入图片描述
10)在接口的@FeignClient注解中添加fallback属性,表示使用实现类来进行降级方法的实现
在这里插入图片描述
在这里插入图片描述
11)在测试前记得把Controller中的@HystrixCommand注解删了,才能起效果
在这里插入图片描述

在这里插入图片描述

3、服务熔断

服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个
系统出现雪崩,暂时停止对该服务的调用。

熔断配置

1)在8001端口服务的Service类上添加如下代码

//=========服务熔断
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//是否开启断路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//请求次数
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),//时间窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),//失败率达到多少跳闸
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        if(id < 0)
        {
            throw new RuntimeException("******id 不能负数");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
    {
        return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~   id: " +id;
    }

2)Controller类添加:

@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id)
{
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("****result: "+result);
    return result;
}

正常情况:
在这里插入图片描述
异常情况:
在这里插入图片描述
3)测试服务熔断

当我们多次提交错误请求,然后再提交正确请求会发现正确请求也会报错

在这里插入图片描述

服务熔断的三个状态

  • 熔断关闭状态(Closed)

      服务没有故障时,熔断器所处的状态,对调用方的调用不做任何限制。
    
  • 熔断开启状态(Open)

      在固定时间内(Hystrix默认是10秒),接口调用出错比率达到一个阈值(Hystrix默认为50%)
      会进入熔断开 启状态。进入熔断状态后, 后续对该服务接口的调用不再经过网络,直接执
      行本地的fallback方法。
    
  • 半熔断状态(Half-Open)

      在进入熔断开启状态一段时间之后(Hystrix默认是5秒),熔断器会进入半熔断状态。
      所谓半熔断就是尝试恢复服务调用,允许有限的流量调用该服务,并监控调用成功率。
      如果成功率达到预期,则说明服务已恢复,进入熔断关闭状态;如果成功率仍旧很低,则重新进入熔断开启状态。
    

    状态图如下

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值