SpringCloud系列(三)服务降级

这是我参与更文挑战的第 13 天,活动详情查看: 更文挑战

Hystrix

一、概述

Hystrix是一个用于分布式系统的延迟和容错的开源库,在分布式系统中,许多依赖会不可避免的调用失败,Hystrix能够在保证在一个依赖出问题的情况下,不会影响到整体的服务失败,避免联级故障,提高发生系统的弹性。

问题引出

多个微服务之间的调用会形成一个链路,当链路上的某个微服务发生故障失效(超时、异常等),这个有问题的模块再去调用其他模块,这样就会发生级联故障,也叫雪崩。

解决问题

‘断路器’是一种开关装置,当某个服务发生故障时,通过断路器的故障监控,向调用方返回一个符合预期的、可处理的备选方案,而不是长时间的等待或抛出异常,保证了服务调用放的线程不会被长期不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hystrix的功能:

  • 服务降级:在程序运行异常、超时、线程池\信号量打满的情况下会触发降级,给客户端一个友好的提示,fallback。
  • 服务熔断:类似保险丝,达到最大访问量后直接拒绝访问,然后调用访问降级给出友好提示。
  • 服务限流:秒杀高并发操作,防止一窝蜂的访问访问,设置每秒执行多上个请求,超出的请求进行排队。
  • 实时监控

二、服务降级

1、单侧降级

依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
复制代码

配置

server:
  port: 80

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

feign:
  hystrix:
    enabled: true
复制代码

Order80服务调用8001提供的服务,其中有一个延迟的接口

@Service
public class PaymentService {
    public String paymentInfoSuccess(Integer id){
        return "线程池:" + Thread.currentThread().getName() + "  paymentInfoSuccessId:" + id;
    }

    public String paymentInfoFailed(Integer id){
        //制造超时错误
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  paymentInfoFailedId:" + id;
    }
}
复制代码

因为OpenFeign的默认等待时间为1秒,所以调用超过1秒的接口会抛出异常,这样后面的服务可能都会因为这个延迟而故障,接下来添加服务降级功能解决问题。
降级配置,设置一个兜底方案。

提供服务8001模块异常后的fallback

@Service
public class PaymentService {
    //fallbackMethod:指定兜底方法,commandProperties指定出现哪些异常进行fallbackMethod
    @HystrixCommand(fallbackMethod = "paymentInfoFailedHandler",commandProperties = {
            //如果超时3秒就执行fallbackMethod
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfoFailed(Integer id){
        //制造超时错误
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "线程池:" + Thread.currentThread().getName() + "  paymentInfoFailedId:" + id;
    }

    public String paymentInfoFailedHandler(){
        return "Payment8001的paymentInfoFailed的fallbackMethod执行了";
    }
}
复制代码

消费者Order80配置fallback,通常在消费者端配置

@RestController
public class OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

    @HystrixCommand(fallbackMethod = "paymentInfoFailedHandler",commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    @GetMapping(value = "/consumer/paymentInfoFailed/{id}")
    public String paymentInfoFailed(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfoFailed(id);
    }

    @GetMapping(value = "/consumer/paymentInfoSuccess/{id}")
    public String paymentInfoSuccess(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfoSuccess(id);
    }

   public String paymentInfoFailedHandler(){
       return "Order80的paymentInfoFailed的fallbackMethod执行了";
   }
}
复制代码

2、全局降级

上面的情况会把fallback代码和业务代码耦合在一起,并且每一个方法都要单独设置一个fallback方法,用起来并不方便,使用全局的fallback来改进。

使用全局降级

@RestController
//指定全局降级的fallback方法
@DefaultProperties(defaultFallback = "payment_Global_RollbackMethod")
public class OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

    //开启全局降级,如果指定了fallback方法就使用已指定的,没有指定就使用全局的
    @HystrixCommand     
    @GetMapping(value = "/consumer/paymentInfoFailed/{id}")
    public String paymentInfoFailed(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfoFailed(id);
    }


    public String payment_Global_RollbackMethod() {
        return "Order80的paymentInfoFailed的payment_Global_RollbackMethod执行了";
    }
}

复制代码

3、通配服务降级

消费端在远程调用提供服务的接口时对其进行降级,如果提供服务的服务器发生宕机,也能降级成功。

创建一个类,实现远程调用的接口

@Component
public class PaymentFallbackService implements PaymentHystrixService {
    @Override
    public String paymentInfoFailed(Integer id) {
        return "PaymentFallbackService的PaymentFallbackService fallback 方法";
    }

