谈谈我对服务熔断、服务降级的理解 专题

伴随着微服务架构被宣传得如火如荼,一些概念也被推到了咱们面前(管你接受不接受),其实大多数概念之前就有,但不多被提的这么频繁(如今好像不说起都很差意思交流了)。
想起有人总结的一句话,微服务架构的特色就是:“一解释就懂,一问就不知,一讨论就吵架”。

其实对老外的总结能力一直特别崇拜,Kevin Kelly、Martin Fowler、Werner Vogels……,都是著名的“演讲家”。正好这段时间看了些微服务、容器的相关资料,也在咱们新一代产品中进行了部分实践,回过头来,再来谈谈对一些概念的理解。php

 
今天先来讲说“服务熔断”和“服务降级”。为何要说这个呢,由于我很长时间里都把这两个概念同质化了,不知道这两个词你们怎么理解,一个意思or有所不一样?如今的我是这么来看的:
  1. 在股票市场,熔断这个词你们都不陌生,是指当股指波幅达到某个点后,交易所为控制风险采起的暂停交易措施。相应的,服务熔断通常是指软件系统中,因为某些缘由使得服务出现了过载现象,为防止形成整个系统故障,从而采用的一种保护措施,因此不少地方把熔断亦称为过载保护。
  2. 你们都见过女生旅行吧,大号的旅行箱是必备物,日常走走近处绰绰有余,但一旦出个远门,再大的箱子都白搭了,怎么办呢?常见的情景就是把物品拿出来分分堆,比了又比,最后一些非必需品的就忍痛放下了,等到下次箱子够用了,再带上用一用。而服务降级,就是这么回事,总体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。
因此从上述分析来看,二者其实从有些角度看是有必定的相似性的:
  1. 目的很一致,都是从可用性可靠性着想,为防止系统的总体缓慢甚至崩溃,采用的技术手段;
  2. 最终表现相似,对于二者来讲,最终让用户体验到的是某些功能暂时不可达或不可用;
  3. 粒度通常都是服务级别,固然,业界也有很多更细粒度的作法,好比作到数据持久层(容许查询,不容许增删改);
  4. 自治性要求很高,熔断模式通常都是服务基于策略的自动触发,降级虽然说可人工干预,但在微服务架构下,彻底靠人显然不可能,开关预置、配置中心都是必要手段;
而二者的区别也是明显的:
  1. 触发缘由不太同样,服务熔断通常是某个服务(下游服务)故障引发,而服务降级通常是从总体负荷考虑;
  2. 管理目标的层次不太同样,熔断实际上是一个框架级的处理,每一个微服务都须要(无层级之分),而降级通常须要对业务有层级之分(好比降级通常是从最外围服务开始)
  3. 实现方式不太同样,这个区别后面会单独来讲;
固然这只是我我的对二者的理解,外面把二者归为彻底一致的也不在少数,或者把熔断机制理解为应对降级目标的一种实现也说的过去,可能“一讨论就吵架”也正是这个缘由吧!
概念算是说完了,避免空谈,我再总结下对经常使用的实现方法的理解。对于这两个概念,号称支持的框架可很多,Hystrix当属其中的佼佼者。
先说说最裸的熔断器的设计思路,下面这张图你们应该不陌生(我只是参考着又画了画),简明扼要的给出了好的熔断器实现的三个状态机:

  1. Closed:熔断器关闭状态,调用失败次数积累,到了阈值(或必定比例)则启动熔断机制;
  2. Open:熔断器打开状态,此时对下游的调用都内部直接返回错误,不走网络,但设计了一个时钟选项,默认的时钟达到了必定时间(这个时间通常设置成平均故障处理时间,也就是MTTR),到了这个时间,进入半熔断状态;
  3. Half-Open:半熔断状态,容许定量的服务请求,若是调用都成功(或必定比例)则认为恢复了,关闭熔断器,不然认为还没好,又回到熔断器打开状态;
那Hystrix,做为Netflix开源框架中的最受喜好组件之一,是怎么处理依赖隔离,实现熔断机制的呢,他的处理远比我上面说个实现机制复杂的多。
一块儿来看看核心代码吧,我只保留了代码片断的关键部分:
public abstract class HystrixCommand<R> extends AbstractCommand<R> implements HystrixExecutable<R>, HystrixInvokableInfo<R>, HystrixObservable<R> {  
  
    protected abstract R run() throws Exception;  
  
    protected R getFallback() {  
        throw new UnsupportedOperationException("No fallback available.");  
    }  
  
    @Override  
    final protected Observable<R> getExecutionObservable() {  
        return Observable.defer(new Func0<Observable<R>>() {  
            @Override  
            public Observable<R> call() {  
                try {  
                    return Observable.just(run());  
                } catch (Throwable ex) {  
                    return Observable.error(ex);  
                }  
            }  
        });  
    }  
  
    @Override  
    final protected Observable<R> getFallbackObservable() {  
        return Observable.defer(new Func0<Observable<R>>() {  
            @Override  
            public Observable<R> call() {  
                try {  
                    return Observable.just(getFallback());  
                } catch (Throwable ex) {  
                    return Observable.error(ex);  
                }  
            }  
        });  
    }  
  
    public R execute() {  
        try {  
            return queue().get();  
        } catch (Exception e) {  
            throw decomposeException(e);  
        }  
    } 

 

HystrixCommand是重重之重,在Hystrix的整个机制中,涉及到依赖边界的地方,都是经过这个Command模式进行调用的,显然,这个Command负责了核心的服务熔断和降级的处理,子类要实现的方法主要有两个:
  1. run方法:实现依赖的逻辑,或者说是实现微服务之间的调用;
  2. getFallBack方法:实现服务降级处理逻辑,只作熔断处理的则可不实现;
使用时,可参考以下方式:
public class TestCommand extends HystrixCommand<String> {  
  
    protected TestCommand(HystrixCommandGroupKey group) {  
        super(group);  
    }  
  
    @Override  
    protected String run() throws Exception {  
        //这里须要作实际调用逻辑  
        return "Hello";  
    }  
      
    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {  
        TestCommand command = new TestCommand(HystrixCommandGroupKey.Factory.asKey("TestGroup"));  
          
        //1.这个是同步调用  
        command.execute();  
          
        //2.这个是异步调用  
        command.queue().get(500, TimeUnit.MILLISECONDS);  
          
        //3.异步回调  
        command.observe().subscribe(new Action1<String>() {  
            public void call(String arg0) {  
                  
            }  
        });  
    }  
}  

 

细心的同窗确定发现Command机制里大量使用了Observable相关的API,这个是什么呢?原来其隶属于RxJava,这个框架就很少介绍了 --- 响应式开发,也是Netflix的做品之一,具体你们可参考这系列博客,我以为做者写的很通俗:http://blog.csdn.net/lzyzsd/article/details/41833541/
接着呢,你们必定会问,那以前说的熔断阈值设置等,都在哪块作的呢?再来看看另外一块核心代码:
public abstract class HystrixPropertiesStrategy {  
  
    public HystrixCommandProperties getCommandProperties(HystrixCommandKey commandKey, HystrixCommandProperties.Setter builder) {  
        return new HystrixPropertiesCommandDefault(commandKey, builder);  
    }  
  
    ......  
}  

 

这个类做为策略类,返回相关的属性配置,你们可从新实现。而在具体的策略中,主要包括如下几种策略属性配置:

 

  1. circuitBreakerEnabled:是否容许熔断,默认容许;
  2. circuitBreakerRequestVolumeThreshold:熔断器是否开启的阀值,也就是说单位时间超过了阀值请求数,熔断器才开;
  3. circuitBreakerSleepWindowInMilliseconds:熔断器默认工做时间,超过此时间会进入半开状态,即容许流量作尝试;
  4. circuitBreakerErrorThresholdPercentage:错误比例触发熔断;
  5. ......

 

属性不少,这里就不一一说明了,你们可参考HystrixCommandProperties类里的详细定义。还有一点要着重说明的,在熔断器的设计里,隔离采用了线程的方式(听说还有信号的方式,这两个区别我还没搞太明白),处理依赖并发和阻塞扩展,示意图以下:
如上图,好处也很明显,对于每一个依赖都有独立可控的线程池,固然高并发时,CPU切换较多,有必定的影响。
啰嗦了一堆,最后总结一下,我认为服务熔断和服务降级二者是有区别的,同时经过对Hystrix的简单学习,了解了其实现机制,会逐步引入到咱们的产品研发中。固然还有好多概念:服务限流、分流,请求与依赖分离等,后面有时间一一与你们分享。 
http://blog.csdn.net/guwei9111986/article/details/51649240
http://www.primeton.com/read.php?id=2230&his=1



 

Sentinel: 分布式系统的流量防卫兵

Sentinel 是什么?

随着微服务的流行,服务和服务之间的稳定性变得愈来愈重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。html

Sentinel 具备如下特征:java

  • 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量能够承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。
  • 完备的实时监控:Sentinel 同时提供实时的监控功能。您能够在控制台中看到接入应用的单台机器秒级数据,甚至 500 台如下规模的集群的汇总运行状况。
  • 普遍的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只须要引入相应的依赖并进行简单的配置便可快速地接入 Sentinel。
  • 完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您能够经过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。

Sentinel 的主要特性:react

 

Sentinel 的开源生态:git

 

Sentinel 分为两个部分:github

