SpringCloud中的Hystrix服务降级、熔断、限流详细教程(服务降级篇)

SpringCloud中的Hystrix


为什么要学这个?你不想面试的时候多装两个逼多拿两千块钱?

Hystrix现在已经停止维护,有兴趣可以看我的另一篇博客链接: link.
首先我们先想一想分布式微服务可能存在哪些问题。

服务雪崩
多个微服务之间的调用,假设A调用B,B又调用C和D服务,C和D又分别调用其他的微服务。
那么此次请求的涉及面就越来越大,这就是“扇出”!假如当前D这个服务调用时间过长或者挂掉了
那么对于A的以后调用就会占据越来越多的资源,从而引起系统崩溃。

简单来说,当你发现一个模块故障了之后,但是这个模块还会继续的接收请求,然而这个故障模块还调用了其他很多的模块,这样就发生了级联故障,这就是雪崩。

这里我们学习一下Hystrix

Hystrix是一个用于分布式系统的延迟的容错的开源组件,在分布式系统里不可避免会遇到调用失败,超时,异常等,Hystrix可以保证在一个依赖的服务出问题的时候,不会导致整体服务失败,避免雪崩。

1.服务降级

服务不可用了,我不能让线程一直等待在这里消耗资源,我要给服务调用方返回一个友好提示(fallback)。
触发服务降级的场景。

  • 程序运行异常
  • 超时
  • 服务熔断
  • 线程池/信号量打满

代码案例
服务端8001的两个服务,Controller层和消费端代码我就不贴出来了,只说最关键的部分。
案例场景:
用JeMeter压测工具对paymentInfo_TimeOut()方法进行压测,假设现在有2万个线程访问paymentInfo_TimeOut()方法,每个线程进来呢都会等待3S。大家可以想像一下,现在服务器的信号量肯定已经打满了。TomCat的线程池也就10个而已。那么现在我们去访问paymentInfo_ok()这个正常的接口,你就会发现也会出现等待的情况甚至触发了Feign的调用超时返回一个报错的页面,客户体验十分不好也就说明整个服务都已经被paymentInfo_TimeOut()给 拖慢了

@Service
public class DeptServiceImpl implements DeptService {


   @Override
   //正常访问没有异常的方法
   public String paymentInfo_ok(Integer id) {
       return "线程池:"+Thread.currentThread().getName()+"paymentInfo_ok:id:"+id;
   }

   @Override
   //等待3S返回的方法
   public String paymentInfo_TimeOut(Integer id) {
       try {
           Thread.sleep(3000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut:id:"+id+"耗时3秒";
   }
}

解决问题三大维度:

  • 服务8001超时了,调用者不能一直等待,返回错误页面,必须要有服务降级
  • 服务8001宕机或异常了,调用者不能一直等待,返回错误页面,必须要有服务降级
  • 服务8001Ok了,但调用方自己出故障了或有自我要求(自己的等待时间小于服务8001),自己处理降级

开始解决:

@EnableCircuitBreaker   //添加对Hystrix的支持
public class HystrixDeptProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDeptProvider_8001.class, args)

修改业务类,对paymentInfo_TimeOut()方法进行整改,加上 @HystrixCommand注解,其中有两个参数:
1.fallbackMethod :表示我这个方法如果出问题了,那我马上去执行我预先写好的fallback方法,马上给调用者返回一个友好提示。
2.commandProperties :是一个数组,里面可以写很多参数,表示对paymentInfo_TimeOut()方法的一系列控制。我这里写例子中的含义是:一个线程来访问paymentInfo_TimeOut(),最长执行时间是2秒,如果超过2秒都还没执行完,我就算执行出错,立刻进行服务降级,执行fallbackMethod

@Service
public class DeptServiceImpl implements DeptService {


    @Override
    //正常访问没有异常的方法
    public String paymentInfo_ok(Integer id) {
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_ok:id:"+id;
    }

    @Override
    //等待3S返回的方法
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",
            commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "2000")
    })
    public String paymentInfo_TimeOut(Integer id) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //int a = 1/0;
        return "线程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut:id:"+id+"耗时3秒";
    }


    public String paymentInfo_TimeOutHandler(Integer id) {

    return "8001服务调用失败,请稍后再试";
    }
}

测试发现,两秒钟会直接返回“8001服务调用失败,请稍后再试”。 如果我们把Thread.sleep(3000);注释掉,手动的写一个运行时异常int a = 1/0;,也会有同样的效果。

好,服务端的降级保护做完了后。我们想一个这样的场景,paymentInfo_TimeOut()方法在经过降级保护后,超时时间最大为2s,超过两秒我就直接降级返回了。那这个时候我们服务消费者是通过OpenFeign来调用我们这个paymentInfo_TimeOut()方法,我们知道OpenFeign也是有自己的调用超时时间的,默认是1s。不知道的同学看看这篇文章: SpringCloud中的OpenFeign的超时控制和日志增强.
那么这个时候其实对于调用者来说,服务调用依然是超时的,会返回错误信息。那我们知道,其实在服务客户端,我们也是需要去做这样一个服务降级保护处理的。

开始解决客户端的降级处理:

在客户端的配置文件中开始基于feign的降级处理

feign:
  hystrix:
    enabled: true

在启动类加上@EnableHystrix注解

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

在客户端的Controller层依葫芦画瓢,但是定义的超时时间为1S。这样Feign在调用超时后,就会被直接降级。避免报错。同理,如果是运行时异常,也会有同样的效果。

@RestController
public class OpenFeignController {


    @Autowired
    private MyTestOpenFeign01 myTestOpenFeign01;

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


    public String paymentInfo_TimeOutHandler(Integer id) {

        return "客户端调用失败,请稍后再试";
    }
1.1:定义全局服务降级

如果我有一百个方法需要降级,上面的写法是不是要写100遍?既不好维护,代码又太膨胀。
@DefaultProperties+@HystrixCommand:表示在此类中,所有打了@HystrixCommand的方法,都会走到defaultFallback降级方法中去!
但如果有个别方法比如代码中的paymentInfo_TimeOut()方法指定了自己的降级逻辑和目标方法的话,它就走自己的方法!

@RestController
@DefaultProperties(defaultFallback = "")
public class OpenFeignController {


    @Autowired
    private MyTestOpenFeign01 myTestOpenFeign01;

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

    public String paymentInfo_TimeOutHandler(Integer id) {
        return "客户端调用失败,请稍后再试";
    }

    public String defaultHandler() {
        return "客户端调用失败,请稍后再试(全局降级方法)";
    }

    @HystrixCommand
    @GetMapping("/paymentInfo_ok/{id}")
    public String paymentInfo_ok(@PathVariable("id") Integer id){
        return  myTestOpenFeign01.paymentInfo_ok(id);
    }


1.2:降级方法与业务代码解耦

上面的各种案例你会发现,业务代码和降级方法都耦合在一起,这是不符合我们编程规范的!
好,我们现在要进行解耦操作。个干个的,分工明确。
首先声明一点:服务降级基本都是在客户端做的事情,因为如果遇到服务端宕机的情况,降级的事情只能由客户端来完成
下面的代码是客户端中的FeignClient接口代码,我们在@FeignClient注解中加上一个fallback 属性,属性的值是一个类模板对象!也就是说,这个接口中的所有方法,在出错的时候(异常,超时,服务器宕机),都会走到MytestOpenFeignImpl中去! 那MytestOpenFeignImpl是什么呢,是当前接口的实现类!

@Component
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallback = MytestOpenFeignImpl.class)
public interface MyTestOpenFeign01 {

    @GetMapping("/paymentInfo_ok/{id}")
    String paymentInfo_ok(@RequestParam("id")Integer id);

    @GetMapping("/paymentInfo_ok/{id}")
    String paymentInfo_TimeOut(@RequestParam("id")Integer id);

FeignClient接口的实现类,接口中的每一个方法都会被重写,那么重写过后的方法,就是接口对应的FallBack方法!

@Component
public class MytestOpenFeignImpl implements MyTestOpenFeign01{
    @Override
    public String paymentInfo_ok(Integer id) {
        return "我是paymentInfo_ok接口的FallBack方法";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return "我是paymentInfo_TimeOut接口的FallBack方法";
    }

2.服务熔断

服务达到最大访问量了,直接拒绝方法,然后调用服务降级的方法给调用方一个友好提示。然后慢慢恢复服务。
链接: 服务熔断

3. 服务限流

高并发请求的情况下,禁止流量全部涌入,限制流量进入服务,如一秒N个。
服务限流我们不用Hystrix来讲解,请看我关于springcloudalibaba中的Sentinel组件

好了 基本已经讲完,欢迎大家评论区指出不足,一起学习进步!

大家看完了点个赞,码字不容易啊。。。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

易柏州Innovation

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值