• springcloud一篇搞定Hystrix


    Hystrix是什么?

    基础概念

    服务雪崩

    在分布式微服务的系统中,一定是会存在某个A服务调用多个服务,而多个服务也会继续去掉其他服务。因此服务的调用,从调用的结构上看是“扇形”趋势。
    因此就会存在一个很重大的问题:如果其中一个服务出现问题了咋办,比如超时,宕机,异常....就会导致前面的调用方在死等着,占据着资源。这还只是一个请求情况下,如果是在高并发的情况下,由于多个服务滞留占用资源,发生蝴蝶效应,导致系统整体服务使用出现缓慢甚至不可以,叫服务雪崩。
    这个影响主要有两个:

    1. 假设A服务出现异常,调用A服务所在的服务链路将不能完成整体业务功能
    2. 由于A服务出现异常,那些滞留占用资源的服务影响到了其他正常服务的使用。

    Hystrix是为了解决第二个问题而出现的延迟和容错的开源库。第一种影响解决不了,本身A服务有问题,所在那条链路肯定完成不了整体功能了,但起码不能影响其他服务的使用吧

    服务降级

    服务降级:就是说用户发送请求到A服务接口,由于A服务出现不明故障导致不能及时用户返回正确的结果,这时启动备用方案;
    一般来说备用方案,通常是返回一个友好的提示给用户,并不能真正完成用户要做的业务
    导致服务降级的几种情况:

    1. 程序运行异常
    2. 超时
    3. 服务熔断调用服务降级
    4. 线程池打满

    总结:服务降级是一种备用方案。

    服务熔断

    服务熔断:就是说用户发送请求到A服务接口,由于A服务出现不明重大故障导致不能给用户返回正确的结果,直接把A服务熔断掉,直接拒绝用户访问A服务了
    这里也说下服务熔断和服务降级的区别:

    出现服务降级的情况,可能这时的A服务还是正常的,只不过当前压力比较大,大部分请求能及时响应,部分请求无法及时响应,因此给部分无法及时响应给出备用方案
    而服务熔断,是A服务受到了极大的影响,整个服务不可用了,这时直接拒绝用户访问A服务是熔断操作

    某个服务发生了服务熔断后,后续一般会先 先调用服务降级,给用户一个友好提示,接着恢复服务的使用。

    服务限流

    服务限流理解就比较简单点,A服务1秒钟只能处理10个请求,现在同一时间有100个请求调用A服务,A服务肯定承受不了,因此服务限流可以理解为将这100个请求进行排队,不要挤在一块访问A服务

    服务降级

    Hystrix使用方式1:在controller接口加上@HystrixCommand,并在controller类加上备案方法

    降级在生产者服务端

    1. 在主启动类加上@EnableHystrix
    2. 给可能会出现高并发或超时的接口加上@HystrixCommand,并编写备案方法,如下:
        @GetMapping("/payment/hystrix/timeout/{id}")
        @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
                @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="3000")
        })
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id) throws InterruptedException
        {
            String result = paymentService.paymentInfo_TimeOut(id);
            log.info("****result: "+result);
            return result;
        }
    
        public String paymentInfo_TimeOutHandler(Integer id){
            return "/(ㄒoㄒ)/调用支付接口超时或异常:\t"+ "\t当前线程池名字" + Thread.currentThread().getName();
        }
    

    如果调用paymentInfo_TimeOut方法超过3秒或者异常,则会调用paymentInfo_TimeOutHandler方法进行响应。

    降级在消费者服务端

    1. yml加上如下,因为消费者服务使用feign作为服务调用,和hystrix有紧密合作:
    feign:
      hystrix:
        enabled: true
    
    1. 在主启动类加上@EnableHystrix
    2. 示例代码:
        @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 = paymentHystrixApi.paymentInfo_TimeOut(id);
            return result;
        }
        public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
        {
            return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
        }
    

    总结

    上述的写法有两个弊病:

    1. 备案方法和controller耦合在一起了
    2. 每个需要降级的方法都得对应一个备案方法,代码冗余

    Hystrix使用方式2:在controller接口加上@DefaultProperties,在controller需要降级的方法加上@HystrixCommand

    使用方式1的弊病之一:代码会出现冗余。而使用方式2是一定程度上减少了代码冗余,具体使用步骤如下(激活注解和yml步骤省略):

    1. 在controller类上加上@DefaultPropertie,如下:
    @RestController
    @Slf4j
    @DefaultProperties(defaultFallback = "payment_Global_FallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })
    public class OrderHystirxController
    {}
    
    1. 在@DefaultProperties所在类,需要降级的方法加上@HystrixCommand,如下:
        @GetMapping("/consumer/payment/hystrix/timeout/{id}")
        @HystrixCommand
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
        {
            String result = paymentHystrixApi.paymentInfo_TimeOut(id);
            return result;
        }
    

    总结

    优点:

    1. 一定程度上减少了降级方法冗余
    2. 类里的降级方法,@HystrixCommand如果没有指定具体的降级方法和超时时间,则走默认的@DefaultProperties。如果指定了,则走自己的。

    缺点:

    1. 备案方法和controller耦合在一起了

    Hystrix使用方式3:创建一个备案类实现ClientApi接口,重写的方法即为备案方法

    话不多说,操作如下(激活注解和yml省略):

    1. 创建PaymentHystrixApiFallback实现 PaymentHystrixApi,重写的方法即为备案方法:
    @Component
    public class PaymentHystrixApiFallback implements PaymentHystrixApi {
    
        @Override
        public String paymentInfo_OK(@PathVariable("id") Integer id){
            return "服务调用失败,提示来自:cloud-consumer-feign-order80";
        }
    
        @Override
        public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
            return "服务调用失败,提示来自:cloud-consumer-feign-order80";
        }
    }
    
    1. 在指定备案类PaymentHystrixApiFallback,如下:
    @Component
    @FeignClient(value = "cloud-provider-hystrix-payment",fallback = PaymentHystrixApiFallback.class)
    public interface PaymentHystrixApi {
        @GetMapping("/payment/hystrix/ok/{id}")
        String paymentInfo_OK(@PathVariable("id") Integer id);
    
        @GetMapping("/payment/hystrix/timeout/{id}")
        String paymentInfo_TimeOut(@PathVariable("id") Integer id);
    }
    

    假设我们调用paymentInfo_OK方法失败了,就会调用其重写方法返回"服务调用失败,提示来自:cloud-consumer-feign-order80";

    总结

    优点:

    1. 将降级方法抽取出来,实现解耦。
      2.从根本上解决问题,此方式偏向考虑服务之间调用失败而做出的降级反馈,而方式1和2有很大的不同,方式1和2是针对自身方法,而方式3针对的是服务调用方法

    缺点:

    1. 每个服务接口都有一个备案方法,代码冗余

    方式3使用的会比较多一点,但具体情况依项目而定;服务降级的使用多用在消费者服务端

    服务熔断

    Hystrix服务熔断有3个状态,open,close,half-open

    open: 熔断已开启
    close:熔断关闭
    half-open:熔断半开启

    如果服务在正常情况下,一般是close状态,如果由于某种原因出现熔断了,会进行open状态,在休眠一段时间后会进入half-open状态,尝试处理请求,如果处理请求还是失败的,又会回到open状态,反之close。
    代码示例实际上和服务降级差不多,但不一样是服务熔断只针对自身方法,如下:

        //=========服务熔断
        @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
    	//是否开启熔断机制
                @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),
    			//某段时间内请求次数阈值
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),
    			//进入open状态,等待多久才进入到half-open状态
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),
    			//达到多少失败百分比进入open状态
                @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;
        }
    

    上面的一些参数可举个例子:最近10次如果失败率达到60%以上,则进入open状态,后面的请求(不管对错)直接返回备案方法。在休眠10秒后进入half-open状态尝试处理请求,如果请求能正常处理,则进入close状态,反之open。

  • 相关阅读:
    软件工程实践总结作业——个人作业
    第四次作业——个人作业——软件案例分析
    第三次作业——结对编程
    第二次作业——结对项目之需求分析与原型模型设计
    使用Git进行代码管理心得
    调研Android Studio开发环境的发展演变(附安装教程,多图)
    软件工程的实践项目的自我目标
    软件工程实践总结
    教师报课系统测试
    第四次个人作业--关于 微软必应词典客户端 的案例分析
  • 原文地址:https://www.cnblogs.com/ibcdwx/p/16084669.html
Copyright © 2020-2023  润新知