  • 核心库(Java 客户端)不依赖任何框架/库,可以运行于全部 Java 运行时环境,同时对 Dubbo / Spring Cloud 等框架也有较好的支持。
  • 控制台(Dashboard)基于 Spring Boot 开发,打包后能够直接运行,不须要额外的 Tomcat 等应用容器。

Quick Start

1.1 公网Demo:

若是你想最快的了解Sentinel在作什么,你能够经过Sentinel 新手指南 来运行一个例子,而且能在控制台上看到最直观的监控,流控效果等。web

1.2 手动接入Sentinel以及Dashboard

下面的例子将展现应用如何三步接入 Sentinel。同时,Sentinel 也提供所见即所得的控制台,能够实时监控资源以及管理规则。算法

STEP 1. 在应用中引入Sentinel Jar包

若是应用使用 pom 工程,则在 pom.xml 文件中加入如下代码便可:spring

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>x.y.z</version>
</dependency>

 

 

注意: Sentinel 仅支持 Java 6 或者以上版本。若是您未使用依赖管理工具,请到 Maven Center Repository 直接下载 JAR 包。数据库

STEP 2. 定义资源

接下来,把须要控制流量的代码用 Sentinel API SphU.entry("HelloWorld") 和 entry.exit() 包围起来便可。在下面的例子中,咱们将 System.out.println("hello wolrd"); 做为资源,用 API 包围起来。参考代码以下:

public static void main(String[] args) {
    initFlowRules();
    while (true) {
        Entry entry = null;
        try {
        entry = SphU.entry("HelloWorld");
            /*您的业务逻辑 - 开始*/
            System.out.println("hello world");
            /*您的业务逻辑 - 结束*/
    } catch (BlockException e1) {
            /*流控逻辑处理 - 开始*/
        System.out.println("block!");
            /*流控逻辑处理 - 结束*/
    } finally {
       if (entry != null) {
           entry.exit();
       }
    }
    }
}

 

 

完成以上两步后,代码端的改造就完成了。固然,咱们也提供了 注解支持模块,能够以低侵入性的方式定义资源。

STEP 3. 定义规则

接下来,经过规则来指定容许该资源经过的请求次数,例以下面的代码定义了资源 HelloWorld 每秒最多只能经过 20 个请求。

private static void initFlowRules(){
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("HelloWorld");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // Set limit QPS to 20.
    rule.setCount(20);
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

 

 

完成上面 3 步,Sentinel 就可以正常工做了。更多的信息能够参考 使用文档

STEP 4. 检查效果

Demo 运行以后,咱们能够在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的输出:

|--timestamp-|------date time----|--resource-|p |block|s |e|rt
1529998904000|2018-06-26 15:41:44|hello world|20|0    |20|0|0
1529998905000|2018-06-26 15:41:45|hello world|20|5579 |20|0|728
1529998906000|2018-06-26 15:41:46|hello world|20|15698|20|0|0
1529998907000|2018-06-26 15:41:47|hello world|20|19262|20|0|0
1529998908000|2018-06-26 15:41:48|hello world|20|19502|20|0|0
1529998909000|2018-06-26 15:41:49|hello world|20|18386|20|0|0

其中 p 表明经过的请求, block 表明被阻止的请求, s 表明成功执行完成的请求个数, e 表明用户自定义的异常, rt 表明平均响应时长。

能够看到,这个程序每秒稳定输出 "hello world" 20 次,和规则中预先设定的阈值是同样的。

更详细的说明能够参考: 如何使用

更多的例子能够参考: Demo

STEP 5. 启动 Sentinel 控制台

您能够参考 Sentinel 控制台文档 启动控制台,能够实时监控各个资源的运行状况,而且能够实时地修改限流规则。

详细文档

请移步 Wiki,查阅详细的文档、示例以及使用说明。若您但愿从其它熔断降级组件(如 Hystrix)迁移或进行功能对比,能够参考 迁移指南

Please refer to README for README in English。

与 Sentinel 相关的生态(包括社区用户实现的扩展、整合、示例以及文章)能够参见 Awesome Sentinel,欢迎补充!

若是您正在使用 Sentinel,欢迎在 Wanted: Who is using Sentinel 留言告诉咱们您的使用场景,以便咱们更好地去改进。

https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D


Sentinel 与 Hystrix 的对比

Sentinel 是阿里中间件团队研发的面向分布式服务架构的轻量级高可用流量控制组件,最近正式开源。Sentinel 主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。你们可能会问:Sentinel 和以前经常使用的熔断降级库 Netflix Hystrix 有什么异同呢?本文将从多个角度对 Sentinel 和 Hystrix 进行对比,帮助你们进行技术选型。

Overview

先来看一下 Hystrix 的官方介绍:

Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency.

能够看到 Hystrix 的关注点在于以 隔离 和 熔断 为主的容错机制,超时或被熔断的调用将会快速失败,并能够提供 fallback 机制。

而 Sentinel 的侧重点在于:

  • 多样化的流量控制
  • 熔断降级
  • 系统负载保护
  • 实时监控和控制台

能够看到二者解决的问题仍是有比较大的不一样的,下面咱们来分别对比一下。

共同特性

资源模型和执行模型上的对比

Hystrix 的资源模型设计上采用了命令模式,将对外部资源的调用和 fallback 逻辑封装成一个命令对象(HystrixCommand / HystrixObservableCommand),其底层的执行是基于 RxJava 实现的。每一个 Command 建立时都要指定 commandKey 和 groupKey(用于区分资源)以及对应的隔离策略(线程池隔离 or 信号量隔离)。线程池隔离模式下须要配置线程池对应的参数(线程池名称、容量、排队超时等),而后 Command 就会在指定的线程池按照指定的容错策略执行;信号量隔离模式下须要配置最大并发数,执行 Command 时 Hystrix 就会限制其并发调用。

Sentinel 的设计则更为简单。相比 Hystrix Command 强依赖隔离规则,Sentinel 的资源定义与规则配置的耦合度更低。Hystrix 的 Command 强依赖于隔离规则配置的缘由是隔离规则会直接影响 Command 的执行。在执行的时候 Hystrix 会解析 Command 的隔离规则来建立 RxJava Scheduler 并在其上调度执行,如果线程池模式则 Scheduler 底层的线程池为配置的线程池,如果信号量模式则简单包装成当前线程执行的 Scheduler。而 Sentinel 并不指定执行模型,也不关注应用是如何执行的。Sentinel 的原则很是简单:根据对应资源配置的规则来为资源执行相应的限流/降级/负载保护策略。在 Sentinel 中资源定义和规则配置是分离的。用户先经过 Sentinel API 给对应的业务逻辑定义资源(埋点),而后能够在须要的时候配置规则。埋点方式有两种:

  • try-catch 方式(经过 SphU.entry(...)),用户在 catch 块中执行异常处理 / fallback
  • if-else 方式(经过 SphO.entry(...)),当返回 false 时执行异常处理 / fallback

将来 Sentinel 还会引入基于注解的资源定义方式,同时能够经过注解参数指定异常处理函数和 fallback 函数。

Sentinel 提供多样化的规则配置方式。除了直接经过 loadRules API 将规则注册到内存态以外,用户还能够注册各类外部数据源来提供动态的规则。用户能够根据系统当前的实时状况去动态地变动规则配置,数据源会将变动推送至 Sentinel 并即时生效。

隔离设计上的对比

隔离是 Hystrix 的核心功能之一。Hystrix 提供两种隔离策略:线程池隔离(Bulkhead Pattern)和信号量隔离,其中最推荐也是最经常使用的是线程池隔离。Hystrix 的线程池隔离针对不一样的资源分别建立不一样的线程池,不一样服务调用都发生在不一样的线程池中,在线程池排队、超时等阻塞状况时能够快速失败,并能够提供 fallback 机制。线程池隔离的好处是隔离度比较高,能够针对某个资源的线程池去进行处理而不影响其它资源,可是代价就是线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。

可是,实际状况下,线程池隔离并无带来很是多的好处。首先就是过多的线程池会很是影响性能。考虑这样一个场景,在 Tomcat 之类的 Servlet 容器使用 Hystrix,自己 Tomcat 自身的线程数目就很是多了(可能到几十或一百多),若是加上 Hystrix 为各个资源建立的线程池,总共线程数目会很是多(几百个线程),这样上下文切换会有很是大的损耗。另外,线程池模式比较完全的隔离性使得 Hystrix 能够针对不一样资源线程池的排队、超时状况分别进行处理,但这实际上是超时熔断和流量控制要解决的问题,若是组件具有了超时熔断和流量控制的能力,线程池隔离就显得没有那么必要了。

Sentinel 能够经过并发线程数模式的流量控制来提供信号量隔离的功能。这样的隔离很是轻量级,仅限制对某个资源调用的并发数,而不是显式地去建立线程池,因此 overhead 比较小,可是效果不错。而且结合基于响应时间的熔断降级模式,能够在不稳定资源的平均响应时间比较高的时候自动降级,防止过多的慢调用占满并发数,影响整个系统。而 Hystrix 的信号量隔离比较简单,没法对慢调用自动进行降级,只能等待客户端本身超时,所以仍然可能会出现级联阻塞的状况。

熔断降级对比

Sentinel 和 Hystrix 的熔断降级功能本质上都是基于熔断器模式(Circuit Breaker Pattern)。Sentinel 与 Hystrix 都支持基于失败比率(异常比率)的熔断降级,在调用达到必定量级而且失败比率达到设定的阈值时自动进行熔断,此时全部对该资源的调用都会被 block,直到过了指定的时间窗口后才启发性地恢复。上面提到过,Sentinel 还支持基于平均响应时间的熔断降级,能够在服务响应时间持续飙高的时候自动熔断,拒绝掉更多的请求,直到一段时间后才恢复。这样能够防止调用很是慢形成级联阻塞的状况。

实时指标统计实现对比

Hystrix 和 Sentinel 的实时指标数据统计实现都是基于滑动窗口的。Hystrix 1.5 以前的版本是经过环形数组实现的滑动窗口,经过锁配合 CAS 的操做对每一个桶的统计信息进行更新。Hystrix 1.5 开始对实时指标统计的实现进行了重构,将指标统计数据结构抽象成了响应式流(reactive stream)的形式,方便消费者去利用指标信息。同时底层改形成了基于 RxJava 的事件驱动模式,在服务调用成功/失败/超时的时候发布相应的事件,经过一系列的变换和聚合最终获得实时的指标统计数据流,能够被熔断器或 Dashboard 消费。

Sentinel 目前抽象出了 Metric 指标统计接口,底层能够有不一样的实现,目前默认的实现是基于 LeapArray 的滑动窗口,后续根据须要可能会引入 reactive stream 等实现。

Sentinel 的特点

除了以前提到的二者的共同特性以外,Sentinel 还提供如下的特点功能:

轻量级、高性能

Sentinel 做为一个功能完备的高可用流量管控组件,其核心 sentinel-core 没有任何多余依赖,打包后只有不到 200 KB,很是轻量级。开发者能够放心地引入 sentinel-core 而不需担忧依赖问题。同时,Sentinel 提供了多种扩展点,用户能够很方便地根据需求去进行扩展,而且无缝地切合到 Sentinel 中。

引入 Sentinel 带来的性能损耗很是小。只有在业务单机量级超过 25W QPS 的时候才会有一些显著的影响(5% - 10% 左右),单机 QPS 不太大的时候损耗几乎能够忽略不计。

流量控制

Sentinel 能够针对不一样的调用关系,以不一样的运行指标(如 QPS、并发调用数、系统负载等)为基准,对资源调用进行流量控制,将随机的请求调整成合适的形状。

Sentinel 支持多样化的流量整形策略,在 QPS 太高的时候能够自动将流量调整成合适的形状。经常使用的有:

    • 直接拒绝模式:即超出的请求直接拒绝。
    • 慢启动预热模式:当流量激增的时候,控制流量经过的速率,让经过的流量缓慢增长,在必定时间内逐渐增长到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。

  • 匀速器模式:利用 Leaky Bucket 算法实现的匀速模式,严格控制了请求经过的时间间隔,同时堆积的请求将会排队,超过超时时长的请求直接被拒绝。

 

Sentinel 还支持基于调用关系的限流,包括基于调用方限流、基于调用链入口限流、关联流量限流等,依托于 Sentinel 强大的调用链路统计信息,能够提供精准的不一样维度的限流。

目前 Sentinel 对异步调用链路的支持还不是很好,后续版本会着重改善支持异步调用。

系统负载保护

Sentinel 对系统的维度提供保护,负载保护算法借鉴了 TCP BBR 的思想。当系统负载较高的时候,若是仍持续让请求进入,可能会致使系统崩溃,没法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。若是这个时候其它的机器也处在一个边缘状态的时候,这个增长的流量就会致使这台机器也崩溃,最后致使整个集群不可用。针对这个状况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围以内处理最多的请求。

 

实时监控与控制面板

Sentinel 提供 HTTP API 用于获取实时的监控信息,如调用链路统计信息、簇点信息、规则信息等。若是用户正在使用 Spring Boot/Spring Cloud 并使用了 Sentinel Spring Cloud Starter,还能够方便地经过其暴露的 Actuator Endpoint 来获取运行时的一些信息,如动态规则等。将来 Sentinel 还会支持标准化的指标监控 API,能够方便地整合各类监控系统和可视化系统,如 Prometheus、Grafana 等。

Sentinel 控制台(Dashboard)提供了机器发现、配置规则、查看实时监控、查看调用链路信息等功能,使得用户能够很是方便地去查看监控和进行配置。

 

生态

Sentinel 目前已经针对 Servlet、Dubbo、Spring Boot/Spring Cloud、gRPC 等进行了适配,用户只需引入相应依赖并进行简单配置便可很是方便地享受 Sentinel 的高可用流量防御能力。将来 Sentinel 还会对更多经常使用框架进行适配,而且会为 Service Mesh 提供集群流量防御的能力。

总结

最后用表格来进行对比总结:

  Sentinel Hystrix
隔离策略 基于并发数 线程池隔离/信号量隔离
熔断降级策略 基于响应时间或失败比率 基于失败比率
实时指标实现 滑动窗口 滑动窗口(基于 RxJava)
规则配置 支持多种数据源 支持多种数据源
扩展性 多个扩展点 插件的形式
基于注解的支持 即将发布 支持
调用链路信息 支持同步调用 不支持
限流 基于 QPS / 并发数,支持基于调用关系的限流 不支持
流量整形 支持慢启动、匀速器模式 不支持
系统负载保护 支持 不支持
实时监控 API 各式各样 较为简单
控制台 开箱即用,可配置规则、查看秒级监控、机器发现等 不完善
常见框架的适配 Servlet、Spring Cloud、Dubbo、gRPC 等 Servlet、Spring Cloud Netflix

如有 Sentinel 设计上的疑问或讨论,欢迎来提 issue。若对 Sentinel 的开发感兴趣,不要犹豫,欢迎加入咱们,咱们随时欢迎贡献!

https://yq.aliyun.com/articles/623424

Hystrix工做原理(官方文档翻译)

工做流程图


下面的流程图展现了当使用Hystrix的依赖请求,Hystrix是如何工做的。

 

​ 下面将更详细的解析每个步骤都发生哪些动做:

  • 构建一个HystrixCommand或者HystrixObservableCommand对象。

    第一步就是构建一个HystrixCommand或者HystrixObservableCommand对象,该对象将表明你的一个依赖请求,向构造函数中传入请求依赖所须要的参数。

    若是构建HystrixCommand中的依赖返回单个响应,例如:

    HystrixCommand command = new HystrixCommand(arg1, arg2);

    若是依赖须要返回一个Observable来发射响应,就须要经过构建HystrixObservableCommand对象来完 成,例如:

    HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
  • 执行命令

    有4种方式能够执行一个Hystrix命令。

    • execute()—该方法是阻塞的,从依赖请求中接收到单个响应(或者出错时抛出异常)。
    • queue()—从依赖请求中返回一个包含单个响应的Future对象。
    • observe()—订阅一个从依赖请求中返回的表明响应的Observable对象。
    • toObservable()—返回一个Observable对象,只有当你订阅它时,它才会执行Hystrix命令并发射响应。
    K             value   = command.execute();
    Future<K>     fValue  = command.queue();
    Observable<K> ohValue = command.observe();         //hot observable Observable<K> ocValue = command.toObservable(); //cold observable

同步调用方法execute()实际上就是调用queue().get()方法,queue()方法的调用的是toObservable().toBlocking().toFuture().也就是说,最终每个HystrixCommand都是经过Observable来实现的,即便这些命令仅仅是返回一个简单的单个值。

  • 响应是否被缓存

    若是这个命令的请求缓存已经开启,而且本次请求的响应已经存在于缓存中,那么就会当即返回一个包含缓存响应的Observable(下面将Request Cache部分将对请求的cache作讲解)。

  • 回路器是否打开

    当命令执行执行时,Hystrix会检查回路器是否被打开。

    若是回路器被打开(或者tripped),那么Hystrix就不会再执行命名,而是直接路由到第8步,获取fallback方法,并执行fallback逻辑。

    若是回路器关闭,那么将进入第5步,检查是否有足够的容量来执行任务。(其中容量包括线程池的容量,队列的容量等等)。

  • 线程池、队列、信号量是否已满

    若是与该命令相关的线程池或者队列已经满了,那么Hystrix就不会再执行命令,而是当即跳到第8步,执行fallback逻辑。

  • HystrixObservableCommand.construct() 或者 HystrixCommand.run()

    在这里,Hystrix经过你写的方法逻辑来调用对依赖的请求,经过下列之一的调用:

    • HystrixCommand.run()—返回单个响应或者抛出异常。
    • HystrixObservableCommand.construct() —返回一个发射响应的Observable或者发送一个onError()的通知。

      若是执行run()方法或者construct()方法的执行时间大于命令所设置的超时时间值,那么该线程将会抛出一个TimeoutException异常(或者若是该命令没有运行在它本身的线程中,[or a separate timer thread will, if the command itself is not running in its own thread])。在这种状况下,Hystrix将会路由到第8步,执行fallback逻辑,而且若是run()或者construct()方法没有被取消或者中断,会丢弃这两个方法最终返回的结果。

      请注意,没有任何方式能够强制终止一个潜在[latent]的线程的运行,Hystrix可以作的最好的方式是让JVM抛出一个InterruptedException异常,若是你的任务被Hystrix所包装,并不意味着会抛出一个InterruptedExceptions异常,该线程在Hystrix的线程池内会进行执行,虽然在客户端已经接收到了TimeoutException异常,这个行为可以渗透到Hystrix的线程池中,[though the load is 'correctly shed'],绝大多数的Http Client不会将这一行为视为InterruptedExceptions,因此,请确保正确配置链接或者读取/写入的超时时间。

      若是命令最终返回了响应而且没有抛出任何异常,Hystrix在返回响应后会执行一些log和指标的上报,若是是调用run()方法,Hystrix会返回一个Observable,该Observable会发射单个响应而且会调用onCompleted方法来通知响应的回调,若是是调用construct()方法,Hystrix会经过construct()方法返回相同的Observable对象。

  • 计算回路指标[Circuit Health]

    Hystrix会报告成功、失败、拒绝和超时的指标给回路器,回路器包含了一系列的滑动窗口数据,并经过该数据进行统计。

    它使用这些统计数据来决定回路器是否应该熔断,若是须要熔断,将在必定的时间内不在请求依赖[短路请求](译者:这必定的时候能够经过配置指定),当再一次检查请求的健康的话会从新关闭回路器。

  • 获取FallBack

    当命令执行失败时,Hystrix会尝试执行自定义的Fallback逻辑:

    • construct()或者run()方法执行过程当中抛出异常。
    • 当回路器打开,命令的执行进入了熔断状态。
    • 当命令执行的线程池和队列或者信号量已经满容。
    • 命令执行超时。

写一个fallback方法,提供一个不须要网络依赖的通用响应,从内存缓存或者其余的静态逻辑获取数据。若是再fallback内必须须要网络的调用,更好的作法是使用另外一个HystrixCommand或者HystrixObservableCommand

若是你的命令是继承自HystrixCommand,那么能够经过实现HystrixCommand.getFallback()方法返回一个单个的fallback值。

若是你的命令是继承自HystrixObservableCommand,那么能够经过实现HystrixObservableCommand.resumeWithFallback()方法返回一个Observable,而且该Observable可以发射出一个fallback值。

Hystrix会把fallback方法返回的响应返回给调用者。

若是你没有为你的命令实现fallback方法,那么当命令抛出异常时,Hystrix仍然会返回一个Observable,可是该Observable并不会发射任何的数据,而且会当即终止并调用onError()通知。经过这个onError通知,能够将形成该命令抛出异常的缘由返回给调用者。

失败或不存在回退的结果将根据您如何调用Hystrix命令而有所不一样:
    • execute():抛出一个异常。
    • queue():成功返回一个Future,可是若是调用get()方法,将会抛出一个异常。
    • observe():返回一个Observable,当你订阅它时,它将当即终止,并调用onError()方法。
    • toObservable():返回一个Observable,当你订阅它时,它将当即终止,并调用onError()方法。
  • 返回成功的响应

    若是Hystrix命令执行成功,它将以Observable形式返回响应给调用者。根据你在第2步的调用方式不一样,在返回Observablez以前可能会作一些转换。

 

  • execute():经过调用queue()来获得一个Future对象,而后调用get()方法来获取Future中包含的值。
  • queue():将Observable转换成BlockingObservable,在将BlockingObservable转换成一个Future。
  • observe():订阅返回的Observable,而且当即开始执行命令的逻辑,
  • toObservable():返回一个没有改变的Observable,你必须订阅它,它才可以开始执行命令的逻辑。

回路器


下面的图展现了HystrixCommandHystrixObservableCommand如何与HystrixCircuitBroker进行交互。

 

回路器打开和关闭有以下几种状况:

  • 假设回路中的请求知足了必定的阈值(HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()
  • 假设错误发生的百分比超过了设定的错误发生的阈值HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()
  • 回路器状态由CLOSE变换成OPEN
  • 若是回路器打开,全部的请求都会被回路器所熔断。
  • 必定时间以后HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds(),下一个的请求会被经过(处于半打开状态),若是该请求执行失败,回路器会在睡眠窗口期间返回OPEN,若是请求成功,回路器会被置为关闭状态,从新开启1步骤的逻辑。

隔离


Hystrix采用舱壁模式来隔离相互之间的依赖关系,并限制对其中任何一个的并发访问。

 

  • 线程和线程池

    客户端(第三方包、网络调用等)会在单独的线程执行,会与调用的该任务的线程进行隔离,以此来防止调用者调用依赖所消耗的时间过长而阻塞调用者的线程。

    [Hystrix uses separate, per-dependency thread pools as a way of constraining any given dependency so latency on the underlying executions will saturate the available threads only in that pool]

 

您能够在不使用线程池的状况下防止出现故障,可是这要求客户端必须可以作到快速失败(网络链接/读取超时和重试配置),并始终保持良好的执行状态。

Netflix,设计Hystrix,而且选择使用线程和线程池来实现隔离机制,有如下几个缘由:

  • 不少应用会调用多个不一样的后端服务做为依赖。
  • 每一个服务会提供本身的客户端库包。
  • 每一个客户端的库包都会不断的处于变动状态。
  • [Client library logic can change to add new network calls]
  • 每一个客户端库包均可能包含重试、数据解析、缓存等等其余逻辑。
  • 对用户来讲,客户端库每每是“黑盒”的,对于实现细节、网络访问模式。默认配置等都是不透明的。
  • [In several real-world production outages the determination was “oh, something changed and properties should be adjusted” or “the client library changed its behavior.]
  • 即便客户端自己没有改变,服务自己也可能发生变化,这些因素都会影响到服务的性能,从而致使客户端配置失效。
  • 传递依赖能够引入其余客户端库,这些客户端库不是预期的,也许没有正确配置。
  • 大部分的网络访问是同步执行的。
  • 客户端代码中也可能出现失败和延迟,而不只仅是在网络调用中。

 

  • 使用线程池的好处

    经过线程在本身的线程池中隔离的好处是:

    • 该应用程序彻底能够不受失控的客户端库的威胁。即便某一个依赖的线程池已满也不会影响其余依赖的调用。
    • 应用程序能够低风险的接受新的客户端库的数据,若是发生问题,它会与出问题的客户端库所隔离,不会影响其余依赖的任何内容。
    • 当失败的客户端服务恢复时,线程池将会被清除,应用程序也会恢复,而不至于使得咱们整个Tomcat容器出现故障。
    • 若是一个客户端库的配置错误,线程池能够很快的感知这一错误(经过增长错误比例,延迟,超时,拒绝等),并能够在不影响应用程序的功能状况下来处理这些问题(能够经过动态配置来进行实时的改变)。
    • 若是一个客户端服务的性能变差,能够经过改变线程池的指标(错误、延迟、超时、拒绝)来进行属性的调整,而且这些调整能够不影响其余的客户端请求。
    • 除了隔离的优点以外,拥有专用的线程池能够提供内置的请求任务的并发性,能够在同步客户端上构建异步门面。

简而言之,由线程池提供的隔离功能能够使客户端库和子系统性能特性的不断变化和动态组合获得优雅的处理,而不会形成中断。

注意:虽然单独的线程提供了隔离,但您的底层客户端代码也应该有超时和/或响应线程中断,而不能让Hystrix的线程池处于无休止的等待状态。

  • 线程池的缺点

    线程池最主要的缺点就是增长了CPU的计算开销,每一个命令都会在单独的线程池上执行,这样的执行方式会涉及到命令的排队、调度和上下文切换。

    Netflix在设计这个系统时,决定接受这个开销的代价,来换取它所提供的好处,而且认为这个开销是足够小的,不会有重大的成本或者是性能影响。

  • 线程成本

    Hystrix在子线程执行construct()方法和run()方法时会计算延迟,以及计算父线程从端到端的执行总时间。因此,你能够看到Hystrix开销成本包括(线程、度量,日志,断路器等)。

    Netflix API天天使用线程隔离的方式处理10亿多的Hystrix Command任务,每一个API实例都有40多个线程池,每一个线程池都有5-20个线程(大多数设置为10)

    下图显示了一个HystrixCommand在单个API实例上每秒执行60个请求(每一个服务器每秒执行大约350个线程执行总数):

 

在中间位置(或者下线位置)不须要单独的线程池。

在第90线上,单独线程的成本为3ms。

在第99线上,单独的线程花费9ms。可是请注意,线程成本的开销增长远小于单独线程(网络请求)从2跳到28而执行时间从0跳到9的增长。

对于大多数Netflix用例来讲,这样的请求在90%以上的开销被认为是能够接受的,这是为了实现韧性的好处。

对于很是低延迟请求(例如那些主要触发内存缓存的请求),开销可能过高,在这种状况下,能够使用另外一种方法,如信号量,虽然它们不容许超时,提供绝大部分的有点,而不会产生开销。然而,通常来讲,开销是比较小的,以致于Netflix一般更偏向于经过单独的线程来做为隔离实现。

请求合并


您能够使用请求合并器(HystrixCollapser是抽象父代)来提早发送HystrixCommand,经过该合并器您能够将多个请求合并为一个后端依赖项调用。

下面的图展现了两种状况下的线程数和网络链接数,第一张图是不使用请求合并,第二张图是使用请求合并(假定全部链接在短期窗口内是“并发的”,在这种状况下是10ms)。

 

  • 为何使用请求合并

    事情请求合并来减小执行并发HystrixCommand请求所须要的线程数和网络链接数。请求合并以自动方式执行的,不须要代码层面上进行批处理请求的编码。

    • 全局上下文(全部的tomcat线程)

      理想的合并方式是在全局应用程序级别来完成的,以便来自任何用户的任何Tomcat线程的请求均可以一块儿合并。

      例如,若是将HystrixCommand配置为支持任何用户请求获取影片评级的依赖项的批处理,那么当同一个JVM中的任何用户线程发出这样的请求时,Hystrix会将该请求与其余请求一块儿合并添加到同一个JVM中的网络调用。

      请注意,合并器会将一个HystrixRequestContext对象传递给合并的网络调用,为了使其成为一个有效选项,下游系统必须处理这种状况。

    • 用户请求上下文(单个tomcat线程)

      若是将HystrixCommand配置为仅处理单个用户的批处理请求,则Hystrix仅仅会合并单个Tomcat线程的请求。

      例如,若是一个用户想要加载300个影片的标签,Hystrix可以把这300次网络调用合并成一次调用。

    • 对象建模和代码的复杂性

      有时候,当你建立一个对象模型对消费的对象而言是具备逻辑意义的,这与对象的生产者的有效资源利用率不匹配。

      例如,给你300个视频对象,遍历他们,而且调用他们的getSomeAttribute()方法,可是若是简单的调用,可能会致使300次网络调用(可能很快会占满资源)。

      有一些手动的方法能够解决这个问题,好比在用户调用getSomeAttribute()方法以前,要求用户声明他们想要获取哪些视频对象的属性,以便他们均可以被预取。

      或者,您能够分割对象模型,以便用户必须从一个位置获取视频列表,而后从其余位置请求该视频列表的属性。

      这些方法能够会使你的API和对象模型显得笨拙,而且这种方式也不符合心理模式与使用模式(译者:不太懂什么意思)。因为多个开发人员在代码库上工做,可能会致使低级的错误和低效率开发的问题。由于对一个用例的优化能够经过执行另外一个用例和经过代码的新路径来打破。

      经过将合并逻辑移到Hystrix层,无论你如何建立对象模型,调用顺序是怎样的,或者不一样的开发人员是否知道是否完成了优化或者是否完成。

      getSomeAttribute()方法能够放在最适合的地方,并以任何适合使用模式的方式被调用,而且合并器会自动将批量调用放置到时间窗口。

####请求Cache

*

HystrixCommand和HystrixObservableCommand实现能够定义一个缓存键,而后用这个缓存键以并发感知的方式在请求上下文中取消调用(不须要调用依赖便可以获得结果,由于一样的请求结果已经按照缓存键缓存起来了)。

如下是一个涉及HTTP请求生命周期的示例流程,以及在该请求中执行工做的两个线程: 

 

请求cache的好处有:

  • 不一样的代码路径能够执行Hystrix命令,而不用担忧重复的工做。

这在许多开发人员实现不一样功能的大型代码库中尤为有用。

例如,多个请求路径都须要获取用户的Account对象,能够像这样请求:

Account account = new UserGetAccount(accountId).execute();
//or
Observable<Account> accountObservable = new UserGetAccount(accountId).observe();

 

 

Hystrix RequestCache将只执行一次底层的run()方法,执行HystrixCommand的两个线程都会收到相同的数据,尽管实例化了多个不一样的实例。

  • 整个请求的数据检索是一致的。

每次执行该命令时,再也不会返回一个不一样的值(或回退),而是将第一个响应缓存起来,后续相同的请求将会返回缓存的响应。

  • 消除重复的线程执行。

因为请求缓存位于construct()或run()方法调用以前,Hystrix能够在调用线程执行以前取消调用。

若是Hystrix没有实现请求缓存功能,那么每一个命令都须要在构造或者运行方法中实现,这将在一个线程排队并执行以后进行。

https://segmentfault.com/a/1190000012439580

声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅。

一、概念:Hystrix 熔断机制

二、具体内容

所谓的熔断机制和平常生活中见到电路保险丝是很是类似的,当出现了问题以后,保险丝会自动烧断,以保护咱们的电器, 那么若是换到了程序之中呢?

当如今服务的提供方出现了问题以后整个的程序将出现错误的信息显示,而这个时候若是不想出现这样的错误信息,而但愿替换为一个错误时的内容。

一个服务挂了后续的服务跟着不能用了,这就是雪崩效应

 对于熔断技术的实现须要考虑如下几种状况:

 · 出现错误以后能够 fallback 错误的处理信息;

 · 若是要结合 Feign 一块儿使用的时候还须要在 Feign(客户端)进行熔断的配置。

 2.一、Hystrix 基本配置

 一、 【microcloud-provider-dept-hystrix-8001】修改 pom.xml 配置文件,追加 Hystrix 配置类:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>

 二、 【microcloud-provider-dept-hystrix-8001】修改 DeptRest 程序

复制代码
package cn.study.microcloud.rest;

import javax.annotation.Resource;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import cn.study.microcloud.service.IDeptService;
import cn.study.vo.Dept;

@RestController
public class DeptRest {
    @Resource
    private IDeptService deptService;
    @RequestMapping(value = "/dept/get/{id}", method = RequestMethod.GET)
    @HystrixCommand(fallbackMethod="getFallback")    // 若是当前调用的get()方法出现了错误,则执行fallback
    public Object get(@PathVariable("id") long id) {
        Dept vo = this.deptService.get(id) ;    // 接收数据库的查询结果
        if (vo == null) {    // 数据不存在,假设让它抛出个错误
            throw new RuntimeException("部门信息不存在!") ;
        }
        return vo ;
    }
    public Object getFallback(@PathVariable("id") long id) {    // 此时方法的参数 与get()一致
        Dept vo = new Dept() ;
        vo.setDeptno(999999L);
        vo.setDname("【ERROR】Microcloud-Dept-Hystrix");    // 错误的提示
        vo.setLoc("DEPT-Provider");
        return vo ;
    }
    
    
}
复制代码

 一旦 get()方法上抛出了错误的信息,那么就认为该服务有问题,会默认使用“@HystrixCommand”注解之中配置好的 fallbackMethod 调用类中的指定方法,返回相应数据。

 三、 【microcloud-provider-dept-hystrix-8001】在主类之中启动熔断处理

复制代码
package cn.study.microcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableDiscoveryClient
public class Dept_8001_StartSpringCloudApplication {
    public static void main(String[] args) {
        SpringApplication.run(Dept_8001_StartSpringCloudApplication.class, args);
    }
}
复制代码

 如今的处理状况是:服务器出现了错误(但并不表示提供方关闭),那么此时会调用指定方法的 fallback 处理。

 2.二、服务降级(服务回退)

 全部的 RPC 技术里面服务降级是一个最为重要的话题,所谓的降级指的是当服务的提供方不可以使用的时候,程序不会出现异常,而会出现本地的操做调用。

 例如:在每一年年末 12306 都是最繁忙的时候,那么在这个状况会发现有一些神奇的状况:当到了指定的时间你们开始抢票的 时候,若是你不抢,然后查询一些冷门的车次,票有可能查询不出来。由于这个时候会将全部的系统资源给抢票调度了,而其它的 服务因为其暂时不受到过多的关注,这个时候能够考虑将服务降级(服务暂停)。

 服务的降级处理是在客户端实现的,与你的服务器端没有关系。

 一、 【microcloud-service】扩充一个 IDeptService 的失败调用(服务降级)处理:

复制代码
package cn.study.service.fallback;
import java.util.List;
import org.springframework.stereotype.Component;
import cn.study.service.IDeptClientService;
import cn.study.vo.Dept;
import feign.hystrix.FallbackFactory;
@Component
public class IDeptClientServiceFallbackFactory
        implements
            FallbackFactory<IDeptClientService> {

    @Override
    public IDeptClientService create(Throwable cause) {
        return new IDeptClientService() {
            @Override
            public Dept get(long id) {
                Dept vo = new Dept();
                vo.setDeptno(888888L);
                vo.setDname("【ERROR】Feign-Hystrix"); // 错误的提示
                vo.setLoc("Consumer客户端提供");
                return vo;
            }

            @Override
            public List<Dept> list() {
                return null;
            }

            @Override
            public boolean add(Dept dept) {
                return false;
            }
        };
    }

}
复制代码

 二、 【microcloud-service】修改 IDeptClientService 接口,追加本地的 Fallback 配置。

复制代码
package cn.study.service;

import java.util.List;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import cn.study.commons.config.FeignClientConfig;
import cn.study.service.fallback.IDeptClientServiceFallbackFactory;
import cn.study.vo.Dept;
@FeignClient(value = "MICROCLOUD-PROVIDER-DEPT", configuration = FeignClientConfig.class, fallbackFactory = IDeptClientServiceFallbackFactory.class)
public interface IDeptClientService {
    @RequestMapping(method = RequestMethod.GET, value = "/dept/get/{id}")
    public Dept get(@PathVariable("id") long id);
    @RequestMapping(method = RequestMethod.GET, value = "/dept/list")
    public List<Dept> list();
    @RequestMapping(method = RequestMethod.POST, value = "/dept/add")
    public boolean add(Dept dept);
}
复制代码

 此时当服务不可用的时候就会执行“IDeptClientServiceFallbackFactory”类中返回的 IDeptClientService 接口的匿名对象信息。

 三、 【microcloud-consumer-hystrix】修改 application.yml 配置文件,追加 feign 配置启用。

feign:
  hystrix: 
    enabled: true 

 四、 【microcloud-consumer-hystrix】修改程序启动主类:

复制代码
package cn.study.microcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableEurekaClient
@ComponentScan("cn.study.service,cn.study.microcloud")
@EnableFeignClients(basePackages={"cn.study.service"})
public class Consumer_80_StartSpringCloudApplication {
    public static void main(String[] args) {
        SpringApplication.run(Consumer_80_StartSpringCloudApplication.class,
                args);
    }
}
复制代码

 当追加上了“@ComponentScan("cn.mldn.service")”注解以后才能够进行包的扫描配置。

 此时即便服务端没法继续提供服务了,因为存在有服务降级机制,也会保证服务不可用时能够获得一些固定的提示信息。

 2.三、HystrixDashboard服务监控

 在 Hystrix 里面提供有一种监控的功能,那么这个功能就是“Hystrix Dashboard”,能够利用它来进行总体微服务的监控操做。

 一、 首先为了方便监控,将创建一个新的监控项目:microcloud-consumer-hystrix-dashboard;

 二、 【microcloud-consumer-hystrix-dashboard】修改项目中的 pom.xml 配置文件:

复制代码
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>
复制代码

 三、 【microcloud-provider-*】全部的服务提供者之中都必定要提供有监控服务依赖库:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

 四、 【microcloud-consumer-hystrix-dashboard】修改 application.yml 配置文件,主要进行端口的配置:

server:
  port: 9001

 五、 【microcloud-consumer-hystrix-dashboard】建立一个监控的主类:

复制代码
package cn.study.microcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardApplication_9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication_9001.class, args);
    }
}
复制代码

 六、 修改 hosts 主机文件,增长主机列表:

