HystrixCommand注解使用

使用Spring Cloud时绕不开Hystrix,他帮助微服务实现断路器功能。Hystrix具备服务降级,服务熔断,线程和信号隔离,请求缓存,请求合并以及服务监控等强大功能。

让我们来看一下这个注解的简单应用:


package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@Service
public class ConsumerService {

    @Autowired
    private RestTemplate restTemplate;

    @HystrixCommand(commandKey = "testCommand", groupKey = "testGroup", threadPoolKey = "testThreadKey",
            fallbackMethod = "hiConsumerFallBack", ignoreExceptions = {NullPointerException.class},
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "30"),
                    @HystrixProperty(name = "maxQueueSize", value = "101"),
                    @HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
                    @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
            }
            )
    public String hiConsumer(String id) {
        
        //SERVICE_HI是服务端的spring.application.name,并且大写,hi为服务端提供的接口
        return restTemplate.getForEntity("http://SERVICE_HI/hi", String.class).getBody();
    }
    
    public String hiConsumerFallBack(String id, Throwable e) {
        return "This is a error";
    }

}


让我们来逐个介绍下@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:线程池相关参数设置,具体可以设置哪些参数请见:

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

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

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

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

使用技巧

通常使用是在controller方法上启用注解 @HystrixCommand 但是每个方法都写 fallbackMethod 总有些麻烦 所以可以定义一个抽象类 BaseController 使用的时候 controller 继承 BaseController 即可实现统一 fallbackMethod


@DefaultProperties(defaultFallback = "defaultFallback", ignoreExceptions = {
		BusinessException.class }, commandProperties = {}, groupKey = "组名", threadPoolKey = "线程池名")
@ResponseBody
public abstract class BaseController {

	@Value("${isDevMode:true}")
	private boolean isDevMode;

	private static final Logger LOG = LoggerFactory.getLogger(BaseController.class);

	protected ResponseEntity<Object> defaultFallback(Throwable e) {
		String devPrefix = "CircuitBreaker triggered:\r\n";
		String message = "系统繁忙,请稍后再试.";
		if (e != null) {
			String errorDetail = devPrefix + ExceptionUtils.errorDetail(e);
			LOG.error("执行hystrixMethod");
			LOG.error(errorDetail);
			message = isDevMode ? errorDetail : message;
		}
		System.err.println(Thread.currentThread().getName());
		return ResponseEntity.badRequest().body(message);
	}
}

配置超时时间


#超时配置 单位毫秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000

原理分享

如何达到一个服务超时熔断的目的???
Future 原生 api 来实现超时熔断
Future .get(100, TimeUnit.MILLISECONDS);
每个请求封装在一个线程池 默认 线程池大小是10

简易demo


public class FutureCircuitBreakerDeo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //初始化线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        RandomCommand randomCommand = new RandomCommand();

        Future<String> future = executorService.submit(() -> {
            //获取 run 方法计算结果
            return randomCommand.run();
        });

        String result = null;
        // 100 ms 超时时间
        try {
            result = future.get(100, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            // fallback 方法调用
            result = randomCommand.fallback();
        }

        System.out.println(result);

        executorService.shutdown();
    }

    /**
     * 随机对象
     */
    private static final Random RANDOM = new Random();

    /**
     * 随机事件执行命令
     */
    static class RandomCommand implements Command<String> {
        @Override
        public String run() throws InterruptedException {
            long executeTime = RANDOM.nextInt(200);

            System.out.println("execute time : " + executeTime + "ms");

            Thread.sleep(executeTime);

            return "hello";
        }

        @Override
        public String fallback() {
            return "fallback";
        }
    }


    public static interface Command<T> {
        /**
         * 正常执行,并且返回结果
         * @return T
         */
        T run() throws InterruptedException;

        /**
         * 错误时,返回容错结果
         * @return T
         */
        T fallback();
    }
}

你可能感兴趣的:(HystrixCommand注解使用)