zoukankan      html  css  js  c++  java
  • Hystrix入门demo

    Hystrix入门demo

    Hystrix的三种降级策略

    • 熔断触发降级
    • 超时触发降级
    • 资源隔离触发降级

    熔断触发降级

    ​ 熔断机制是一种保护性的机制,当系统某个服务失败率过高的时候,将开启熔断器,对该服务的后续调用直接拒绝,进行fallback操作。

    熔断器开启的两个条件

    • 请求数达到设定的阈值
    • 请求的失败数 / 总请求书 > 错误占比阈值

    代码展示

         
    
    	/**
         * HystrixProperty的参数可参考 hystrixCommandProperties
         * 熔断触发降级
         * @return
         */
        @GetMapping("/circuitBreaker/{num}")
        @HystrixCommand(commandProperties  = {
                @HystrixProperty (name = "circuitBreaker.enabled" ,value = "true"),
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="5"),
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds" , value ="5000"),  //熔断时间5秒
                @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50")  //错误流程比例
    
        } ,fallbackMethod = "fallback")
        public String circuitBreaker(@PathVariable("num")int num){
            if(num%2==0){
                return "正常访问";
            }
    
            throw new RuntimeException("");
        }
    
        public String fallback(int num){
            return "熔断触发降级";
        }
    
    

    而且需要开启熔断器的开关

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

    参数解析:

    @HystrixProperty (name = "circuitBreaker.enabled" ,value = "true") 开启熔断降级功能

    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value ="5") 最小请求次数,这里是5个请求

    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds" , value ="5000") 熔断时间5秒

    @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value = "50") 错误比例超过50%,就熔断


    fallbackMethod = "fallback" 触发回调的方法 , 当前类的下的方法名

    注意: 请求服务方法和回调方法,入参要一致,不然会报错的

    功能解析:

    • 当请求错误的时候,会触发fallback这个回调方法
    • 10s钟之内,当发起了超过5次请求,且失败率超过50%,熔断自动开启,从熔断开启到后续5s之内的请求,都不会进入到方法里,并且直接触发fallback这个回调方法返回。

    结果展示:

    Hystrix入门demo-1

    ​ 分析: 可以看到我发送了三次请求:http://127.0.0.1:9091/hystrix/circuitBreaker/1 , 然后发送http://127.0.0.1:9091/hystrix/circuitBreaker/2 请求两次,这两次返回都是正常访问的。发送第三次的时候就返回熔断触发降级了。 这是因为:前面发送了5个请求,3个失败了,2个成功,但是我们设置了是5次请求且失败率是50%以上触发的。所以到第6个请求的时候触发了熔断降级,需要等待5秒后才能正常恢复正常。

    超时触发降级

    ​ 超时触发降级:当系统某个服务请求需要时间超过这个服务正常的请求时间,可以认为该服务已经请求失败,直接降级返回。

    ​ 项目经历的场景:比如测试一个数据源是否有效,正常情况下的话(不管成功还是失败),会很快就能返回了,有些情况,服务会一直请求访问数据源而卡着。这个时候用超时触发降级就能达到要求了。因为他卡着其实这个数据源你是不成功的,那我直接返回给前台告诉他数据源不可用就行了

    代码展示:

        /**
         * 超时时间触发降级
         * @return
         */
        @GetMapping("/timeOut")
        @HystrixCommand(commandProperties  = {
            @HystrixProperty(name = "execution.timeout.enabled" , value = "true"),
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "1000"),
        } ,fallbackMethod = "fallback")
        public String timeOut() throws InterruptedException {
            Thread.sleep(3000) ;
            return "正常访问";
    
        }
    
    
        public String fallback(){
            return "触发降级";
        }
    
    

    参数解析:

    @HystrixProperty(name = "execution.timeout.enabled" , value = "true"), 启动超时触发降级
    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "1000"), 超过1s就触发降级

    功能解析:

    • 当请求超过1s,就会触发降级

    资源隔离触发降级

    资源隔离触发降级又分成两种:

    • 信号量隔离
    • 线程池隔离

    信号量隔离

    ​ 信号隔离是通过限制依赖服务的并发请求数,来控制隔离开关,它不会使用Hystrix管理的线程池处理请求。使用容器(Tomcat)的线程处理请求逻辑。特点:不涉及线程切换,资源调度,上下文的转换等,相对效率高。触发条件:信号量隔离也会启动熔断机制。如果请求并发数超标,则触发熔断,返回fallback数据。

         public String fallback(){
            return "触发降级";
        }
    
    	@GetMapping("/semaphore")
        @HystrixCommand(
                commandProperties = {
                        @HystrixProperty(name = "execution.isolation.strategy" , value = "SEMAPHORE"),
                        @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests" , value = "2") //信号量大小
                },
                fallbackMethod = "fallback"
        )
        public String semaphore() throws InterruptedException {
            return "semaphore正常访问";
        }
    

    参数配置:

    • @HystrixProperty(name = "execution.isolation.strategy" , value = "SEMAPHORE"), 隔离的种类,可选值只有THREAD(线程池隔离)和SEMAPHORE(信号量隔离)。默认是THREAD线程池隔离。设置信号量隔离后,线程池相关配置失效。
    • @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests" , value = "2") 信号量大小,这服务只能两个请求同时访问 信号量最大并发数。默认值是10。常见配置500~1000。

    功能解析:

    ​ 这个方法不能超过2个线程以上同时访问。

    线程池隔离

    ​ hystrix进行资源隔离,其实是提供了一个抽象,叫做command,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内对这个依赖服务的所有调用请求,全部走这个资源池内的资源,不会去用其他的资源了,这个就叫做资源隔离

    ​ 比如:对某一个商品服务所有的调用请求,全部隔离到一个线程池内,对商品服务的每次调用请求都封装在一个command里面每个command(每次服务调用请求)都是使用线程池内的一个线程去执行的,所以哪怕现在这个商品服务同时发起的调用量已经到了1000了,但是线程池内就10个线程,其他的请求都会存到等待队列中,最多就只会用这10个线程去执行请求,对商品服务的请求,因为接口调用延迟。不会将tomcat内部所有的线程资源全部耗尽。

    使用线程隔离的好处:

    • 应用程序可以不受失控的第三方客户端的威胁,如果第三方客户端出现问题,可以通过降级来隔离依赖。
    • 当失败的客户端服务恢复时,线程池将会被清除,应用程序也会恢复,而不至于使整个Tomcat容器出现故障。
    • 简而言之,由线程供的隔离功能可以使客户端和应用程序优雅的处理各种变化,而不会造成中断。

    线程池的缺点

    • 线程最主要的缺点就是增加了CPU的计算开销,每个command都会在单独的线程上执行,这样的执行方式会涉及到命令的排队、调度和上下文切换。
    • Netflix在设计这个系统时,决定接受这个开销的代价,来换取它所提供的好处,并且认为这个开销是足够小的,不会有重大的成本或者是性能影响。

    代码展示:

     private int num1 = 1;
       
    
    @HystrixCommand(
                commandProperties = {
                        @HystrixProperty(name = "execution.isolation.strategy" , value = "THREAD"),  //使用线程池的隔离
                        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "3000"),  //超时设置为3秒
    
                },
                threadPoolProperties = {
    
                        @HystrixProperty(name = "coreSize" , value = "20"), //线程池大小
                        @HystrixProperty(name = "maxQueueSize" , value = "1000"), //等待队列长度
                        @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),//线程存活时间
                        @HystrixProperty(name = "queueSizeRejectionThreshold" , value = "800"),  
                },
                groupKey = "ThreadService", commandKey = "thread" ,threadPoolKey = "ThreadService",
                fallbackMethod = "fallback"
                )
        public void  thread() throws  Exception  {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread() + "正常访问" + num1++);
        }
    
    
    
        public void fallback(){
            System.out.println("熔断时间:" + new Date());
        }
    

    参数解析:

    • ​ @HystrixProperty(name = "execution.isolation.strategy" , value = "THREAD") 使用线程池的隔离

    • ​ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds" , value = "3000") 超时设置为3秒,默认是1s


    • ​ @HystrixProperty(name = "coreSize" , value = "20") 线程池大小,默认是5个

    • ​ @HystrixProperty(name = "maxQueueSize" , value = "1000") 等待队列长度,注意 :该设置只会在初始化时有效,之后不能修改threadpool的queue size

    • ​ @HystrixProperty(name = "keepAliveTimeMinutes", value = "2") 线程存活时间,如果corePoolSize和maxPoolSize设成一样(默认实现)该设置无效

    • ​ @HystrixProperty(name = "queueSizeRejectionThreshold" , value = "800"), 即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。因为maxQueueSize不能被动态修改,这个参数将允许我们动态设置该值。if maxQueueSize == -1,该字段将不起作用


    • threadPoolKey 线程池的名字。 默认的threadpool key就是command group名称

    • groupKey 群组的key

    • commandKey 代表了一类command,一般来说,代表了底层的依赖服务的一个接口

    ​ command group一般来说,可以是对应一个服务,多个command key对应这个服务的多个接口,多个接口的调用共享同一个线程池

    如果说你的command key,要用自己的线程池,可以定义自己的threadpool key.

    测试用例:

    @Autowired
    private ThreadService threadService;
    
    @Test
    public void  threadTest() throws Exception {
    
    
        System.out.println("开始时间: " + new Date());
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                public void run()  {
                    try {
                        threadService.thread();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
      }
        Thread.sleep(100000 );
        System.out.println("完成了");
    }
    

    结果测试展示:

    image-20200915222146171

    • 可以留意到了线程的名字是ThreadService就是我们设置的threadPoolKey,看序号刚好就到20,跟我们想象的结果一样的,证明了maxQueueSize是对的

    • 超时的熔断时间-开始时间 = 4 秒 .这个是正常的。因为这里的开始时间是第一个启动线程的开始时间,而不是那个熔断的线程启动的开始时间,算下来有4秒跟三秒差不多。其实也验证了我们这个execution.isolation.thread.timeoutInMilliseconds的设置是正确的。

    hystrix和openfeign的结合使用

    这个可以直接在配置文件里application.yml中配置

    feign:
      hystrix:
        enabled: true   #开始openFeigin的配置
    
    
    hystrix:
      command:
        default:  #全局配置, feignclient#method(param)
          execution:
            timeout:
              enable: true       #开启超时降级
            isolation:
              thread:
                timeoutInMilliseconds: 1000     #降级时间是1s
        SpringHystrixController#query():       #给SpringHystrixController类下的query方法定义降级策略
          execution:
            isolation:
              strategy: SEMAPHORE                #信号量降级
              semaphore:
                maxConcurrentRequests: 10         #信号量数量是10
        SpringHystrixController#insert():
          execution:
            isolation:
              strategy: THREAD
      threadpool:
        order-service:
          coreSize: 2
          maxQueueSize: 1000
          queueSizeRejectionThreshold:  800
    

    注意:这些设置只针对于加了feignClient的方法

    参数可以参考HystrixCommandPropertiesHystrixThreadPoolProperties进行设置

    HystrixCommand的使用

    ​ 这个功能通过继承HystrixCommand这个类来实现熔断,因为这个是在代码层面上的,其实可以动态的修改熔断的规则。

    使用:

     * 熔断器设置
     * @author gyc
     * @date 2020/9/20
     */
    public class CircuitBreakerService extends HystrixCommand<String> {
    
    
        private int num;
    
        static HystrixCommand.Setter CIRCUITBREAKER = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerEnabled(true)
                        .withCircuitBreakerRequestVolumeThreshold(5)
                        .withCircuitBreakerErrorThresholdPercentage(50)
                        .withCircuitBreakerSleepWindowInMilliseconds(5000)
                );
    
        /**
         *
         * @param num
         */
        public CircuitBreakerService(int num) {
            super(CIRCUITBREAKER);
            this.num = num;
        }
    
        protected String run() throws Exception {
            if(num % 2 == 0){
                return "正常访问";
            }
    
            throw new RuntimeException("");
        }
    
        @Override
        protected String getFallback() {
            return "熔断降级";
        }
    }
    

    调用:

        @GetMapping("/circuitBreaker/{num}")
        public String circuitBreaker(@PathVariable("num")int num){
            return new CircuitBreakerService(num).execute();
        }
    
    

    ​ 这个熔断器的功能其实跟上面@HystrixCommand熔断器的功能是一样的。其实我们可以发现,我们把要执行的代码都写到HystrixCommand.run()方法中,然后调用CircuitBreakerService.execute()就可以了。其实这个跟Thread线程使用的方法其实是一样的。Thread也是把要执行的代码写到run()方法中,然后通过Thread.start()来做一个执行。

    下面附上超时,线程池隔离,信号量隔离的代码:

     //熔断降级
        static HystrixCommand.Setter CIRCUITBREAKER = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withCircuitBreakerEnabled(true)
                        .withCircuitBreakerRequestVolumeThreshold(5)
                        .withCircuitBreakerErrorThresholdPercentage(50)
                        .withCircuitBreakerSleepWindowInMilliseconds(5000)
                );
    
        //超时降级
        static HystrixCommand.Setter TIMEOUT = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionTimeoutEnabled(true)
                        .withExecutionTimeoutInMilliseconds(3000)
                );
    
    
    
        //线程池隔离
        static HystrixCommand.Setter THREAD = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
                .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
                        .withCoreSize(20)
                        .withMaxQueueSize(1000)
                        .withQueueSizeRejectionThreshold(800)
                        .withKeepAliveTimeMinutes(2));
    
    
    
        //信号量隔离
        static HystrixCommand.Setter SEMAPHORE = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HystrixService"))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                        .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
                        .withExecutionIsolationSemaphoreMaxConcurrentRequests(2)
                );
    
    

    代码地址: https://gitee.com/gzgyc/cloud-demo.git 里的hystrix-demo

  • 相关阅读:
    python3下搜狗AI API实现
    辽宁移动宽带体验及魔百盒M101s-2刷机
    年会抽奖系统-支持作弊,指定中奖
    论python3下“多态”与“继承”中坑
    fiddler抓手机报文的配置指南
    微软BI套件
    IntelliJ IDEA 2018 Community(社区版)创建J2EE项目+Tomcat9部署
    Spring.net的Demo项目,了解什么是控制反转
    manjaro安装virtualbox教程
    Debian9 安装后的配置笔记
  • 原文地址:https://www.cnblogs.com/dabenxiang/p/13676116.html
Copyright © 2011-2022 走看看