127.0.0.1 dashboard.com

 服务运行地址:http://dashboard.com:9001/hystrix;

 

 七、 获得 microcloud-provider-dept 的监控信息:http://studyjava:hello@dept-8001.com:8001/hystrix.stream;

 · 若是此时要想获取监控信息必须去运行微服务;

 八、 将以前的监控的路径http://studyjava:hello@dept-8001.com:8001/hystrix.stream填写到以前启动好的 dashboard 程序页面之中;

 

监控效果以下图所示:

2.四、Turbine 聚合监控

 HystrixDashboard 主要的功能是能够针对于某一项微服务进行监控,可是若是说如今有许多的微服务须要进行总体的监控,那 么这种状况下就能够利用 turbine 技术来实现。

 一、 下面准备出一个新的微服务:Company,这个微服务不打算使用 SpringSecurity 安全处理以及 Mybatis 数据库链接,只是作一 个简单的数据信息。经过一个已有的 microcloud-provider-hystrix-8001 复制一个新的项目:microcloud-provider-company-8101;

 二、 【microcloud-provider-company-8101】修改项目中的 pom.xml 配置文件,将与安全有关的依赖包删除掉以及与数据库链接池、MyBatis 的相关的程序类或接口所有删除掉,只保留有用的包;

复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.study</groupId>
            <artifactId>microcloud-api</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>
    </dependencies>
复制代码

 三、 【microcloud-api】追加一个新的 VO 类:Company。

复制代码
package cn.study.vo;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Company implements Serializable {
    private String title ;
    private String note ;
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getNote() {
        return note;
    }
    public void setNote(String note) {
        this.note = note;
    }
    @Override
    public String toString() {
        return "Company [title=" + title + ", note=" + note + "]";
    }
}
复制代码

 四、 【microcloud-provider-company-8101】创建一个新的微服务的程序类:CompanyRest

复制代码
package cn.study.microcloud.rest;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

import cn.study.vo.Company;

@RestController
public class CompanyRest {
    @RequestMapping(value = "/company/get/{title}", method = RequestMethod.GET)
    @HystrixCommand    // 若是须要进行性能监控,那么必需要有此注解
    public Object get(@PathVariable("title") String title) {
        Company vo = new Company() ;
        vo.setTitle(title);
        vo.setNote("www.study.cn");
        return vo ;
    }
}
复制代码

 五、 【microcloud-provider-company-8101】修改程序的启动主类:

复制代码
package cn.study.microcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
@EnableDiscoveryClient
public class Company_8101_StartSpringCloudApplication {
    public static void main(String[] args) {
        SpringApplication.run(Company_8101_StartSpringCloudApplication.class, args);
    }
}
复制代码

 六、 【microcloud-provider-company-8101】修改 application.yml 配置文件:

复制代码
server:
  port: 8101
eureka: 
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://edmin:studyjava@eureka-7001.com:7001/eureka,http://edmin:studyjava@eureka-7002.com:7002/eureka,http://edmin:studyjava@eureka-7003.com:7003/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 若是如今超过了5秒的间隔(默认是90秒)
    instance-id: dept-8001.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址
info: 
  app.name: study-microcloud
  company.name: www.study.cn
  build.artifactId: $project.artifactId$
  build.version: $project.verson$
spring: 
  application:
    name: microcloud-provider-company
复制代码

 七、 【microcloud-provider-company-8101】启动微服务,随后取得监控信息:

 · 在 hosts 配置文件之中追加有一个映射路径:

127.0.0.1 company-8101.com

· 访问地址:http://company-8101.com:8101/company/get/hello;

· hystrix 监控地址:http://company-8101.com:8101/hystrix.stream;

八、 如 果 要 想 实 现 trubine 的 配 置 , 则须要创建一个 turbine项目模块 , 这个项目能够直接经过以前的 microcloud-consumer-hystrix-dashboard 模块进行复制为“microcloud-consumer-turbine”模块;

九、 【microcloud-consumer-turbine】修改 pom.xml 配置文件,追加 turbine 的依赖程序包:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-turbine</artifactId>
        </dependency>

十、 【microcloud-consumer-turbine】修改 application.yml 配置文件:

复制代码
server:
  port: 9101  # turbine的监听端口为9101
eureka: 
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://edmin:studyjava@eureka-7001.com:7001/eureka,http://edmin:studyjava@eureka-7002.com:7002/eureka,http://edmin:studyjava@eureka-7003.com:7003/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒)
    lease-expiration-duration-in-seconds: 5 # 若是如今超过了5秒的间隔(默认是90秒)
    instance-id: dept-8001.com  # 在信息列表时显示主机名称
    prefer-ip-address: true     # 访问的路径变为IP地址
turbine: 
  app-config: MICROCLOUD-PROVIDER-COMPANY,MICROCLOUD-PROVIDER-DEPT  # 定义全部要监控的微服务信息
  cluster-name-expression: new String("default")  # 设置监控的表达式,经过此表达式表示要获取监控信息名称
复制代码

十一、 【microcloud-consumer-turbine】创建一个 turbine 的使用主类信息

复制代码
package cn.study.microcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.cloud.netflix.turbine.EnableTurbine;
@SpringBootApplication
@EnableHystrixDashboard
@EnableTurbine
public class TurbineApplication_9101 {
    public static void main(String[] args) {
        SpringApplication.run(TurbineApplication_9101.class, args);
    }
}
复制代码

