Hystrix [hɪst'rɪks],在微服务架构中,多个服务之间相互依赖,在一次请求执行过程中,难免在调用某一服务时出错。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
Hystrix设计目标
Hystrix被设计的目标是:
对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。
在复杂的分布式系统中阻止级联故障。
快速失败,快速恢复。
回退,尽可能优雅地降级。
启用近实时监控、警报和操作控制。
Hystrix解决了什么问题
复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。
服务链上,因为某个微服务的异常,而导致雪崩效应,整条服务链宕机的问题;
Hystrix会去捕获这个异常,利用Hystrix接口处理这类异常。
当一个用户的请求所依赖的各个服务都提供正常的服务,则请求如下图:
当其中有一个系统有延迟时,它可能阻塞整个用户请求:
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)
Hystrix设计原则是什么
防止任何单个依赖项耗尽所有容器(如Tomcat)用户线程。
甩掉包袱,快速失败而不是排队。
在任何可行的地方提供回退,以保护用户不受失败的影响。
使用隔离技术(如隔离板、泳道和断路器模式)来限制任何一个依赖项的影响。
通过近实时的度量、监视和警报来优化发现时间。
通过配置的低延迟传播来优化恢复时间。
支持对Hystrix的大多数方面的动态属性更改,允许使用低延迟反馈循环进行实时操作修改。
避免在整个依赖客户端执行中出现故障,而不仅仅是在网络流量中。
Hystrix是如何实现它的目标的
用一个HystrixCommand 或者 HystrixObservableCommand (这是命令模式的一个例子)包装所有的对外部系统(或者依赖)的调用,典型地它们在一个单独的线程中执行
调用超时时间比你自己定义的阈值要长。有一个默认值,对于大多数的依赖项你是可以自定义超时时间的。
为每个依赖项维护一个小的线程池(或信号量);如果线程池满了,那么该依赖性将会立即拒绝请求,而不是排队。
调用的结果有这么几种:成功、失败(客户端抛出异常)、超时、拒绝。
在一段时间内,如果服务的错误百分比超过了一个阈值,就会触发一个断路器来停止对特定服务的所有请求,无论是手动的还是自动的。
当请求失败、被拒绝、超时或短路时,执行回退逻辑。
近实时监控指标和配置变化。
当你使用Hystrix来包装每个依赖项时,上图中所示的架构会发生变化,如下图所示:
每个依赖项相互隔离,当延迟发生时,它会被限制在资源中,并包含回退逻辑,该逻辑决定在依赖项中发生任何类型的故障时应作出何种响应:
Ribbon中使用熔断器
前提:参考博客:https://blog.csdn.net/u013310119/article/details/112169872 创建spring-cloud-consumer-ribbon工程,该工程开启了Ribbon。
第一步:改造spring-cloud-consumer-ribbon工程
pom.xml中引入Hystrix依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>
启动类上添加@EnableHystrix注解
在启动类上添加@EnableHystrix注解开启Hystrix的熔断器功能,改造后启动类如下:
@EnableHystrix //在启动类上添加@EnableHystrix注解开启Hystrix的熔断器功能。
@EnableEurekaClient
@SpringBootApplication
public class RibbonConsumerApplication {
//当添加@LoadBalanced注解,就代表启动Ribbon,进行负载均衡
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(RibbonConsumerApplication.class, args);
}
}
Controller类请求第三方服务的方法修改
在需要有熔断机制的方法上添加 @HystrixCommand,属性fallbackMethod是熔断时返回的方法,编写完成后HystrixConsumerController.java代码如下:
/**
* 消费者
*/
@Slf4j
@RestController
@RequestMapping("/hystrix/consumer")
public class HystrixConsumerController {
@Autowired
private RestTemplate restTemplate;
/**
* 调用 user微服务
*/
@HystrixCommand(fallbackMethod = "getDefaultUser")
@GetMapping("getUser")
public String getUser(Integer id) {
String url = "http://user-service/provider/getUser?id=" + id;
return restTemplate.getForObject(url, String.class);
}
public String getDefaultUser(Integer id) {
System.out.println("熔断,默认回调函数");
return "{\"id\":-1,\"name\":\"熔断用户\",\"password\":\"123456\"}";
}
}
开始测试
代码编写之后,按顺序启动spring-cloud-eureka、spring-cloud-user-service和spring-cloud-consumer-ribbon,此时打开浏览器访问http://localhost:8082/hystrix/consumer/getUser?id=2,服务正常,截图如下:
服务正常截图
然后停服务spring-cloud-user-service,再次访问访问http://localhost:8082/hystrix/consumer/getUser?id=2,此时会触发熔断,截图如下:
触发熔断后截图
Feign中使用熔断器
Feign在整合到Spring Cloud时已经自带了hystrix模块,所以pom.xml中不需要额外引入feign依赖。
新建一个spring boot项目spring-cloud-consumer-fegin-hystrix,按照下面步骤操作。
application.yml中开启熔断器
server:
port: 8082 #服务端口
eureka:
client:
serviceUrl:
defaultZone: http://localhost:9001/eureka/
spring:
application:
name: fegin-hystrix-consumer
feign:
hystrix:
# feign熔断器开关
enabled: true
新建一个FeignClient接口
新建一个FeignClient接口UserFeginService并指定fallback,代码如下:
//表示"user-service"的服务,指定fallback
@FeignClient(value = "user-service", fallback = UserFeginFailBackImpl.class)
public interface UserFeginService {
@RequestMapping(value = "/provider/getUser")
public String getUser(@RequestParam("id") Integer id);
}
@FeignClient注解参数说明:
name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。
fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口。
fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码
path: 定义当前FeignClient的统一前缀,类似于注解到类上的@RequestMapping的功能
添加熔断处理类UserFeginFailBackImpl
@Slf4j
@Component
public class UserFeginFailBackImpl implements UserFeginService {
@Override
public String getUser(Integer id) {
log.info("熔断,默认回调函数");
return "{\"id\":-1,\"name\":\"熔断用户\",\"msg\":\"请求异常,返回熔断用户!\"}";
}
}
注意:
hystrix的异常fallback method wasn't found
出现这个异常是因为指定的备用方法和原方法的参数个数或类型不同造成的,所以需要统一参数的类型和个数。
添加Controller
添加FeginHystrixController,用于调用user-service,代码如下:
@RestController
@RequestMapping("/hystrix/consumer")
public class FeginHystrixController {
@Autowired
private UserFeginService userFeginService;
@GetMapping("/getUser")
public String getUser(Integer id) {
return userFeginService.getUser(id);
}
}
开始测试
代码编写之后,按顺序启动spring-cloud-eureka、spring-cloud-user-service和spring-cloud-consumer-fegin-hystrix,此时打开浏览器访问http://localhost:8082/hystrix/consumer/getUser?id=2,服务正常,截图如下:
服务正常截图
然后停服务spring-cloud-user-service,再次访问访问http://localhost:8082/hystrix/consumer/getUser?id=2,此时会触发熔断,截图如下: