spring-cloud源码解析-hystrix的基本介绍和配置属性说明
博客专区 > J猿 的博客 > 博客详情
spring-cloud源码解析-hystrix的基本介绍和配置属性说明
J猿 发表于1周前
spring-cloud源码解析-hystrix的基本介绍和配置属性说明
  • 发表于 1周前
  • 阅读 207
  • 收藏 15
  • 点赞 1
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

Hystrix简单介绍


  • Hystrix是一个延迟和容错库,目的是用来隔离远程系统、服务和第三方库的调用以及停止故障传播,并在无法完全避免发生故障的复杂的分布式系统中实现弹性

  • Hystrix主要的作用

    • 隔离(线程隔离、信号量隔离):主要是限制调用分布式服务的资源,避免个别服务出现问题时对其他服务产生影响

    • 熔断(容错):当失败率达到一定阈值时,熔断器触发快速失败

    • 降级(超时降级、熔断降级):触发降级时可以使用回调方法返回托底数据

    • 缓存:请求缓存、请求合并

    • 实时监控、报警

  • 源码地址:https://github.com/Netflix/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作为入口查看依赖的包

spring-cloud-starter-hystrix依赖包

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

spring-cloud-netflix-core中hystrix类

主要有两个配置类:HystrixAutoConfiguration、HystrixCircuitBreakerConfiguration。其中HystrixAutoConfiguration主要是hystrix的健康检查的配置,主要配置在HystrixCircuitBreakerConfiguration中,从里面加载的bean名称可以看到Hystrix的处理入口是:HystrixCommandAspect

@Bean
public HystrixCommandAspect hystrixCommandAspect() {
    return new HystrixCommandAspect();
}

HystrixCommandAspect这个类利用AOP切面对 HystrixCommandHystrixCollapser 两种注解的方法进行扩展处理。下面是截取的部分代码

/**
 * 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.defaulthystrix.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

参考资料


  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 52
博文 15
码字总数 33599
×
J猿
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: