(五)Spring Cloud Hystrix 服务熔断

在分布式系统下,微服务之间不可避免地会发生相互调用,但是没有一个系统能够保证自身运行的绝对正确。微服务在调用过程中,很可能会面临依赖服务失效的问题,这些问题的发生有很多原因,有可能是因为微服务之间的网络通信出现较大的延迟,或者是被调用的微服务发生了调用异常,还有可能是因为依赖的微服务负载过大无法及时响应请求等 。总而言之服务若在调用时出现故障会导致连 锁效应,也就是可能会让整个系统变得不可用,这种情况我们称之为服务雪崩效应。

Hystrix 是 Netflix 针对微服务分布式系统采用的熔断保护中间件,相当于电路中的保险丝。它能够在依赖服务失效的情况下,通过隔离系统依赖服务的方式,防止服务级联失败;同时 Hystrix 提供失败回滚机制,使系统能够更快地从异常中恢复。这样可以阻止故障的连锁效应,能够让接口调用快速失败并迅速恢复正常,或者回退并优雅降级。

spring-cloud-netflix-hystrix 对 Hystrix 进行封装和适配,使 Hystrix 能够更好地运行于 Spring Cloud 环境中,为微服务间的调用提供强有力的容错机制。

Spring Cloud Hystrix 简单使用

添加 Hystrix 依赖。

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

在启动类上添加 @EnableHystrix 或者 @EnableCircuitBreaker 注解

@EnableHystrix 中包含了 @EnableCircuitBreaker

编写一个接口,在上面增加一个 @HystrixCommand 注解,用于指定调用延迟或失败时调用的方法。

@GetMapping("hello")
@HystrixCommand(fallbackMethod = "defalutHello")
public String hello(String name) throws Exception {
    if(name == null){
        throw new Exception();
    }
    return "hello";
}

public String defalutHello(String name){
    return "fail hello";
}

当调用失败触发熔断是就是调用 defalutHello 方法来回退具体的内容。

Feign 整合 Hystrix

OpenFeign 中自带 Hystrix,但是默认没有打开,需要添加如下配置:

feign:
    hystrix:
        enabled: true

fallback 方式

在 Feign 的客户端类上的@FeignClient 注解中指定 fallback 进行回退。

@FeignClient(value = "order-service", fallback = OrderClientFallback.class)
public interface OrderClient {
    @GetMapping("/order/add")
    String addOrder();
}
@Component
public class OrderClientFallback implements OrderClient{
    @Override
    public String addOrder() {
        return "fail";
    }
}

FallbackFactory 方式

通过 fallback 可以实现服务不可用时回退的功能,但如果想知道触发回退的原因,可以使用 FallbackFactory 来实现回退功能。

@FeignClient(value = "order-service", fallbackFactory = OrderClientFallbackFactory.class)
public interface OrderClient {

    @GetMapping("/order/add")
    String addOrder();

}
@Component
public class OrderClientFallbackFactory implements FallbackFactory<OrderClient> {
    @Override
    public OrderClient create(Throwable cause) {
        cause.printStackTrace();
        return new OrderClient() {
            @Override
            public String addOrder() {
                return "fail";
            }
        };
    }
}

Hystrix 监控

在微服务架构中,Hystrix 除了实现容错外,还提供了实时监控功能。在服务调用时 Hystrix 会实时累积关于 HystrixCommand 的执行信息,比如每秒的请求数、成功数等。

更多的指标信息请查看官方文档:https://github.com/Netflix/Hystrix/wiki/Metrics-and-Monitoring

Hystrix 监控需要两个必备条件:

  • 必须有 Actuator 的依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  • 必须有 Hystrix 的依赖,切必须添加 @EnableHystrix 开启 Hystrix
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

放开 hystrix 监控数据访问端点:

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

访问端点地址:http://localhost:9010/actuator/hystrix.stream
可以看到一直在输出“ ping : ”,出现这种情况是因为还没有数据,等 HystrixCommand 执行了之后就可以看到具体数据了。

整合 Dashboard 查看监控数据

