SpringCloud-Hystrix原理与Dubbo整合

Hystrix原理与Dubbo整合

断路器使用命令模式将要执行的目标Tag方法,封装到HystrixCommand中执行(线程池、信号量),为微服务架构提供服务熔断、限流、降级机制的框架工具。
Hystrix提供多种隔离方式:

  • 线程池隔离
  • 信号量隔离
  • 熔断
  • 降级回退

Hystrix防止雪崩效应

  • Hystrix将请求逻辑封装,隔离在单独线程中执行
  • Hystrix有自动超时策略,如果外部请求超过阀值,Hystrix会以快速超时失败处理
  • Hystrix为每一个分组服务,提供一个线程池,当线程池满载时不会线程排队,会直接快速失败
  • Hystrix在管理的服务失效超过一定配置比例时,会自动切断服务一段时间。

Hystrix 工作原理

image

执行步骤如下:

    1. 创建HystrixCommand或者HystrixObservableCommand对象,将目标Tag方法封装到命令模式中。
    1. 执行命令execute()、queue()、observe()、toObservable()
    1. 如果请求结果缓存特性开启(不建议开启),并且命中缓存则从缓存获取Observable返回。
    1. 检查熔断状态,如果熔断机制启动,则执行getFallback命令,否则执行这个命令
    1. 检查执行命令的线程池或者信号量是否满载,如果满载则执行getFallback命令
    1. 执行HystrixCommand.run()或HystrixObservableCommand.construct(),如果超时或者执行失败则执行getFallback命令
    1. Hystrix 会将请求成功,失败,被拒绝或超时信息报告给熔断器,熔断器维护一些用于统计数据用的计数器

Circuit Breaker 断路器

HystrixCircuitBreaker断路器实现接口:

public interface HystrixCircuitBreaker {

    /**
     * Every {@link HystrixCommand} requests asks this if it is allowed to proceed or not.  It is idempotent and does
     * not modify any internal state, and takes into account the half-open logic which allows some requests through
     * after the circuit has been opened
     * 
     * @return boolean whether a request should be permitted
     */
    boolean allowRequest();

    /**
     * Whether the circuit is currently open (tripped).
     * 
     * @return boolean state of circuit breaker
     */
    boolean isOpen();

    /**
     * Invoked on successful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markSuccess();

    /**
     * Invoked on unsuccessful executions from {@link HystrixCommand} as part of feedback mechanism when in a half-open state.
     */
    void markNonSuccess();

    /**
     * Invoked at start of command execution to attempt an execution.  This is non-idempotent - it may modify internal
     * state.
     */
    boolean attemptExecution();
}

实现子类有两个:

  • NoOpCircuitBreaker:断路器空实现
  • HystrixCircuitBreakerImpl:完整的断路器实现,在AbstractCommand构造函数中初始化HystrixCircuitBreaker(通过HystrixCircuitBreaker.Factory构造)。

HystrixCircuitBreaker 工厂

CircuitBreaker 工厂中维护了基于HystrixCommandKey的单例断路器对象实例。

private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap<String, HystrixCircuitBreaker>();

Factory代码如下:

ublic static class Factory {
        //用一个ConcurrentHashMap来保存HystrixCircuitBreaker对象
        private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap<String, HystrixCircuitBreaker>();
        
//Hystrix首先会检查ConcurrentHashMap中有没有对应的缓存的断路器,如果有的话直接返回。如果没有的话就会新创建一个HystrixCircuitBreaker实例,将其添加到缓存中并且返回
        public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
            
            HystrixCircuitBreaker previouslyCached = circuitBreakersByCommand.get(key.name());
            if (previouslyCached != null) {
                return previouslyCached;
            }

            
            HystrixCircuitBreaker cbForCommand = circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreakerImpl(key, group, properties, metrics));
            if (cbForCommand == null) {
                return circuitBreakersByCommand.get(key.name());
            } else {
                return cbForCommand;
            }
        }

        
        public static HystrixCircuitBreaker getInstance(HystrixCommandKey key) {
            return circuitBreakersByCommand.get(key.name());
        }

        static void reset() {
            circuitBreakersByCommand.clear();
        }
}

