Hystrix-HystrixCommand注解方式实现线程池隔离

目录

配置解释

简单代码

线程池隔离代码

属性配置


配置解释

让我们来逐个介绍下@HystrixCommand注解的各个参数:

1:commandKey:配置全局唯一标识服务的名称,比如,库存系统有一个获取库存服务,那么就可以为这个服务起一个名字来唯一识别该服务,如果不配置,则默认是@HystrixCommand注解修饰的函数的函数名。

2:groupKey:一个比较重要的注解,配置全局唯一标识服务分组的名称,比如,库存系统就是一个服务分组。通过设置分组,Hystrix会根据组来组织和统计命令的告、仪表盘等信息。Hystrix命令默认的线程划分也是根据命令组来实现。默认情况下,Hystrix会让相同组名的命令使用同一个线程池,所以我们需要在创建Hystrix命令时为其指定命令组来实现默认的线程池划分。此外,Hystrix还提供了通过设置threadPoolKey来对线程池进行设置。建议最好设置该参数,使用threadPoolKey来控制线程池组。

3:threadPoolKey:对线程池进行设定,细粒度的配置,相当于对单个服务的线程池信息进行设置,也可多个服务设置同一个threadPoolKey构成线程组。

4:fallbackMethod:@HystrixCommand注解修饰的函数的回调函数,@HystrixCommand修饰的函数必须和这个回调函数定义在同一个类中,因为定义在了同一个类中,所以fackback method可以是public/private均可。

5:commandProperties:配置该命令的一些参数,如executionIsolationStrategy配置执行隔离策略,默认是使用线程隔离,此处我们配置为THREAD,即线程池隔离。参见:com.netflix.hystrix.HystrixCommandProperties中各个参数的定义。

6:threadPoolProperties:线程池相关参数设置,具体可以设置哪些参数请见:com.netflix.hystrix.HystrixThreadPoolProperties

7:ignoreExceptions:调用服务时,除了HystrixBadRequestException之外,其他@HystrixCommand修饰的函数抛出的异常均会被Hystrix认为命令执行失败而触发服务降级的处理逻辑(调用fallbackMethod指定的回调函数),所以当需要在命令执行中抛出不触发降级的异常时来使用它,通过这个参数指定,哪些异常抛出时不触发降级(不去调用fallbackMethod),而是将异常向上抛出。

8:observableExecutionMode:定义hystrix observable command的模式;

9:raiseHystrixExceptions:任何不可忽略的异常都包含在HystrixRuntimeException中;

10:defaultFallback:默认的回调函数,该函数的函数体不能有入参,返回值类型与@HystrixCommand修饰的函数体的返回值一致。如果指定了fallbackMethod,则fallbackMethod优先级更高。

 

简单代码

改造serice-ribbon 工程的代码,首先在pox.xml文件中加入spring-cloud-starter-hystrix的起步依赖:

<dependency>

    <groupId>org.springframework.cloud</groupId>

    <artifactId>spring-cloud-starter-hystrix</artifactId>

</dependency>

 

在程序的启动类ServiceRibbonApplication 加@EnableHystrix注解开启Hystrix:

@EnableHystrix

@EnableDiscoveryClient

@SpringBootApplication

public class RibbonConsumerApplication {

   public static void main(String[] args) {

      SpringApplication.run(RibbonConsumerApplication.class, args);

   }

  

   @Bean

    @LoadBalanced

    RestTemplate restTemplate() {

        return new RestTemplate();

    }

}

 

改造RibbonConsumerService2类,在hiService方法上加上@HystrixCommand注解。该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法,熔断方法直接返回了一个字符串,字符串为”hi,”+name+”,sorry,error!”,代码如下:

@Service

public class RibbonConsumerService2 {

    @Autowired

    RestTemplate restTemplate;

 

    @HystrixCommand(fallbackMethod = "hiError")

    public String hiService(String name) {

        return restTemplate.getForObject("http://my-eureka-service/hi?name="+name,String.class);

    }

   

    /**

     * @param 熔断则掉该方法

     * @return

     */

    public String hiError(String name) {

        return "hi,"+name+",sorry,error!";

    }

}

 

测试:

此时关闭 my-eureka-service工程,当我们再访问http://localhost:8764/hi2?name=forezp,浏览器会显示:

Hystrix-HystrixCommand注解方式实现线程池隔离

如果访问没有熔断的另一个接口,访问http://localhost:8764/hi?name=forezp,浏览器会显示:

Hystrix-HystrixCommand注解方式实现线程池隔离

 

 

线程池隔离代码

 

这个命令作用:

1.默认开启服务隔离方式:线程池方式。

Tomcat默认使用一个线程池的。下面演示,加了 hystrixCommand后,会默认开启新线程池得例子。

Hystrix-HystrixCommand注解方式实现线程池隔离

而别的接口,线程池是

Hystrix-HystrixCommand注解方式实现线程池隔离

 

//@HystrixCommand 加到服务的接口方法上,可以对接口限流。

//下面的代码,给服务的 /hello接口 加了hystrix线程隔离,并且限制并发为5

//当接口熔断或者降级时,会走降级方法,降级方法将异常信息返回,并且返回状态码 503

@RestController

@RequestMapping(value = "/hystrix")

public class HystrixCommandController {

    // controller层的接口做hystrix线程池隔离,可以起到限流的作用

    @HystrixCommand(

           fallbackMethod = "fallbackMethod", // 指定降级方法,在熔断和异常时会走降级方法

           groupKey = "querygroup-one", // 一组command,如果没有配threadPoolKey,相同的groupKey会使用同一线程池(线程池隔离策略下)

           commandKey = "helloCommand", // 服务标识,默认就是方法名

           commandProperties = {

              // 超时时间

              @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),

              @HystrixProperty(name = "execution.timeout.enabled", value = "true"),

              // 默认是线程池隔离策略

              @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")

           },

           threadPoolKey = "helloPool", // 可以在多个方法上定义同一个线程池

           threadPoolProperties = {

              // 并发,缺省为10

              @HystrixProperty(name = "coreSize", value = "3")

           })

    @RequestMapping(value = "/hello", method = RequestMethod.GET)

    public String sayHello(HttpServletResponse httpServletResponse) throws InterruptedException {

       Thread.sleep(2000L);

       System.out.println("Hello2 World!" + new Date());

       return "Hello World!-thread name:" + Thread.currentThread().getName();

    }

   

    @HystrixCommand(

           fallbackMethod = "fallbackMethod", // 指定降级方法,在熔断和异常时会走降级方法

           groupKey = "querygroup-one", // 一组command,如果没有配threadPoolKey,相同的groupKey会使用同一线程池(线程池隔离策略下)

           threadPoolKey = "helloPool", // 可以在多个方法上定义同一个线程池

           threadPoolProperties = {

              // 并发,缺省为10

              @HystrixProperty(name = "coreSize", value = "2")

           })

    @RequestMapping(value = "/hello2", method = RequestMethod.GET)

    public String sayHello2(HttpServletResponse httpServletResponse) throws InterruptedException {

       Thread.sleep(2000L);

       System.out.println("Hello2 World!" + new Date());

       return "Hello2 World!-thread name:" + Thread.currentThread().getName();

    }

 

    /**

     * 降级方法,状态码返回503 注意,降级方法的返回类型与形参要与原方法相同,可以多一个Throwable参数放到最后,用来获取异常信息

     */

    public String fallbackMethod(HttpServletResponse httpServletResponse, Throwable e) {

       httpServletResponse.setStatus(HttpStatus.SERVICE_UNAVAILABLE.value());

       e.printStackTrace();

       return "亲,后台报错了:" + e;

    }

}

 

线程池隔离-如何多个api使用同一个线程池?

只需要groupKeythreadPoolKey一样即可。

Hystrix-HystrixCommand注解方式实现线程池隔离

 

那么问题来了,这么多api公用一个线程池,那么配置核心coreSize怎么设置?

测试下来,发现如果配置不一样,则使用第一个访问api接口设置的。

Hystrix-HystrixCommand注解方式实现线程池隔离

 

线程池隔离-测试熔断

使用默认配置,访问地址:http://localhost:8764/hystrix/hello

接口超时时间设置1秒,然后代码sleep2秒,这样就会造成 接口访问超时。

当不停的访问 接口后,页面报错,监控面板上circuit=open

Hystrix-HystrixCommand注解方式实现线程池隔离

Hystrix-HystrixCommand注解方式实现线程池隔离

 

 

属性配置