Hystrix 提供了监控的功能,可以通过 hystrix.stream 端点来获取监控 数据 ,但是这些数据是以字符串的形式展现的,实际使用中不方便查看。我们可以借助 hystrix-dashboard 对监控进行图形化展示。

新建一个项目,然后添加以下依赖:

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

在启动类中添加如下注解:

@SpringBootApplication
@EnableHystrixDashboard

在属性配置文件中只需要配置服务名称和服务端口:

spring:
  application:
    name: hystrix-monitor-demo
server:
  port: 8081

然后启动服务,访问 http://localhost:8081/hystrix,就可以看到 dashboard 页面,如下图:

image.png

在页面上有三个地方可以填写:

我们把前面创建的 hystrix 应用的监控地址填入,然后点击监控

image.png
  • 圆圈: 代表流量的大小和流量的健康,有绿色,黄色,橙色,红色,通过这些颜色的标识,可以快速发现故障,具体的实例,请求压力等。
  • 曲线:代表 2 分钟内流量的变化,可以根据它发现请求的浮动趋势。
  • 左边的7个数字:
    • 绿色:请求成功数
    • 黄色:超时的请求数
    • 深蓝色:熔断数
    • 紫色:线程池拒绝数
    • 天蓝色:错误的请求数
    • 红色:失败的请求数
    • 灰色:最近 10 秒内错误的比率
  • Host&Cluster:代表机器和集群的请求频率
  • Circuit:断路器状态,open/closed
  • Hosts&Median&Mean: 集群下的报告,百分位延迟数。
  • Thread Pools: 线程池的指标,核心线程池指标,队列大小等。

Turbine 聚合 Hystrix

上面尝试了单个实例的 Hystrix Dashboard,但在整个系统和集群的情况下不是特别有用,所以需要一种方式来聚合整个集群下的监控状况,Turbine 就是来聚合所有相关的 hystrix.stream 流的方案, 然后在 Hystrix Dashboard 中显示。

继续在上面 hystrix-dashboard 工程上改造。首先添加 Turbine 依赖。

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

添加配置

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
turbine:
    ## 需要聚合的服务名称,多个逗号隔开
  app-config: feign-demo
  ## 需要聚合的集群名称 
  cluster-name-expression: "'default'"

clusterNameExpression 默认表达式appName,此时:turbine.aggregator.clusterConfig 需要配置想要监控的应用名称(必须大写)
当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default
当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC,则需要配置,同时turbine.aggregator.clusterConfig: ABC

修改启动类添加 @EnableTurbine 和 @EnableDiscoveryClient 注解

@SpringBootApplication
@EnableHystrixDashboard
@EnableTurbine
@EnableDiscoveryClient
public class HystrixDashboardApplication {

    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class,args);
    }

}

这样咱们就可以使用 http://localhost:8081/turbine.stream 来访问集群的监控数据了。 Turbine 会通过 Eureka 中查找的服务 homePageUrl 加上 hystrix.stream 来获取服务的监控数据。

context-path 导致监控失败

如果被监控的服务中设置了 context-path ,则会导致 Turbine 无法获取监控数据。
这个时候需要在 Turbine 中指定 turbine.instanceUrlSuffix 来解决这个问题:

turbine:
    instanceUrlSuffix: /api/actuator/hystrix.stream

上面这种方式是全局配置,会有一个问题,就是一般我们在使用中会用一个集群去监控多个服务,如果每个服务的 context-path 都不一样,这个时候有一些就会出问题,那么就需要对每个服务做一个集群,然后配置集群对应的。

修改需要监控的服务设置集群名称:

eureka:
  instance:
    metadata-map:
      cluster: feign-demo

修改 turbine 配置

turbine:
  ## 需要聚合的服务名称,多个逗号隔开
  app-config: feign-demo
  ## 用于指定集群名称,当服务数量非常多的时候,可以启动多个
  cluster-name-expression: metadata['cluster']
  aggregator:
    ## 多个逗号隔开
    cluster-config: feign-demo
  instanceUrlSuffix:
    feign-demo: /api/actuator/hystrix.stream