十二、 【microcloud-consumer-hystrix-dashboard】运行 hystrix-dashboard 监控程序;

1三、 【microcloud-consumer-turbine】运行 trubine 聚合监控程序;

· 可是在正常启动 trubine 的时候出现了如下的错误提示信息,这是由于没有对有安全认证的微服务MICROCLOUD-PROVIDER-DEPT进行安全认证

· 修改 hosts 配置文件,追加一个映射路径:

127.0.0.1 turbine.com

 · trubine 访问路径:http://turbine.com:9101/turbine.stream

 1四、 运行 HystrixDashboard 监控程序:http://dashboard.com:9001/hystrix.stream;

 · 在监控的位置上填写以前设置好的 turbine 的访问地址看到的效果以下:

 

 1五、 【microcloud-security】若是如今须要 turbine 进行加密的微服务的访问操做,只可以采起一种折衷方案,就是要去修改整个项目中的安全策略,追加 WEB 安全策略配置:

复制代码
package cn.study.microcloud.config;

import javax.annotation.Resource;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/hystrix.stream","/turbine.stream") ;
    }
    
    @Resource
    public void configGlobal(AuthenticationManagerBuilder auth)
            throws Exception {
        auth.inMemoryAuthentication().withUser("studyjava").password("hello")
                .roles("USER").and().withUser("admin").password("hello")
                .roles("USER", "ADMIN");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表示全部的访问都必须进行认证处理后才能够正常进行
        http.httpBasic().and().authorizeRequests().anyRequest()
                .fullyAuthenticated();
        // 全部的Rest服务必定要设置为无状态,以提高操做性能
        http.sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}
复制代码

 如今全部的安全策略会自动抛开以上的两个访问路径,这种是基于 Bean 配置,若是要是你如今基于的是 application.yml 文件的配置,则就必须修改 application.yml 配置文件,追加以下内容:

security:
  ignored:
  - /hystrix.stream
  - /turbine.stream

 这个时候若是启动以后没有出现任何的错误提示,那么就表示如今已经能够绕过了 Security 的配置而直接进行服务的访问了。

 

https://www.cnblogs.com/leeSmall/p/8847652.html
 
 

Hystrix中止开发,咱们该何去何从?

是的,Hystrix中止开发了。官方的新闻以下:

 

考虑到以前Netflix宣布Eureka 2.0孵化失败时,被业界过分消费(关于Eureka 2.x,别再人云亦云了!),为了防止再度出现相似现象,笔者编写了这篇文章。

我相信看到这篇文章,你们无非会思考几个问题:

  • 若是Hystrix还能不能继续用于生产?
  • Spring Cloud生态中是否有替代实现?

下面依次展开:

就笔者经验来看,Hystrix是比较稳定的,而且Hystrix只是中止开发新的版本,并非彻底中止维护,Bug什么的依然会维护的。所以短时间内,Hystrix依然是继续使用的。

但从长远来看,Hystrix总会达到它的生命周期,那么Spring Cloud生态中是否有替代产品呢?

答案显然是有。

Alibaba Sentinel

Sentinel 是阿里巴巴开源的一款断路器实现,目前在Spring Cloud的孵化器项目Spring Cloud Alibaba中,预计Spring Cloud H系列中能够孵化完成。

尽管Sentinel还没有在Spring Cloud项目中孵化完成,但Sentinel自己在阿里内部已经被大规模采用,很是稳定。所以能够做为一个较好的替代品。

Resilience4J

Resilicence4J 在今年的7月进入笔者视野,小玩了一下,以为很是轻量、简单,而且文档很是清晰、丰富。我的比较看好,这也是Hystrix官方推荐的替代产品。

不只如此,Resilicence4j还原生支持Spring Boot 1.x/2.x,并且监控也不像Hystrix同样弄Dashboard/Hystrix等一堆轮子,而是支持和Micrometer(Pivotal开源的监控门面,Spring Boot 2.x中的Actuator就是基于Micrometer的)、prometheus(开源监控系统,来自谷歌的论文)、以及Dropwizard metrics(Spring Boot曾经的模仿对象,相似于Spring Boot)进行整合。

笔者特别看重Resilience4J和micrometer整合的能力,这意味着:若是你用Spring Boot 2.x并在项目中引入Resilience4J,那么监控数据和Actuator天生就是打通的!你再也不须要一个专属的、相似于Hystrix Dashboard的东西去监控断路器。

预报

考虑到目前国内Resilience4J的文档还比较少,笔者准备近期分享系列博客,敬请期待!

 

原文:http://www.itmuch.com/spring-cloud-sum/hystrix-no-longer/ ,转载请说明出处

相关文章
相关标签/搜索