Hystrix简单介绍
-
Hystrix是一个延迟和容错库,目的是用来隔离远程系统、服务和第三方库的调用以及停止故障传播,并在无法完全避免发生故障的复杂的分布式系统中实现弹性
-
Hystrix主要的作用
-
隔离(线程隔离、信号量隔离):主要是限制调用分布式服务的资源,避免个别服务出现问题时对其他服务产生影响
-
熔断(容错):当失败率达到一定阈值时,熔断器触发快速失败
-
降级(超时降级、熔断降级):触发降级时可以使用回调方法返回托底数据
-
缓存:请求缓存、请求合并
-
实时监控、报警
-
spring cloud整合Hystrix基本使用
- 引入maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
-
在启动类上增加注解@EnableCircuitBreaker或@EnableHystrix(此注解引入了@EnableCircuitBreaker)
-
在需要使用熔断降级的方法上增加注解,并配置降级方法
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;
}
}
这里简单介绍下两种注解的配置
- HystrixCommand
用于标记需要命令模式处理的方法
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 "";
}
- HystrixCollapser
这个注解需要和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即可。如果是在方法注解的属性里配置,则不需要这个前缀
- HystrixCommandProperties
/**
* 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
参考资料
- https://www.jianshu.com/p/e07661b9bae8
- http://blog.csdn.net/hry2015/article/details/78577695
- http://www.cnblogs.com/lexiaofei/p/7169483.html
- https://segmentfault.com/a/1190000012256788
- http://blog.csdn.net/u012314558/article/details/78347219
- http://tietang.wang/2016/02/25/hystrix/Hystrix%E5%8F%82%E6%95%B0%E8%AF%A6%E8%A7%A3/