可以看:配置在:HystrixPropertiesManager.java,所有默认属性都在HystrixCommandProperties.java

hystrix的缺省属性配置:

如果将@HystrixCommand注解加到方法上,不对属性(如CoreSize)做任何配置,那么相当于使用了如下缺省配置。 每个属性的意义可以参考  hystrix学习资料

@HystrixCommand(

            commandProperties = {

                    //execution

                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),

                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000"),

                    @HystrixProperty(name = "execution.timeout.enabled", value = "true"),

                    @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),

                    @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "false"),

                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),

                    //fallback

                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),

                    @HystrixProperty(name = "fallback.enabled", value = "true"),

                    //circuit breaker 断路器

                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),

                    @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),

                    @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),

                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),

                    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),

                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),

                    //Metrics

                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),

                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),

                    @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "true"),

                    @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),

                    @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "6"),

                    @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),

                    @HystrixProperty(name = "metrics.healthSnapshot.intervalInMilliseconds", value = "500"),

                    //request context

                    @HystrixProperty(name = "requestCache.enabled", value = "true"),

                    @HystrixProperty(name = "requestLog.enabled", value = "true")},

            threadPoolProperties = {

                    @HystrixProperty(name = "coreSize", value = "10"),

                    @HystrixProperty(name = "maximumSize", value = "10"),

                    @HystrixProperty(name = "maxQueueSize", value = "-1"),

                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),

                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "1"),

                    @HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "false"),

                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),

                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10")

            }

    )

    如果要自定义这些属性,那么需要先了解下hystrix属性配置的优先级。

配置在YML里面

Hystrix-HystrixCommand注解方式实现线程池隔离

Hystrix-HystrixCommand注解方式实现线程池隔离

全局配置属性配置(配置在yml)

如下是 threadpool 和 command下的属性全局配置,注意具体的配置都在  default  下

#hystrix全局属性配置

hystrix:

  threadpool:

    default: #对应@HystrixCommand注解中threadPoolKey的属性值,默认为default

      coreSize: 50 #线程池的核心线程数及并发执行的最大线程数

  command:

    default: #对应@HystrixCommand注解中commandKey的属性值,默认为default

      execution:

        isolation:

          thread:

            timeoutInMilliseconds: 1000 #执行超时时间

      fallback:

        isolation:

          semaphore:

            maxConcurrentRequests: 1000 #任意时间点允许的降级方法并发数。当请求达到或超过该设置值其余请求不会调用fallback而是直接被拒绝

 

熔断配置:

circuitBreaker.enabled   是否开启断路器功能

circuitBreaker.requestVolumeThreshold  该属性设置在滚动时间窗口中(一段时间内),断路器的最小请求数。默认20,如果在窗口时间内请求次数19,即使19个全部失败,断路器也不会打开

circuitBreaker.sleepWindowInMilliseconds    改属性用来设置当断路器打开之后的休眠时间,默认5000。休眠时间结束后断路器为半开状态,断路器能接受请求,如果请求失败又重新回到打开状态,如果请求成功又回到关闭状态

circuitBreaker.errorThresholdPercentage  该属性设置断路器打开的错误百分比,默认50%。在滚动时间内,在请求数量超过circuitBreaker.requestVolumeThreshold,如果错误请求数的百分比超过这个比例,断路器就为打开状态

circuitBreaker.forceOpen   true表示强制打开断路器,拒绝所有请求

circuitBreaker.forceClosed  true表示强制进入关闭状态,接收所有请求

metrics.rollingStats.timeInMilliseconds   设置滚动时间窗的长度,单位 毫秒。这个时间窗口就是断路器收集信息的持续时间。断路器在收集指标信息的时会根据这个时间窗口把这个窗口拆分成多个桶,每个桶代表一段时间的指标,默认10000

metrics.rollingStats.numBuckets   滚动时间窗统计指标信息划分的桶的数量,但是滚动时间必须能够整除这个桶的个数,要不然抛异常

bootstrap.properties 具体配置方法:

#全局超时时间

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

#hystrix.command.default.metrics.rollingStats.timeInMilliseconds=4000

#hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=2000

#hystrix.command.<commandKey>作为前缀,默认是采用Feign的客户端的方法名字作为标识

hystrix.command.queryContents.circuitBreaker.sleepWindowInMilliseconds=20000