注解配置Hystrix (原文:https://blog.csdn.net/chenxyz707/article/details/80913725?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param)
Hystrix也支持以注解的形式配置。通过@HystrixCommand
注解的fallbackMethod
属性指定降级方法。groupKey
和commandKey
默认为方法名
(当然threadPoolKey
不指定时,默认和groupKey
一致,所以也是方法名),也可以指定这三个key值,配置文件通过groupKey
,commandKey
,threadPoolKey
使用恰当的配置。
commandProperties
和threadPoolProperties
是通过@HystrixProperty
的name
value
键值对进行配置。
@Component public class UserAnnotationCommand { @Autowired @Qualifier("lbRestTemplate") RestTemplate lbRestTemplate; @HystrixCommand(fallbackMethod = "timeoutFallback", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "20"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "20") }, commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "8000") }) public String timeout() { return lbRestTemplate.getForObject("http://user-service/user/timeout", String.class); } public String timeoutFallback() { return "timeout 降级"; } @HystrixCommand(fallbackMethod = "exceptionFallback", threadPoolProperties = { @HystrixProperty(name = "coreSize", value = "20"), @HystrixProperty(name = "queueSizeRejectionThreshold", value = "20") }, commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") }) public String exception() { return lbRestTemplate.getForObject("http://user-service/user/exception", String.class); } public String exceptionFallback() { return "exception 降级"; } }
查看com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect
源码,我们可以看到@HystrixCommand
注解配置的方式是使用了AOP动态的生成了一个HystrixInvokable
对象,通过调用HystrixInvokable
的方法实现了HystrixCommand的功能。
// 使用AOP让注解的@HystrixCommand生效 @Aspect public class HystrixCommandAspect { @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)") public void hystrixCommandAnnotationPointcut() { } @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)") public void hystrixCollapserAnnotationPointcut() { } @Around("hystrixCommandAnnotationPointcut() || hystrixCollapserAnnotationPointcut()") public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable { Method method = getMethodFromTarget(joinPoint); /***省略***/ MetaHolderFactory metaHolderFactory = META_HOLDER_FACTORY_MAP.get(HystrixPointcutType.of(method)); MetaHolder metaHolder = metaHolderFactory.create(joinPoint); HystrixInvokable invokable = HystrixCommandFactory.getInstance().create(metaHolder); ExecutionType executionType = metaHolder.isCollapserAnnotationPresent() ? metaHolder.getCollapserExecutionType() : metaHolder.getExecutionType(); Object result; try { if (!metaHolder.isObservable()) { result = CommandExecutor.execute(invokable, executionType, metaHolder); } else { result = executeObservable(invokable, executionType, metaHolder); } } catch (HystrixBadRequestException e) { throw e.getCause() != null ? e.getCause() : e; } catch (HystrixRuntimeException e) { throw hystrixRuntimeExceptionToThrowable(metaHolder, e); } return result; } }
同样的为了测试这个配置是否生效我们新增接口:
@RestController @RequestMapping("/user") public class UserController { @Autowired UserAnnotationCommand userAnnotationCommand; @RequestMapping("/command/annotation/timeout") public String commandAnnotationTimeout() { return userAnnotationCommand.timeout(); } @RequestMapping("/command/annotation/exception") public String commandAnnotationException() { return userAnnotationCommand.exception(); } }
和FeignClient集成
在SpringCloud中Hystrix和Feign的集成十分方便。在客户端我们需要使用@EnableCircuitBreaker
启用熔断机制。
@SpringBootApplication @EnableEurekaClient @EnableFeignClients(defaultConfiguration = FeignClientsConfiguration.class) @EnableCircuitBreaker public class WebApplicationStarter { public static void main(String[] args) { SpringApplication.run(WebApplicationStarter.class, args); } }
同时application.yml
配置文件中开启Hystrix,并进行Hystrix配置。
# 开启熔断机制 feign: hystrix: enabled: true ribbon: # 开启eureka与ribbon的集成 eureka: enabled: true # 暂不开启熔断机制 hystrix: enabled: false # 配置ribbon默认的超时时间 ConnectTimeout: 20000 ReadTimeout: 20000 # 是否开启重试 OkToRetryOnAllOperations: true # 重试的时候实例切换次数 MaxAutoRetriesNextServer: 3 # 每个实例重试次数 MaxAutoRetries: 2 ## hystrix相关配置 ## hystrix默认会读取classpath下的config.properties文件,application会覆盖config.properties中的属性 hystrix: threadpool: # 指定服务的配置 user-service: coreSize: 20 maxQueueSize: 200 queueSizeRejectionThreshold: 3 # userThreadPool是UserTimeOutCommand中配置的threadPoolKey userThreadPool: coreSize: 20 maxQueueSize: 20 queueSizeRejectionThreshold: 3 # 这是默认的配置 default: coreSize: 10 maxQueueSize: 200 queueSizeRejectionThreshold: 2 command: # 指定feign客户端中具体的方法 UserService#timeout(): execution: isolation: thread: timeoutInMilliseconds: 20000 userCommandKey: execution: isolation: thread: timeoutInMilliseconds: 15000 # 这是默认的配置 default: execution: timeout: enabled: true isolation: strategy: THREAD thread: timeoutInMilliseconds: 15000 interruptOnTimeout: true interruptOnFutureCancel: false semaphore: maxConcurrentRequests: 2 fallback: enabled: true isolation: semaphore: maxConcurrentRequests: 10 circuitBreaker: enabled: true forceOpen: false forceClosed: false requestVolumeThreshold: 4 errorThresholdPercentage: 50 sleepWindowInMilliseconds: 10000 metrics: rollingStats: timeInMilliseconds: 5000 numBuckets: 10 rollingPercentile: enabled: true timeInMilliseconds: 60000 numBuckets: 6 bucketSize: 100 healthSnapshot: intervalInMilliseconds: 500
这里为了测试超时接口,所以Ribbon配置的超时时间(20s)大于接口的返回时间(10s),否则在Ribbon超时后将自动重试,直到超过Hystrix的超时时间时返回降级信息。在实际中,Hystrix的超时时间应该大于Ribbon的超时时间*Ribbon的重试次数。
如果项目中
config.properties
和application.yml
文件中都有Hystrix的配置,对于同一配置项,application.yml
中的属性会覆盖config.properties
中的属性值。当然使用Java API和注解方式也是优先读取application.yml
中的配置。
在这个配置中我们甚至可以指定FeignClient中的某一方法的Hystrix配置。比如上面的配置文件中我们定义了UserService
的timeout()
方法的超时时间为20s。
新增Feign客户端和降级类,降级类必须实现对应的Feign客户端。
@FeignClient(name="user-service", fallback = UserServiceFallback.class) public interface UserService { @RequestMapping(value = "/user/timeout", method = RequestMethod.GET) public String timeout(); @RequestMapping(value = "/user/exception", method = RequestMethod.GET) public String exception(); }
@Component public class UserServiceFallback implements UserService { @Override public String timeout() { return "timeout 降级"; } @Override public String exception() { return "exception 降级"; } }