Hystrix简单介绍
spring cloud整合Hystrix基本使用
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
package com.itopener.demo.hystrix.client.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import com.itopener.framework.ResultMap; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; @Service public class HystrtixService { private final Logger logger = LoggerFactory.getLogger(HystrtixService.class); /** * @description HystrixCommand注解默认超时时间是1s * HystrixCommand注解配置属性参见 {@code HystrixCommandProperties} * @author fuwei.deng * @date 2018年2月8日 下午5:02:22 * @version 1.0.0 * @param id * @return */ @HystrixCommand(fallbackMethod = "callFallback") public ResultMap call(long id){ try { Thread.sleep(5000); } catch (Exception e){ logger.error("sleep exception ", e); } return ResultMap.buildSuccess(); } public ResultMap callFallback(long id){ return ResultMap.buildFailed("hystrix fallback : " + id); } }
Hystrix部分源码解析
spring cloud是基于spring boot开发的,可以由spring-cloud-starter-hystrix作为入口查看依赖的包

其中,hystrix开头的是Hystrix原本的jar包,所以查看spring-cloud-netflix-core包中

主要有两个配置类:HystrixAutoConfiguration、HystrixCircuitBreakerConfiguration。其中HystrixAutoConfiguration主要是hystrix的健康检查的配置,主要配置在HystrixCircuitBreakerConfiguration中,从里面加载的bean名称可以看到Hystrix的处理入口是:HystrixCommandAspect
@Bean public HystrixCommandAspect hystrixCommandAspect() { return new HystrixCommandAspect(); }
HystrixCommandAspect这个类利用AOP切面对 HystrixCommand 、 HystrixCollapser 两种注解的方法进行扩展处理。下面是截取的部分代码
/** * AspectJ aspect to process methods which annotated with {@link HystrixCommand} annotation. */ @Aspect public class HystrixCommandAspect { private static final Map<HystrixPointcutType, MetaHolderFactory> META_HOLDER_FACTORY_MAP; static { META_HOLDER_FACTORY_MAP = ImmutableMap.<HystrixPointcutType, MetaHolderFactory>builder() .put(HystrixPointcutType.COMMAND, new CommandMetaHolderFactory()) .put(HystrixPointcutType.COLLAPSER, new CollapserMetaHolderFactory()) .build(); } @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); Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint); if (method.isAnnotationPresent(HystrixCommand.class) && method.isAnnotationPresent(HystrixCollapser.class)) { throw new IllegalStateException("method cannot be annotated with HystrixCommand and HystrixCollapser " + "annotations at the same time"); } 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(); } catch (HystrixRuntimeException e) { throw hystrixRuntimeExceptionToThrowable(metaHolder, e); } return result; } }
这里简单介绍下两种注解的配置
用于标记需要命令模式处理的方法
package com.netflix.hystrix.contrib.javanica.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 用于标记使用Hystrix命令模式执行的方法 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface HystrixCommand { /** * 命令模式分组key,用于将如报表,告警,仪表板或团队/库所有权分组,默认值是类名 */ String groupKey() default ""; /** * 命令的key值,默认值是方法名 */ String commandKey() default ""; /** * 线程池key,用于表示HystrixThreadPool,用于监视,度量标准发布,缓存和其他此类用途. */ String threadPoolKey() default ""; /** * 执行降级回调方法,指定的方法必须和注解方法在同一个类中,并且必须和注解方法有相同的方法签名 */ String fallbackMethod() default ""; /** * 配置命令模式的参数,具体参数对应类:HystrixCommandProperties */ HystrixProperty[] commandProperties() default {}; /** * 配置线程池参数,具体参数对应类:HystrixThreadPoolProperties */ HystrixProperty[] threadPoolProperties() default {}; /** * 定义需要忽略的异常。如果方法抛出的异常包含RUNTIME_EXCEPTION,会被包装成HystrixRuntimeException。具体逻辑在HystrixCommandAspect的hystrixRuntimeExceptionToThrowable方法 */ Class<? extends Throwable>[] ignoreExceptions() default {}; /** * 定义观察者命令执行方式,详细说明见ObservableExecutionMode。判断逻辑在CommandExecutor.execute方法中 */ ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER; /** * 如果hystrix方法抛出的异常包括RUNTIME_EXCEPTION,则会被封装HystrixRuntimeException异常。此方法定义需要抛出的异常 */ HystrixException[] raiseHystrixExceptions() default {}; /** * 默认降级回调方法,配置的方法不能有参数,返回值需要与注解的返回值兼容 */ String defaultFallback() default ""; }
这个注解需要和HystrixCommand注解配合使用。主要是用来做请求合并的,可以指定在某段时间内(默认10毫秒)调用此方法时会合并到一起执行。源码中有详细注释和示例,贴出来的进行了一部分翻译
package com.netflix.hystrix.contrib.javanica.annotation; import com.netflix.hystrix.HystrixCollapser.Scope; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 此注解用来将一些命令合并成单个后端依赖关系调用 * 此注解需要与{@link HystrixCommand}注解一起使用 * <p/> * 示例: * <pre> * @HystrixCollapser(batchMethod = "getUserByIds"){ * public Future<User> getUserById(String id) { * return null; * } * @HystrixCommand * public List<User> getUserByIds(List<String> ids) { * List<User> users = new ArrayList<User>(); * for (String id : ids) { * users.add(new User(id, "name: " + id)); * } * return users; * } *</pre> * * 使用{@link HystrixCollapser}注解的方法可以返回任何兼容的类型,返回结果并不影响合并请求的执行,甚至可以返回{@code null}或者其他子类 * 需要注意的是:注解的方法如果返回Future类型,那么泛型必须和命令方法返回的List泛型一致 * 例如: * <pre> * Future<User> - {@link HystrixCollapser}注解方法返回值 * List<User> - 批量命令方法返回值 * </pre> * <p/> * 注意:批量命令方法必须用{@link HystrixCommand}注解标记 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface HystrixCollapser { /** * 指定一个key,默认值是注解方法名 */ String collapserKey() default ""; /** * 批量命令方法的名称,指定的方法必须有如下的签名 * java.util.List method(java.util.List) * 注意:指定的方法只能有一个参数 */ String batchMethod(); /** * 指定合并请求的范围默认是{@link Scope#REQUEST} */ Scope scope() default Scope.REQUEST; /** * 指定合并请求的配置,具体参见{@link HystrixCollapserProperties} */ HystrixProperty[] collapserProperties() default {}; }
配置方式和属性说明
Hystrix的配置借助了Archaius来初始化,Archaius用于动态管理属性配置,是对apache common configuration类库的扩展。可以事先将多环境多个配置加载进去,支持定时刷新(线程安全),需要属性时可以直接获取。Archaius的配置类是ArchaiusAutoConfiguration,这里主要介绍一些属性的意思
还需要说明的是,属性配置可以是在properties文件中,也可以是在方法注解的属性里配置,两处配置的属性名称有区别,在properties里配置的属性是以 hystrix.command.default. 、 hystrix.threadpool.default 、 hystrix.collapser.default 开头,其中default表示默认值,如需要配置指定commandKey的值,将default换成commandKey即可。如果是在方法注解的属性里配置,则不需要这个前缀
/** * Command execution properties. */ # 隔离策略,默认是线程隔离,还有信号量隔离,参见枚举:ExecutionIsolationStrategy hystrix.command.default.execution.isolation.strategy=THREAD # 隔离线程超时时间,默认1s hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000 # 是否启用超时配置 hystrix.command.default.execution.timeout.enabled=true # 超时的时候是否中断隔离线程 hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true # 隔离线程正在执行取消操作时是否中断 hystrix.command.default.execution.isolation.thread.interruptOnFutureCancel=false # 隔离策略的最大信号量,只有使用信号量隔离策略时生效 hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10 /** * Command fallback properties.HystrixCommand.getFallback() */ # 降级方法的最大调用线程数,如果超出此信号量,会抛出异常 hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=10 # 是否启用降级 hystrix.command.default.fallback.enabled=true /** * Command circuit breaker properties. */ # 是否启用断路器 hystrix.command.default.circuitBreaker.enabled=true # 请求量阈值,请求量达到该值是会开启断路器 hystrix.command.default.circuitBreaker.requestVolumeThreshold=20 # 当断路器打开后,会直接拒绝请求,此时间是配置多长时候后再次尝试处理请求 hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000 # 打开断路器并走回退逻辑的错误率,默认50% hystrix.command.default.circuitBreaker.errorThresholdPercentage=50 # 是否强制打开断路器,打开后会直接拒绝所有请求 hystrix.command.default.circuitBreaker.forceOpen=false # 是否强制关闭断路器,关闭后会处理所有请求 hystrix.command.default.circuitBreaker.forceClosed=false /** * Command metrics properties.主要用于统计执行情况 */ # 统计的时间窗口值 hystrix.command.default.metrics.rollingStats.timeInMilliseconds=10000 # 统计时间窗口内分成的份数,需要保证timeInMilliseconds % numBuckets == 0 hystrix.command.default.metrics.rollingStats.numBuckets=10 # 是否启用百分数统计 hystrix.command.default.metrics.rollingPercentile.enabled=true # 百分数统计的时间周期 hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=60000 # 百分数统计时间内分成的份数 hystrix.command.default.metrics.rollingPercentile.numBuckets=6 # 百分数统计每份的最大数量。每个bucket只取这个配置数量的执行数来统计 hystrix.command.default.metrics.rollingPercentile.bucketSize=100 # 记录健康快照间隔毫秒数 hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=500 /** * Command CommandRequest Context properties. */ # 是否启用请求缓存。当HystrixCommand.getCacheKey()调用后,缓存到HystrixRequestCache hystrix.command.default.requestCache.enabled=true # 是否启用请求日志记录。HystrixCommand执行或者事件的日志到HystrixRequestLog hystrix.command.default.requestLog.enabled=true
- HystrixThreadPoolProperties
/** * Thread pool properties. */ # 是否启用maximumSize配置 hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize=false # 线程数量 hystrix.threadpool.default.coreSize=10 # 最大执行线程数 hystrix.threadpool.default.maximumSize=10 # 线程存活毫秒数 hystrix.threadpool.default.keepAliveTimeMinutes=1 # 最大等待线程队列,如果-1为SynchronousQueue;其他则为LinkedBlockingQueue hystrix.threadpool.default.maxQueueSize=-1 # 拒绝队列大小,即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。当maxQueueSize为-1,则该属性不可用 hystrix.threadpool.default.queueSizeRejectionThreshold=5 # 线程池统计时间窗口值 hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds=10000 # 线程池统计时间窗口内分成的份数 hystrix.threadpool.default.metrics.rollingStats.numBuckets=10
- HystrixCollapserProperties
/** * Collapser properties. */ # 批处理最大请求数,达到该值时就算没有达到时间也会触发批处理,默认值Integer.MAX_VALUE hystrix.collapser.default.maxRequestsInBatch=0x7fffffff # 触发批处理的延迟,在触发之前的同样请求可能会放到同一个批处理中 hystrix.collapser.default.timerDelayInMilliseconds=10 # 是否启用请求缓存 hystrix.collapser.default.requestCache.enabled=true # 统计时间窗口值 hystrix.collapser.default.metrics.rollingStats.timeInMilliseconds=10000 # 统计时间窗口内分成的份数 hystrix.collapser.default.metrics.rollingStats.numBuckets=10 # 是否启用百分数统计 hystrix.collapser.default.metrics.rollingPercentile.enabled=true # 百分数统计的时间周期 hystrix.collapser.default.metrics.rollingPercentile.timeInMilliseconds=60000 # 百分数统计时间内分成的份数 hystrix.collapser.default.metrics.rollingPercentile.numBuckets=6 # 百分数统计每份的最大数量。每个bucket只取这个配置数量的执行数来统计 hystrix.collapser.default.metrics.rollingPercentile.bucketSize=100
参考资料