SpringBoot + SpringCloud Hystrix 实现服务熔断
什么是Hystrix
在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是依赖服务,有的时候某些依赖服务出现故障也是很常见的。
Hystrix是Netflix公司开源的一个项目,它提供了熔断器功能,能够解决分布式系统中出现联动故障,Hystrix是通过隔离服务的访问点阻止故障,并提供故障解决方案,从而提高分布式系统弹性。
Hystrix可以让我们在分布式系统中对服务间的调用进行控制,加入一些调用延迟或者依赖故障的容错机制。Hystrix通过将依赖服务进行资源隔离,进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;同时Hystrix 还提供故障时的 fallback 降级机制。
总而言之,Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。
Hystrix解决了什么问题
在分布式系统中,可能有几十个服务相互依赖。这些服务由于某些原因导致不可用。如果系统不隔离不可用的服务,则可能会导致整个系统不可用。<br /> 在高并发情况下,单个服务的延迟会导致整个请求都处于延迟状态,可能在几秒钟就使整个线程处于负载饱和状态。<br /> 某个服务的单点故障会导致用户的请求处于阻塞状态,最终的结果就是整个服务的线程资源消耗殆尽。由于服务的依赖性,会导致依赖该故障服务的其他服务也处于线程阻塞状态,最终导致这些依赖服务的线程资源消耗殆尽,直到不可用,从而导致整个服务系统不可用,这就是雪崩效应。<br /> 为了防止雪崩效应,因而产生了熔断器模型。Hystrix是业界表现非常好的一个熔断器模型实现的开源组件,是SpringCloud组件不可缺少的一部分。<br />
Hystrix设计原则
- 防止单个服务故障耗尽整个服务的Servlet容器(Tomcat/Jetty)的线程资源。
- 快速失败机制,如果某个服务出现故障,则调用该服务的请求迅速失败,而不是线程等待。
- 提供回退方案(fallback),在请求发生故障时,提供设定好的回退方案。
- 使用熔断机制,防止故障扩散到其他服务。
- 提供熔断器的监控组件Hystrix Dashboard,近实时的监控,报警以及运维操作。
Hystrix工作服原理
首先,当服务的某个API接口的失败次数在一定时间内小于设定的阈值时,熔断器处于关闭状态,该API接口正常提供服务。当该API接口处理请求失败的次数大于设定阈值时,Hystrix判定该接口出现了故障,打开熔断器。这时请求该API接口会执行快速失败的逻辑(fallback的逻辑),不执行业务,请求的线程不会处于阻塞状态。 处于打开状态的熔断器,一段时间后会处于半打开状态,并将一定数量的请求执行正常逻辑。剩余的请求会执行快速失败,若执行正常请求的逻辑失败了,则熔断器继续打开。如果执行成功了,则将熔断器关闭,这样设计的熔断器则具备了自我修复的能力。
在RestTemplate和Ribbon作为服务消费者时使用Hystrix
新建父Module和4个子Module,项目结构如下图
父Module主要用来引入DependencyManagement
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
</dependencyManagement>
创建EurekaServer
创建子Module, try-spring-cloud-eureka-server。
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 7001
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka # 注册中心端口7001
register-with-eureka: false
fetch-registry: false
Main 函数入口
@SpringBootApplication
@EnableEurekaServer
public class MyEurekaServer7001 {
public static void main(String[] args) {
SpringApplication.run(MyEurekaServer7001.class,args);
}
}
创建Student Server Provider
创建子Module,try-spring-cloud-student-service
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</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>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 8001
spring:
application:
name: CLOUD-STUDENT-SERVICE
eureka:
client:
fetch-registry: false
register-with-eureka: true # 注册进Eureka Server
service-url:
defaultZone: http://localhost:7001/eureka # 单机版指向7001
Main函数
@SpringBootApplication
@EnableEurekaClient
public class MyStudentService8001 {
public static void main(String[] args) {
SpringApplication.run(MyStudentService8001.class,args);
}
}
Controller, 我在这里先返回了一个字符串用来测试使用。
@RestController
@RequestMapping("/student")
public class StudentController {
@GetMapping("/version")
public String version(){
return "8001,202008182343";
}
}
创建Ribbon Client
创建子Module,try-spring-cloud-ribbon-hystrix。 该模块下,我们基于RestTemplate和Ribbon作为消费者调用服务,先测试下服务正常时访问逻辑, 和前面两个Module不同,这个Module里需要引入spring-cloud-starter-netflix-hystrix依赖。
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</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>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
application.yml
server:
port: 8087
spring:
application:
name: STUDENT-CONSUMER
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka # 服务注册中心地址
Main函数,这里增加EnableHystrix注解,开启熔断器。
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class MyStudentRibbonHystrix {
public static void main(String[] args) {
SpringApplication.run(MyStudentRibbonHystrix.class,args);
}
}
Ribbon调用配置,关键注解LoadBalanced。
@Configuration
public class MyWebConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
Controller, 这里调用CLOUD-STUDENT-SERVICE的version()接口,同时增加了HystrixCommand注解,设置了属性fallbackMethod, 如果方法调用失败则执行快速失败方法getErrorInfo。
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
RestTemplate restTemplate;
/**
* 使用HystrixCommand注解,设置服务调用失败时回调方法(getErrorInfo)
* **/
@GetMapping("/version")
@HystrixCommand(fallbackMethod = "getErrorInfo")
public String version() {
System.out.println("Ribbon调用前");
String result = restTemplate.getForObject("http://CLOUD-STUDENT-SERVICE/student/version", String.class);
System.out.println("Ribbon调用后,返回值:" + result);
return result;
}
public String getErrorInfo(){
return "Network error, please hold on...";
}
}
依此启动Eureka-Server、Student-Service、Ribbon-Hystrix先测试下服务正常调用逻辑。
访问http://localhost:8087/student/version, 如果正常输出8001,202008182343字符串,则说明服务目前正常。然后我们在停止Student-Service(8001)的服务,再访问下8087服务, 结果输出Network error, please hold on... 则说明熔断器已经生效。
HystrixCommand注解参数
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface HystrixCommand {
#配置全局唯一标识服务的名称
String groupKey() default "";
#配置全局唯一标识服务分组的名称,比如,库存系统、订单系统、系统用户就是一个单独的服务分组
String commandKey() default "";
#对线程池进行设定,细粒度的配置,相当于对单个服务的线程池信息进行设置,也可多个服务设置同一个threadPoolKey构成线程组
String threadPoolKey() default "";
#执行快速失败的回调函数,@HystrixCommand修饰的函数必须和这个回调函数定义在同一个类中,因为定义在了同一个类中,所以fackback method可以是public/private均可
String fallbackMethod() default "";
#配置该命令的一些参数,如executionIsolationStrategy配置执行隔离策略,默认是使用线程隔离
HystrixProperty[] commandProperties() default {};
#线程池相关参数设置,具体可以设置哪些参数请见:com.netflix.hystrix.HystrixThreadPoolProperties
HystrixProperty[] threadPoolProperties() default {};
#调用服务时,除了HystrixBadRequestException之外,其他@HystrixCommand修饰的函数抛出的异常均会被Hystrix认为命令执行失败而触发服务降级的处理逻辑(调用fallbackMethod指定的回调函数),所以当需要在命令执行中抛出不触发降级的异常时来使用它,通过这个参数指定,哪些异常抛出时不触发降级(不去调用fallbackMethod),而是将异常向上抛出
Class<? extends Throwable>[] ignoreExceptions() default {};
#定义hystrix observable command的模式
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
#任何不可忽略的异常都包含在HystrixRuntimeException中
HystrixException[] raiseHystrixExceptions() default {};
#默认的回调函数,该函数的函数体不能有入参,返回值类型与@HystrixCommand修饰的函数体的返回值一致。如果指定了fallbackMethod,则fallbackMethod优先级更高
String defaultFallback() default "";
}
## 在OpenFeign作为服务消费者时使用Hystrix
创建子Module, try-spring-cloud-openfeign-hystrix,这个节点使用OpenFeign作为服务消费者时使用Hystrix, OpenFeign里已经依赖了Hystrix,所以这里不需要再单独引入,只需要引入spring-cloud-starter-openfeign即可。
pom.xml
```java
junit
junit
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
org.springframework.boot
spring-boot-starter-actuator
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-starter-openfeign
```
```java
server:
port: 8087
spring:
application:
name: STUDENT-OPENFEIGN-CONSUMER
eureka:
client:
register-with-eureka: false
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka # 服务注册中心地址
feign:
hystrix:
enabled: true #在feign中开启hystrix
定义调用CLOUD-STUDENT-SERVICE的接口,也就是增加注解FeignClient,设置value和fallback属性。
```java
@FeignClient(value = "CLOUD-STUDENT-SERVICE",fallback = StudentFallbackService.class)
public interface StudentService {
@GetMapping("/student/version")
String version();
}
StudentFallbackService
@Component
public class StudentFallbackService implements StudentService {
@Override
public String version() {
return "Network Error, I am callback service...";
}
}
Controller
@RestController
@RequestMapping("/student")
public class StudentController {
@Autowired
StudentService studentService;
@GetMapping("/version")
public String version(){
System.out.println("===openfeign 调用===");
return studentService.version();
}
}
Main函数
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class MyStudentOpenfeignHystrix {
public static void main(String[] args) {
SpringApplication.run(MyStudentOpenfeignHystrix.class,args);
}
}
依次启动Eureka-Server, Student-Service,OpenFeign-Hystrix-Client,和上面Ribbon一样,先来测试下正常逻辑,访问http://localhost:8087/student/version. 输出8001,202008182343字符串说明服务调用正常,然后停掉Student-Service,再次访问接口,输出Network Error, I am callback service... 则说明基于OpenFeign的熔断器已经生效。
请求缓存功能@CacheResult
Hystrix还提供了请求缓存功能,当一些查询服务不可用时,可以调用缓存查询。@CacheResult需要和@HystrixCommand组合使用
注解 | 描述 | 属性 |
---|---|---|
@CacheResult | 该注解用来标记请求命令返回的结果应该被缓存,它必须与@HystrixCommand注解结合使用 | cacheKeyMethod |
@CacheRemove |
| 该注解用来让请求命令的缓存失效,失效的缓存根据定义Key决定 | commandKey,
cacheKeyMethod |
| @CacheKey | 该注解用来在请求命令的参数上标记,使其作为缓存的Key值,如果没有标注则会使用所有参数。如果同事还是使用了@CacheResult和@CacheRemove注解的cacheKeyMethod方法指定缓存Key的生成,那么该注解将不会起作用 | value |
总结
Hystrix现在已经宣布停止更细了,进入了维护模式,但是其性能也比较稳定了,spring官方推荐了[Resilience4J](https://github.com/resilience4j/resilience4j)、阿里的[Sentinel](https://github.com/alibaba/Sentinel)、[Spring Retry](https://github.com/spring-projects/spring-retry)作为替代方案。
SpringBoot + SpringCloud Hystrix 实现服务熔断的更多相关文章
- Spring Cloud实战之初级入门(四)— 利用Hystrix实现服务熔断与服务监控
目录 1.环境介绍 2.服务监控 2.1 加入依赖 2.2 修改配置文件 2.3 修改启动文件 2.4 监控服务 2.5 小结 3. 利用hystrix实现消费服务熔断 3.1 加入服务熔断 3.2 ...
- JNPF.java前后端分离框架,SpringBoot+SpringCloud开发微服务平台
JNPF.java版本采用全新的前后端分离架构模式.前后端分离已成为互联网项目开发的业界标准开发方式,通过 nginx+tomcat 等方式有效的进行解耦合,并且前后端分离会为以后的大型分布式架构.弹 ...
- springcloud组件之hystrix服务熔断,降级,限流
hystrix 简介 Hystrix是什么 在分布式环境中,许多服务依赖项中的一些必然会失败.Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互.Hystrix通过 ...
- 《springcloud 四》服务保护机制
服务保护机制SpringCloud Hystrix 微服务高可用技术 大型复杂的分布式系统中,高可用相关的技术架构非常重要. 高可用架构非常重要的一个环节,就是如何将分布式系统中的各个服务打造成高可用 ...
- 【springcloud】服务熔断与降级(Hystrix)
转自:https://blog.csdn.net/pengjunlee/article/details/86688858 服务熔断 服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的 ...
- java框架之SpringCloud(5)-Hystrix服务熔断、降级与监控
前言 分布式系统面临的问题 复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败.不做任何处理的情况下,很容易导致服务雪崩. 服务雪崩:多个微服务之间调用的时候,假设 ...
- SpringCloud微服务(03):Hystrix组件,实现服务熔断
本文源码:GitHub·点这里 || GitEE·点这里 写在前面:阅读本文前,你可能需要熟悉一下内容. 微服务组件:Eureka管理注册中心 微服务组件:Ribbon和Feign服务调用 Sprin ...
- Spring-cloud微服务实战【七】:服务熔断与降级hystrix
在之前的文章中,我们先后介绍了eureka,ribbon,feign,使用eureka集群的方式来保证注册中心的高可用,在eureka中使用ribbon进行负载均衡,使用feign接口替换手动编码 ...
- SpringCloud Netflix (五) : Hystrix 服务熔断和服务降级
什么是Hystrix 在分布式环境中,许多服务依赖项中的一些服务依赖不可避免地会失败.Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助您控制这些分布式服务之间的交互.Hystrix通过隔离服务 ...
随机推荐
- JS的执行上下文
定义 执行上下文时是代码执行时的环境,JS代码在运行前进行编译,那么会生成两部分,一部分是可执行的代码,而另一部分则是执行上下文. 发展 执行上下文所包含的内容是在不断的变化的.它主要分为了三个不同的 ...
- 进度条函数 -------ajax初试
做一个显示任务完成情况的进度条: <!DOCTYPE html> <html> <head> <meta charset="utf-8"& ...
- PHP cal_info() 函数
------------恢复内容开始------------ 实例 返回格利高里历法的信息: <?phpprint_r(cal_info(0));?> 运行实例 » 定义和用法 cal_i ...
- PHP decoct() 函数
实例 把十进制转换为八进制: <?phpecho decoct("30") . "<br>";echo decoct("10&quo ...
- Lambda表达式运行原理
目录 一.创建测试样例 二.利用Java命令编译分析 三.文末 JDK8引入了Lambda表达式以后,对我们写代码提供了很大的便利,那么Lambda表达式是如何运用简单表示来达到运行效果的呢?今天,我 ...
- Redis分布式限流器
以下文章来源于微信公众号:程序员内点事 ,作者:程序员内点事 请大家关注原作者 1. 什么是限流?为什么要限流? 限流是保证系统高可用的重要手段!!!由于互联网公司的流量巨大,系统上线会做一个流量峰值 ...
- 07 . ELK Stack一键多机部署脚本
一键部署脚本 目录结构 tree Log_Analysis_Platform_Document Log_Analysis_Platform_Document ├── InstallES.sh ├── ...
- java数组输出的三种方式
第一种:foreach语句遍历输出 //通过foreach语句遍历输出数组 int nums[] = new int [4]; for (int num:nums) { System.out.prin ...
- DataGrip 2020.1 安装与激活
1 软件下载 百度网盘: 链接:https://pan.baidu.com/s/1kHSq1XS0i4YDF0HuzsxCLg 提取码:djyc 2 安装 解压文件后点击 datagrip-2020. ...
- 01-java开发环境配置
1 JDK.JRE.JVM的关系 JDK: java开发工具包 JRE: java运行时环境 JVM: java虚拟机 2 JDK下载 方式一:官网 方式二:该链接提供jdk1.6 ,jdk1.7 , ...