这是SpringCloud实战系列中第4篇文章,了解前面第两篇文章更有助于更好理解本文内容:

①SpringCloud 实战:引入Eureka组件,完善服务治理

②SpringCloud 实战:引入Feign组件,发起服务间调用

③SpringCloud 实战:使用 Ribbon 客户端负载均衡

简介

Hystrix 是一个延迟和容错库,旨在隔离对远程系统、服务和第三方库的访问点,停止级联故障,并在故障不可避免的复杂分布式系统中实现恢复能力。

服务雪崩

在分布式微服务的架构体系下,一般都会存在多层级服务服务的调用链,当链路中的某个服务发生异常,最后导致整个系统不可用,这种现象称为服务雪崩效应。

如上图所示,从最开始的整个系统正常状态,到单个服务出现异常,再到多个服务出现异常,到最后整个系统可不用,整个过程就是服务雪崩效应。如果在单个服务出现异常的时候,我们能及时发现、预防、处理,也就不会出现级联效果导致整个系统不可用。Hystrix 就是来保证上面的情况发生时能停止级联故障,保证系统稳定运行的。

Hystrix遵循的设计原则

  1. 避免线程耗尽

    由于被调用方出现问题,调用方无法及时获取响应结果,而一直在发送请求,最终会耗尽所有线程的资源。
  2. 快速失败

    当被调用方出现问题后,调用方发起的请求可以快速失败并返回,这样就不用一直阻塞住,同时也释放了线程资源。
  3. 支持回退

    发起的请求在返回失败后,我们可以让用户有回退的逻辑,比如获取备用数据,从缓存中获取数据,记录日志等操作。
  4. 资源隔离

    当你的服务依赖了 A、B、C 三个服务,当只有 C 服务出问题的时候,如果没做隔离,最终也会发生雪崩效应,导致整个服务不可用,如果我们进行了资源隔离,A、B、C 三个服务都是相互隔离的,即使 C 服务出问题了,那也不影响 A 和 B。这其实就跟不要把所有的鸡蛋放进一个篮子里是一样的道理。
  5. 近实时监控

    它能帮助我们了解整个系统目前的状态,有哪些服务有问题,当前流量有多大,出问题后及时告警等。

Hystrix 两种隔离方式

Hystrix 支持线程池和信号量两种隔离方式,默认使用的线程池隔离。

线程池隔离是当用户请求到 A 服务后,A 服务需要调用其他服务,这个时候可以为不同的服务创建独立的线程池,假如 A 需要调用 B 和 C,那么可以创建 2 个独立的线程池,将调用 B 服务的线程丢入到一个线程池,将调用 C 服务的线程丢入到另一个线程池,这样就起到隔离效果,就算其中某个线程池请求满了,无法处理请求了,对另一个线程池也没有影响。

信号量隔离就比较简单了,信号量就是一个计数器,比如初始化值是 100,那么每次请求过来的时候就会减 1,当信号量计数为 0 的时候,请求就会被拒绝,等之前的请求处理完成后,信号量会加 1,同时也起到了限流的作用,这就是信号量隔离,信号量隔离是在请求主线程中执行的。

线程池隔离的特点是 Command 运行在独立的线程池中,可以支持超时,是单独的线程,支持异步。信号量隔离运行在调用的主线程中,不支持超时,只能同步调用。

Hystrix 实战

引入Hystrix 依赖

  1. 引入相关依赖

    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
  2. ribbon-client服务的启动类上添加注解@EnableHystrix

    这时候服务已经可以启动成功了

Hystrix 的三种使用方式

1.@HystrixCommand 注解方式

HystrixCommand 注解作用于方法上,哪个方法想要使用 Hystrix 来进行保护,就在这个方法上增加 HystrixCommand 注解。

比如在我们的queryPort方法上添加@HystrixCommand注解:

@HystrixCommand(commandKey = "queryPort")
@GetMapping("queryPort")
public String queryPort(){
return providerFeign.queryPort();
}

其中commandKey不指定的话,会默认使用方法名,这里也是queryPort;

@HystrixCommand 有很多默认的配置,比如超时时间,隔离方式等;我们可以手动指定配置信息有比如 commandKey、groupKey、fallbackMethod 等。

配置回退方法fallbackMethod

使用@HystrixCommand 注解方式配置回退方法,需要将回退方法定义在HystrixCommand所在的类中,且回退方法的签名与调用的方法签名(入参,返回值)应该保持一致,比如:

private String queryPortFallBack(){
return "sorry queryPort,jinglingwang.cn no back!";
} //调用方法改造
@HystrixCommand(commandKey = "queryPort",fallbackMethod = "queryPortFallBack")

然后我们把eureka-provider服务停掉或者故意超时,访问接口会出现如下图所示的结果:

我们也可以结合@HystrixProperty注解来丰富我们的配置

@HystrixCommand(commandKey = "queryPort",commandProperties ={
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000"),//超时时间,默认1000,即1秒
@HystrixProperty(name = "execution.isolation.strategy",value = "SEMAPHORE"),//信号量隔离级别
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests",value = "50") //信号量模式下,最大请求并发数,默认10
},fallbackMethod = "queryPortFallBack")
@GetMapping("queryPort")
public String queryPort(){
return providerFeign.queryPort();
}

上面的一些配置信息我们还可以配置到配置文件中,效果是一样的:

# queryPort 是@HystrixCommand注解里面的commandKey,默认值是1000,也就是1秒;在HystrixCommandProperties类可以看到
# 隔离方式,SEMAPHORE:信号量隔离,THREAD:线程隔离(默认值)
hystrix.command.queryPort.execution.isolation.strategy = SEMAPHORE
# 信号量模式下,最大请求并发数,默认10
hystrix.command.queryPort.execution.isolation.semaphore.maxConcurrentRequests = 50
# 超时时间
hystrix.command.queryPort.execution.isolation.thread.timeoutInMilliseconds = 3000

下面的代码展示了线程隔离级别下的配置示例:

@HystrixCommand(commandKey = "queryTempPort",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "101"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
}
,fallbackMethod = "queryTempPortFallBack")
@GetMapping("queryTempPort")
public String queryTempPort(){
return providerTempFeign.queryPort();
}

我们也可以使用@DefaultProperties注解来配置默认属性;

@DefaultProperties是作用在类上面的,可以配置一些比如groupKey、threadPoolKey、commandProperties、threadPoolProperties、ignoreExceptions和raiseHystrixExceptions等属性。方法级别的@HystrixCommand命令中单独指定了的属性会覆盖默认的属性,比如:

@RestController
@DefaultProperties(groupKey = "DefaultGroupKey")
public class RibbonController{
... @HystrixCommand(commandKey = "queryTempPort",groupKey="eureka-provider-temp",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "30"),
@HystrixProperty(name = "maxQueueSize", value = "101"),
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2"),
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "15"),
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "12"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "1440")
}
,fallbackMethod = "queryTempPortFallBack")
@GetMapping("queryTempPort")
public String queryTempPort(){
return providerTempFeign.queryPort();
}
}

2.Feign 整合 Hystrix

开启Feign对Hystrix的支持

在配置文件添加如下配置

# 如果为true,则将使用Hystrix断路器包装OpenFeign客户端,默认是false
feign.hystrix.enabled=true

配置fallback

  1. 为Feign配置回退方法,将fallback属性设置成回退的类名,例如:

    @Component
    public class ProviderTempFeignFallback implements ProviderTempFeign{ @Override
    public String queryPort(){
    return "sorry ProviderTempFeign, jinglingwang.cn no back!";
    }
    } @FeignClient(value = "eureka-provider-temp",fallback = ProviderTempFeignFallback.class)
    public interface ProviderTempFeign{ @RequestMapping("/queryPort")
    String queryPort();
    }
  2. 我们保留上面的@HystrixCommand注解,然后启动项目,把eureka-provider项目的接口加一个断点,保证接口会超时。同时配置有两个fallback时,发现最后生效的是@HystrixCommand注解配置的fallback,说明@HystrixCommand注解的优先级要高一些,返回结果如图:



    然后我们把@HystrixCommand注解注释掉,再重启,成功执行了Feign配置的fallback,效果如图:

fallback返回失败的原因

如果需要访问导致失败回退的原因,可以使用@FeignClient内的fallbackFactory属性。

@Component
public class ProviderFeignFallbackFactory implements FallbackFactory<ProviderFeign>{ @Override
public ProviderFeign create(Throwable cause){
return new ProviderFeign(){
@Override
public String queryPort(){
return "sorry ProviderFeignFallbackFactory, jinglingwang.cn no back! why? ==>" + cause.getCause();
}
};
}
} @FeignClient(value = "eureka-provider",fallbackFactory = ProviderFeignFallbackFactory.class)
public interface ProviderFeign{
/**
* 调用服务提供方,其中会返回服务提供者的端口信息
* @return jinglingwang.cn
*/
@RequestMapping("/queryPort")
String queryPort(); }

3.网关中使用Hystrix

网关中使用Hystrix等到了整合网关的时候再细讲。

hystrix配置总结

  1. 默认配置是全局有效的

    # 配置 Hystrix 默认的配置
    # To set thread isolation to SEMAPHORE
    hystrix.command.default.execution.isolation.strategy: SEMAPHORE
    hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 3000
    hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests: 40
  2. 单独为Feign Client 来指定超时时间

    # 单独为 ProviderFeign 配置
    hystrix.command.ProviderFeign.execution.isolation.strategy = SEMAPHORE
    # 超时时间
    hystrix.command.ProviderFeign.execution.isolation.thread.timeoutInMilliseconds = 5000
    # 最大请求并发数,默认10
    hystrix.command.ProviderFeign.execution.isolation.semaphore.maxConcurrentRequests: 200
  3. 单独为ProviderTempFeign类的queryPort()方法进行配置

    # 单独为ProviderTempFeign类的queryPort()方法配置
    hystrix.command.ProviderTempFeign#queryPort().execution.isolation.strategy = THREAD
    # 超时时间
    hystrix.command.ProviderTempFeign#queryPort().execution.isolation.thread.timeoutInMilliseconds = 5000
  4. 使用 @HystrixCommand 注解配置

    具体做法可以参考上面的示例代码

Hystrix的配置项有很多,其他属性的配置key可以参考HystrixCommandProperties类。

如何合理的配置Hystrix和Ribbon超时时间

Hystrix 的超时时间是和Ribbon有关联的,如果配置的不对,可能会出现莫名其妙的问题。

在Hystrix源码里面是建议hystrixTimeout应该大于等于ribbonTimeout的时间的,否则会输出一句警告:

LOGGER.warn("The Hystrix timeout of " + hystrixTimeout + "ms for the command " + commandKey +
" is set lower than the combination of the Ribbon read and connect timeout, " + ribbonTimeout + "ms.");

而在取ribbonTimeout配置值的时候,是有一个计算公式的:

ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);

假如我们Ribbon的超时时间配置如下:

#读超时
ribbon.ReadTimeout=3000
#连接超时
ribbon.ConnectTimeout=3000
#同一台实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetries=0
#重试负载均衡其他的实例最大重试次数,不包括首次调用
ribbon.MaxAutoRetriesNextServer=1

将上面的值代入到公式计算,得到结果:ribbonTimeout=(3000+3000)(0+1)(1+1),结果为12000,也就是说Hystrix 的超时时间建议配置值要大于等于12000,也就是12秒。

Hystrix Dashboard

引入dashboard依赖:

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

在启动类上加入注解@EnableHystrixDashboard

然后启动项目,访问http://localhost:7071/hystrix,我们可以看到如下页面:

但是这时候并不能直接使用,需要对项目进行监控,首先要有对应的 Stream 地址,Stream 产生数据源,然后将仪表板指向Hystrix客户端应用程序中的单个实例/hystrix.stream端点。

我们在被ribbon-client项目中加入 spring-boot-starter-actuator 依赖,只有加入了 actuator 才能暴露出 hystrix.stream 端点。

然后再配置文件添加如下配置:

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

启动项目,访问http://localhost:7071/actuator/hystrix.stream接口,你会发现页面一直在显示ping;

然后把该地址配置到上面的页面中,点击monitor,OK,等待loading。

然后我们随便访问一些接口,就可以看到监控内容了。

Hystrix 总结

  1. Hystrix 支持@HystrixCommand 命令和配置文件两种方式进行配置
  2. Hystrix 支持两种隔离级别,在网关中建议使用信号量的方式,能起到一定限流的作用
  3. Hystrix 的线程池隔离级别可以为每个client分别配置线程池,起到资源隔离的作用
  4. Hystrix 的线程池隔离级别中使用 ThreadLocal 时数据可能会丢失,需要单独处理
  5. Hystrix 的fallback我们可以用来记录日志或者进行相应的业务告警
  6. Hystrix 超时时间的合理计算和ribbon的配置有关系,否则可能出现莫名其妙的问题

代码示例:Github ribbon client

④SpringCloud 实战:引入Hystrix组件,分布式系统容错的更多相关文章

  1. ⑤SpringCloud 实战:引入Zuul组件,开启网关路由

    这是SpringCloud实战系列中第4篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...

  2. ⑥SpringCloud 实战:引入gateway组件,开启网关路由功能

    这是SpringCloud实战系列中第4篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...

  3. ⑦SpringCloud 实战:引入Sleuth组件,完善服务链路跟踪

    这是SpringCloud实战系列中第7篇文章,了解前面第两篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 ②SpringCloud 实战:引入F ...

  4. 服务容错保护断路器Hystrix之一:入门示例介绍(springcloud引入Hystrix的两种方式)

    限流知识<高可用服务设计之二:Rate limiting 限流与降级> 在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元间通过服务注册与订阅的方式互相依赖.由于每个单元都在不同的 ...

  5. ②SpringCloud 实战:引入Feign组件,完善服务间调用

    这是SpringCloud实战系列中第二篇文章,了解前面第一篇文章更有助于更好理解本文内容: ①SpringCloud 实战:引入Eureka组件,完善服务治理 简介 Feign 是一个声明式的 RE ...

  6. SpringCloud实战-Hystrix请求熔断与服务降级

    我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险 ...

  7. react 项目实战(十)引入AntDesign组件库

    本篇带你使用 AntDesign 组件库为我们的系统换上产品级的UI! 安装组件库 在项目目录下执行:npm i antd@3.3.0 -S 或 yarn add antd 安装组件包 执行:npm ...

  8. SpringCloud实战3-Hystrix请求熔断与服务降级

    我们知道大量请求会阻塞在Tomcat服务器上,影响其它整个服务.在复杂的分布式架构的应用程序有很多的依赖,都会不可避免地在某些时候失败.高并发的依赖失败时如果没有隔离措施,当前应用服务就有被拖垮的风险 ...

  9. 微服务之SpringCloud实战(一):SpringCloud简介

    什么是微服务架构 微服务架构就是系统架构设计的一种风格,它主旨将一个独立的系统,拆分成各个微服务,各个微服务独立运行,他们之间通过Http的Restful API进行通信,拆分出来的微服务是根据原系统 ...

随机推荐

  1. Servlet程序访问jsp文件404的一种情况

    启动Jsp Run on Server的时候出现404的错误,如下图: 检查一下是否文档目录如下图:jsp应该在WebContent下,而不是WEB_INF下,访问放在WEB_INF下的jsp文件就会 ...

  2. lambda表达式的distinct去重

    天真的我最开始以为可以写成list.distinct(x=>x.name);以为这样就可以按照name去重了,结果是不行的.这里记录下正确的用法. 1.这里是针对int集合  可以满足 #reg ...

  3. C++ 基础 4:继承和派生

    1 继承和派生 在 C++ 中 可重用性是通过继承这一机制实现的.继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易.这样做,也达到了重用代码功能和提高执行效率的效果. 当创 ...

  4. reids 入门

    1.reids 服务的安装有两种 1.1 exe文件安装,安装完成后,就直接在 "服务"列表中可以查看,并可以停止或启动 1.2 命令行安装:将文件解压至指定文件夹,CMD命令进入 ...

  5. 【转】BSON 和 JSON 的区别

    BSON与JSON的区别 BSON是由10gen开发的一个数据格式,目前主要用于MongoDB中,是MongoDB的数据存储格式.BSON基于JSON格式,选择JSON进行改造的原因主要是JSON的通 ...

  6. K-近邻算法kNN

    K-近邻算法(k-Nearest Neighbor,简称kNN)采用测量不同特征值之间的距离方法进行分类,是一种常用的监督学习方法,其工作机制很简单:给定测试样本,基于某种距离亮度找出训练集中与其靠近 ...

  7. .Net核心依赖项注入:生命周期和最佳实践

    在讨论.Net的依赖注入(DI)之前,我们需要知道我们为什么需要使用依赖注入 依赖反转原理(DIP): DIP允许您将两个类解耦,否则它们会紧密耦合,这有助于提高可重用性和更好的可维护性 DIP介绍: ...

  8. 主动关闭 time wait结构体

    /* * This is a TIME_WAIT sock. It works around the memory consumption * problems of sockets in such ...

  9. python之深浅copy与id

    我们都知道 所谓变量就是就是在空间中开辟一块内存空间.来存放东西的 学过c语言的都知道而数组也是有内存地址的 我们如何来查看内存地址呢?id()这函数就来了 x = 5 print(id(x)) 如此 ...

  10. 关于多线程--网络编程 -- 注解反射的一点笔记(JAVA篇)

    一 . 线程 java开启一个线程的方法(三种) 方法一:继承Thread类并New一个线程对象 步骤: 1):定义一个类A继承于Java.lang.Thread类. class TestThread ...