    @Override
    public String paymentInfoSuccess(Integer id) {
        return "PaymentFallbackService 的paymentInfoSuccess fallback 方法";
    }
}
复制代码

在远程调用的接口上注明降级的类

@Component
@FeignClient(name = "CLOUD-PAYMENT-HYSTRIX-SERVICE",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService {
    @GetMapping(value = "/paymentInfoFailed/{id}")
    String paymentInfoFailed(@PathVariable("id") Integer id);
    @GetMapping(value = "/paymentInfoSuccess/{id}")
    String paymentInfoSuccess(@PathVariable("id") Integer id);
}
复制代码

三、服务熔断

熔断机制是应对雪崩效应的一种微服务链路保护机制。当扇出链路的某个微服务出错或不可用时,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务响应正常后,恢复链路调用。
SpringCloud中使用Hystrix实现熔断机制,Hystrix会监控微服务之间的调用状况,当失败的调用到一定的y阈值,就会启用熔断机制。

在payment8001服务中配置测试,在Service中配置熔断

@Service
public class PaymentService {
    @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(Integer id){
        if (id < 0) {
            throw new RuntimeException("id 不能小于0");
        }
        String uuid = IdUtil.simpleUUID();
        return Thread.currentThread().getName() + "\t" + "SUCCESS,code:" + uuid;
    }
    public String paymentCircuitBreaker_fallback(Integer id){
        return "id不能为负数,请稍后再试!" + id;
    }
}
复制代码

该方法在10秒钟之内访问次数达到10次以上并且失败率如果达到百分之60,id传入负数,就会启动熔断机制(熔断器全开),这时即使发送的id为整数,也还是会继续走fallback方法(熔断器半开状态),稍等一会就会恢复正常的调用(熔断器关闭)。
熔断器状态:

  • 熔断打开:请求不再调用当前服务,内部设置时钟(平均故障处理时间),当打开时间达到时钟所设时间进入半开状态。
  • 熔断半开:部分请求根据规则调用当前服务,如果请求成功且符合规则认为当前服务恢复正常,关闭熔断。
  • 熔断关闭:不会对服务进行熔断

四、服务监控

除了上述功能,Hystrix还提供的准实时的调用监控,Hystrix会持续的记录所有通过Hystrix发起的请求的执行信息,并以统计报表和图形的形式展示给用户,包括每秒执行成功、失败多少次。

1、搭建dashboard9001服务

依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<!--注意:只要用到web图形化都需要该依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
复制代码

主启动

@SpringBootApplication
@EnableHystrixDashboard
public class DashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(DashboardMain9001.class,args);
    }
}
复制代码

启动9001,访问http://localhost:9001/hystrix即可看到Hystrix Dashboard的主页

2、使用Dashboard监控服务

注意:在被监控的8001端主启动类需要加一个配置,springCloud升级后导致的

 @SpringBootApplication
 @EnableEurekaClient
 @EnableCircuitBreaker
 public class PaymentHystrixMain8001 {
     public static void main(String[] args) {
         SpringApplication.run(PaymentHystrixMain8001.class,args);
     }
 
     @Bean
     public ServletRegistrationBean getServlet(){
         HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
         ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
         registrationBean.setLoadOnStartup(1);
         registrationBean.addUrlMappings("/hystrix.stream");
         registrationBean.setName("HystrixMetricsStreamServlet");
         return registrationBean;
     }
 }
复制代码

监控8001服务

在hystrix主页输入监控地址http://localhost:8001/hystrix.stream

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享