执行原理

HystrixCommand在执行的时候会与HystrixCircuitBreaker交互,执行之前会根据断路器状态来决定后续流程,执行命令成功、超时、失败又会向断路器汇报,断路器根据这些数据修改自身状态。
断路器流程图:


image

断路器可以通过6个参数配置:

  • circuitBreaker.enabled:是否启动断路器,默认时true
  • circuitBreaker.forceOpen:是否强制打开,始终保持打开状态。
  • circuitBreaker.forceClosed:强制断路器关闭,始终保持关闭
  • circuitBreaker.errorThresholdPercentage:设定错误比例,一个时间窗口内错误请求超过给配置则断路器打开
  • circuitBreaker.requestVolumeThreshold:默认20,至少有20个请求参与errorThresholdPercentage错误百分比计算,这个参数是断路器起作用的关键
// check if we are past the statisticalWindowVolumeThreshold
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
    // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything
    return false;
}

if (health.getErrorPercentage() < properties.circuitBreakerErrorThresholdPercentage().get()) {
    return false;
}
  • circuitBreaker.sleepWindowInMilliseconds:半开试探时间默认值5000ms。断路器开启该配置时间后,会尝试放部分量过去,确定以来服务是否可用
  • metrics.rollingStats.timeInMilliseconds:设置滑动窗口统计时间,默认10s
  • metrics.rollingStats.numBuckets:设置滑动统计桶数量,默认10

command可以配置参数:

  • execution.isolation.strategy:执行隔离策略THREAD|SEMAPHORE;THREAD策略每次在一个线程中执行,并发请求数限制于线程池中线程数量;SEMAPHORE策略在调用线程中执行,并发请求书受限于semaphore信号量的值
  • execution.isolation.thread.timeoutInMilliseconds:请求超时时间,默认1s
  • execution.timeout.enabled:是否开启超时,默认true
  • execution.isolation.thread.interruptOnTimeout:超时情况下是否中断HystrixCommand.run()执行,默认true
  • fallback.enabled:是否开启fallback,默认true

Command 线程池配置参数

  • coreSize:设置线程池最大并发数量,默认10
  • maximumSize:设置线程池最大并发数量,默认与coreSize一致
  • maxQueueSize:最大队列长度,默认-1 SynchronizeQueue 实现
  • queueSizeRejectionThreshold:设置拒绝请求的临界值,当线程池占满+临界值占满时,请求会立即拒绝
  • keepAliveTimeMinutes:设置keep-alive时间,默认1分钟,只有当maximumSize起作用的时候才会生效,该配置控制一个线程多久没有使用后释放
  • allowMaximumSizeToDivergeFromCoreSize:确认maximumSize参数是否起作用,默认false

Dubbo 集成 Hystrix

引入hystrix到pom

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>1.4.4.RELEASE</version>
</dependency>

Application类上增加@EnableHystrix来启用hystrix starter

@SpringBootApplication
@EnableHystrix
public class ProviderApplication {

配置Provider端

在Dubbo的Provider上增加@HystrixCommand配置,这样子调用就会经过Hystrix代理。

@Service(version = "1.0.0")
public class HelloServiceImpl implements HelloService {
    @HystrixCommand(commandProperties = {
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
    @Override
    public String sayHello(String name) {
        // System.out.println("async provider received: " + name);
        // return "annotation: hello, " + name;
        throw new RuntimeException("Exception to show hystrix enabled.");
    }
}

配置Consumer端

对于Consumer端,则可以增加一层method调用,并在method上配置@HystrixCommand。当调用出错时,会走到fallbackMethod = "reliable"的调用里。

@Reference(version = "1.0.0")
private HelloService demoService;

@HystrixCommand(fallbackMethod = "reliable")
public String doSayHello(String name) {
    return demoService.sayHello(name);
}
public String reliable(String name) {
    return "hystrix fallback value";
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容