一、Hystrix简介
在分布式环境中,许多服务依赖项中的一些必然会失败。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix如何解决依赖隔离:
1、包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立的线程中执行,使用了设计模式中的“命令模式”;
2、跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间;
3、资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程已满,则发向该依赖的请求就会被立即拒绝,而不是排队等候,从而加速失败判定;
4、监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等;
5、回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑,回退逻辑由开发人员自行提供,如返回一个缺省值;
6、自我修复:断路器打开一段时间后,会自动进入“半开”状态,此时断路器可允许一个请求访问依赖的服务,若请求成功,则断路器关闭,否则断路器转为“打开”状态;
形成过程:
1)服务提供者不可用
a)硬件故障:硬件损坏造成的服务器主机宕机, 网络硬件故障造成的服务提供者的不可访问
b)程序Bug:
c) 缓存击穿:缓存击穿一般发生在缓存应用重启, 所有缓存被清空时,以及短时间内大量缓存失效时. 大量的缓存不命中, 使请求直击后端,造成服务提供者超负荷运行,引起服务不可用
d)用户大量请求:在秒杀和大促开始前,如果准备不充分,用户发起大量请求也会造成服务提供者的不可用
2)重试加大流量
a)用户重试:在服务提供者不可用后, 用户由于忍受不了界面上长时间的等待,而不断刷新页面甚至提交表单
b)代码逻辑重试: 服务调用端的会存在大量服务异常后的重试逻辑
3)服务调用者不可用
a)同步等待造成的资源耗尽:当服务调用者使用同步调用 时, 会产生大量的等待线程占用系统资源. 一旦线程资源被耗尽,服务调用者提供的服务也将处于不可用状态, 于是服务雪崩效应产生了。
为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
Hystrix是一个用于分布式系统的延迟和容错的开源库。在分布式系统里,许多依赖不可避免的调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的”扇出”,如扇出的链路上某个微服务的调用响应式过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统雪崩,所谓的”雪崩效应”。断路器:
“断路器”本身是一种开关装置,当某个服务单元发生故障监控(类似熔断保险丝),向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延。乃至雪崩。服务熔断:
熔断机制是应对雪崩效应的一种微服务链路保护机制,
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回”错误”的响应信息。当检测到该节点微服务响应正常后恢复调用链路,在SpringCloud框架机制通过Hystrix实现,Hystrix会监控微服务见调用的状况,当失败的调用到一个阈值,缺省是5秒内20次调用失败就会启动熔断机制,熔断机制的注解是@HystrixCommand
二、Maven依赖
<!-- Hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
三、 主启动类添加注解EnableCircuitBreaker
@SpringBootApplication
@EnableCircuitBreaker //对Hystrix熔断机制的支持
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
四、熔断测试
@RequestMapping(value = "/user/get/{id}", method = RequestMethod.GET)
// 如果当前调用的get()方法出现了错误,则执行fallback
@HystrixCommand(fallbackMethod="getFallback")
public Object get(@PathVariable("id") long id) {
String vo=null; // 接收数据库的查询结果
if (vo == null) { // 数据不存在,假设让它抛出个错误
throw new RuntimeException("部门信息不存在!") ;
}
return vo ;
}
/**
* 此时方法的参数 与get()一致
* @param id
* @return
*/
public Object getFallback(@PathVariable("id") long id) {
User vo = new User() ;
vo.setId(1);
vo.setUsername("【ERROR】User-Service-Hystrix"); // 错误的提示
return vo ;
}
现在的处理情况是:服务器出现了错误(但并不表示提供方关闭),那么此时会调用指定方法的 fallback 处理。
什么情况下会触发fallback方法?
名字 | 描述 | 触发fallback |
---|---|---|
EMIT | 值传递NO | |
SUCCESS | 执行完成,没有错误 | NO |
FAILURE | 执行抛出异常 | YES |
TIMEOUT | 执行开始,但没有在允许的时间内完成 | YES |
BAD_REQUEST | 执行抛出HystrixBadRequestException | NO |
SHORT_CIRCUITED | 断路器打开,不尝试执行 | YES |
THREAD_POOL_REJECTED | 线程池拒绝,不尝试执行 | YES |
SEMAPHORE_REJECTED | 信号量拒绝,不尝试执行 | YES |
fallback方法在什么情况下会抛出异常
名字 | 描述 | 抛异常 |
---|---|---|
FALLBACK_EMIT | Fallback值传递 | NO |
FALLBACK_SUCCESS | Fallback执行完成,没有错误 | NO |
FALLBACK_FAILURE | Fallback执行抛出出错 | YES |
FALLBACK_REJECTED | Fallback信号量拒绝,不尝试执行 | YES |
FALLBACK_MISSING | 没有Fallback实例 | YES |
五、基于Feign使用Hystrix
通常情况下的Hystrix是通过注解@HystrixCommand的fallbackMethod属性实现回调的,而在Feign中,由于Feign是用接口实现的声明式Rest,所以Hystrix的通用方法在这里就不适用于Feign了,实际上在Feign与SpringCloud的依赖库中已经默认的将Hystrix加入其中了,如图:
- Maven依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
- application.properties配置
server.port=8762
spring.application.name=user-service
#默认feign的hystrix为关闭状态
feign.hystrix.enabled=true
# 服务注册
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
- 启动类
启动类添加@EnableFeignClients,控制层通过注入feign的接口去完成声明式调用:
@SpringBootApplication
@EnableFeignClients
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
- 接口类
// 接口类上加入的注解中添加属性fallback,指定回调类
@FeignClient(name = "USER-SERVICE",fallback = FeignClientFallback.class)
public interface UserFeign {
@RequestMapping("/getUser")
public String getUser();
}
- 回调类
/**
* @Description: 回调实现类
*/
@Component
class FeignClientFallback implements UserFeign {
@Override
public String getUser() {
System.out.println("熔断,默认回调函数");
return "{\"username\":\"admin\",\"age\":\"-1\"}";
}
}
6、测试
@Autowired
private UserFeign userFeign;
@Test
public void getUser() {
String str = userFeign .getUser();
log.info("{}", str);
}
测试方法,服务方法中,产生异常将会调用熔断方法。