Hystrix 异常机制和处理

Hystrix 的异常处理中,有 5 种出错的情况下会被 fallback 所截获,从而触发 fallback。

  • FAILURE:执行失败,抛出异常
  • TIMEOUT:执行超时
  • SHORT CIRCUITED:断路器打开
  • THREAD POOL REJECTED:线程池拒绝
  • SEMAPHORE REJECTED:信号量拒绝 。

有一种类型的异常是不会触发 fallback 且不会被计数进入熔断的,它是 BAD_REQUEST, 会抛出 HystrixBadRequestException,这种异常一般对应的是由非法参数或者一些非系统异常引起的,对于这类异常可以根据响应创建对应的异常进行异常封装或者直接处理

Hystrix 配置说明

properties 配置

# hystrix.command.default和hystrix.threadpool.default中的default为默认CommandKey,CommandKey默认值为服务方法名。
# 在properties配置中配置格式混乱,如果需要为每个方法设置不同的容错规则,建议使用yml文件配置。


# Execution相关的属性的配置:
# 隔离策略,默认是Thread, 可选Thread|Semaphore
hystrix.command.default.execution.isolation.strategy=THREAD
#命令执行超时时间,默认1000ms,只在线程池隔离中有效。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
# 执行是否启用超时,默认启用true,只在线程池隔离中有效。
hystrix.command.default.execution.timeout.enabled=true
# 发生超时是是否中断,默认true,只在线程池隔离中有效。
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
# 最大并发请求数,默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。# 理论上选择semaphore的原则和选择thread一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。# semaphore应该占整个容器(tomcat)的线程池的一小部分。
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10
# 如果并发数达到该设置值,请求会被拒绝和抛出异常并且fallback不会被调用。默认10。# 只在信号量隔离策略中有效,建议设置大一些,这样并发数达到execution最大请求数时,会直接调用fallback,而并发数达到fallback最大请求数时会被拒绝和抛出异常。
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=10


# ThreadPool 相关参数
# 并发执行的最大线程数,默认10
hystrix.threadpool.default.coreSize=10
# BlockingQueue的最大队列数,当设为-1,会使用SynchronousQueue,值为正时使用LinkedBlcokingQueue。# 该设置只会在初始化时有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默认-1。
hystrix.threadpool.default.maxQueueSize=-1
# 即使maxQueueSize没有达到,达到queueSizeRejectionThreshold该值后,请求也会被拒绝。
hystrix.threadpool.default.queueSizeRejectionThreshold=20
# 线程存活时间,单位是分钟。默认值为1。
hystrix.threadpool.default.keepAliveTimeMinutes=1


# Fallback相关的属性 
# 当执行失败或者请求被拒绝,是否会尝试调用fallback方法 。默认true hystrix.command.default.fallback.enabled=true # Circuit Breaker相关的属性 # 是否开启熔断器。默认true hystrix.command.default.circuitBreaker.enabled=true # 一个rolling window内最小的请求数。如果设为20,那么当一个rolling window的时间内(比如说1个rolling window是10毫秒)收到19个请求# 即使19个请求都失败,也不会触发circuit break。默认20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
# 触发短路的时间值,当该值设为5000时,则当触发circuit break后的5000毫秒内都会拒绝远程服务调用,也就是5000毫秒后才会重试远程服务调用。默认5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 错误比率阀值,如果错误率>=该值,circuit会被打开,并短路所有请求触发fallback。默认50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 强制打开熔断器
hystrix.command.default.circuitBreaker.forceOpen=false
# 强制关闭熔断器
hystrix.command.default.circuitBreaker.forceClosed=false

yaml 配置

hystrix:
  command:
    # default代表全部服务配置,如果为某个具体服务定制配置,使用:'服务接口名#方法名(参数类型列表)'的方式来定义。
    # 如:'FirstClientFeignService#test(int)'。如果接口名称在应用中唯一,可以只写simpleName。
    # 如果接口名称在应用中不唯一,需要写fullName(包名.类名)
    "FirstClientFeignService#testFeign()":
      fallback:
        enabled: true
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容