1. Hystrix 是什么?
分布式系统面临的问题:
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败
1.1 服务雪崩
多个微服务之间的调用,结社微服务A调用微服务B和微服务C,微服务B和微服务C有调用其他的微服务,这就是所谓的“扇出”如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所以的 “雪崩效应”
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败糟糕的是,这些应用程序还可能导致服务之间延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理以便单个依赖关系的失败,不能取消整个应用程序或系统
所以,通常当发现一个模块下的某个实例失败后,这时候这个模块依然会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩
1.2 Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等。
Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器” 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方放回一个符合预期的、可处理的备选响应,而不是长时间的等待或抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
2. Hystrix 重要概念
2.1 服务降级
- 服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示
- 哪些情况会触发降级?
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量打满也会导致服务降级
2.2 服务熔断
- 类比保险丝。达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示
- 整体步骤:服务的降级》进而熔断》恢复调用链路
2.3 服务限流
- 秒杀高并发操作,严禁一窝蜂的拥挤,大家排队,一秒钟N个,有序进行
3. Hystrix 使用
使用hystrix已经可以代替原有的provider和order了
所以两个都可以新建一个模块
在原有基础上添加上Hystrix的jar包
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
由于Hystrix已经停更,2.2.10是最后的一个版本
3.1 服务降级
3.1.1 Provider-payment8001
主启动类上需要添加@EnableHystrix注解
@SpringBootApplication(exclude= {
DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableHystrix // 必须开启
public class PaymentHystrixMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class,args);
}
}
这里可以以service服务实现类来做 服务降级
但结构简单了点,直接用了Impl类
@Service
public class PaymentService {
public String paymentInfo_OK(Integer id){
return "线程池:"+Thread.currentThread().getName()+"——id:"+id;
}
/**
* @HystrixCommand 若是出错、超时等异常情况,返回一个兜底方法
* @param id
* @return
*/
@HystrixCommand(fallbackMethod = "peymentInfo_ERROR_Handler",commandKey = "commandKey01")
public String paymentInfo_ERROR(Integer id){
// int i = 10/0;// 直接异常
// 超时异常
try {
TimeUnit.SECONDS.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ERROR - 线程池:"+Thread.currentThread().getName()+"——id:"+id;
}
/**
异常回调函数
*/
public String peymentInfo_ERROR_Handler(Integer id){
return "ERROR - 兜底监听方法 - 线程池:"+Thread.currentThread().getName()+"——id:"+id;
}
}
@HystrixCommand(fallbackMethod = "peymentInfo_ERROR_Handler",commandKey = "commandKey01")
fallbackMethod 参数值直接是兜底方法的方法名
在需要使用异常回调方法的方法上添加上@HystixCommand注解,即可开启服务降级
@HystrixCommand(fallbackMethod = "peymentInfo_ERROR_Handler",commandKey = "commandKey01")
- 参数一:fallbackMethod
- 绑定返回回调函数的名称
- 参数二:commandKey
- 绑定该方法的key值
实际上,服务降级光光在provider上使用注解也可以达到服务降级的操作。
而我们也可以在添加order80后,有了转发,也可以在order80中添加服务降级,比provider要更先一步获得异常并捕获
当要设置超时时间时,需要在yaml配置文件中配置时间。老版本中在注解中设置已经无用
## hystrix 设置兜底请求兜底方法的最多超时时间
hystrix:
command:
HystrixCommandKey:
execution:
isolation:
thread:
timeoutInMilliseconds: 1500
3.1.2 order80
在主启动类上,一样要添加@EnableHystrix注解
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
一样的转发接口
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String peymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/error/{id}")
public String peymentInfo_ERROR(@PathVariable("id") Integer id);
}
在控制器内,若是想要开启该控制器内的全部函数都有一个异常回调方法,
那么需要在开启的方法上添加没有参数的@hystrixCommand注解,其二就是在控制器上添加@DefaultProperties(defaultFallback = "paymentDefaultFallBackMethod")注解。
@DefaultProperties注解的参数就是默认的回调函数
@Slf4j
@RestController
@DefaultProperties(defaultFallback = "paymentDefaultFallBackMethod")
public class OrderHystrixController {
@Resource
private PaymentHystrixService paymentHystrixService;
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/ok/{id}")
public String peymentInfo_OK(@PathVariable("id") Integer id){
String result = paymentHystrixService.peymentInfo_OK(id);
return result;
}
// @HystrixCommand(fallbackMethod = "peymentInfo_ERROR_Handler_80",commandKey = "commandKey_80")
@HystrixCommand
@GetMapping("/consumer/payment/hystrix/error/{id}")
public String peymentInfo_ERROR(@PathVariable("id") Integer id){
String s = paymentHystrixService.peymentInfo_ERROR(id);
return s;
}
public String peymentInfo_ERROR_Handler_80(@PathVariable("id") Integer id){
return "消费者80的监听兜底方法出现了!!!";
}
/**
* 全局fallback方法
* @return
*/
public String paymentDefaultFallBackMethod(){
return "控制器内存在方法或运行异常,执行了默认fallback方法!!";
}
}
若是还不够,那么可以在转发接口上做点文章。可以在接口类的@FeignClient注解上设置第二个参数:fallback;
@Component
/**
* fallback = PaymentHystrixFallbackService.class
* ———————— 设定该接口只要调用的方法中存在错误异常,都会使用该实现类中的兜底方法
*/
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentHystrixFallbackService.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
public String peymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/error/{id}")
public String peymentInfo_ERROR(@PathVariable("id") Integer id);
}
这个参数我们需要一个实现该接口类的service类,来作为我们的异常调用方法
@Component
public class PaymentHystrixFallbackService implements PaymentHystrixService{
@Override
public String peymentInfo_OK(Integer id) {
return "peymentInfo_OK ______ 执行fallback";
}
@Override
public String peymentInfo_ERROR(Integer id) {
return "peymentInfo_ERROR _______ 执行fallback";
}
}
这样在接口转发回调时就会被触发
3.2 服务熔断
我们在provider的控制器中,再次添加一个方法
// ============= 服务熔断
@HystrixCommand(fallbackMethod = "paymentCirecuitBreakerFallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
public String paymentCirecuitBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("###########id,不能是负数");
}
return Thread.currentThread().getName()+"\t"+"调用成功,流水号"+System.currentTimeMillis();
}
public String paymentCirecuitBreakerFallback(@PathVariable("id") Integer id){
return "id不能是负数————————触发fallback"+id;
}
具体看注解。注解中的commandProperties参数就是配置和熔断的各类内容
3.2.1 熔断总结
@HystrixCommand(fallbackMethod = "paymentCirecuitBreakerFallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失败率达到多少后跳闸
})
public String paymentCirecuitBreaker(@PathVariable("id") Integer id){
}
设计到断路器的三个重要参数:快照时间窗、请求总数阈值、错误百分比阈值
- 快照时间窗:断路器确定是否打开需要统计一些请求和错误数据,而统计的时间范围就是快照时间窗,默认为最近的10秒
- 请求总数阈值:在快照时间内,必须满足请求总数阈值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数不足20次,即使所有的请求都超时或其他原因失败,断路器都不会打开
- 错误百分比阈值:当请求总数在快照时间内超过了阈值,比如发生了30调用,如果在这30次调用中,有15此发生了超时异常,也就是超过了50%的错误百分比,在默认设定50%阈值情况下,这时候就会将断路器打开。
4. Hystrix图形化Dashboard搭建:
Hystrix(图形化)已过时,对应的jar包也已经找不到了。
<!-- 未找到依赖,去maven的jar包仓库中也未找到 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
🥸🏏SpringCloud微服务专栏
- 【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
- 【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
- 【SpringCloud(3)】Ribbon负载均衡:IRule原理轮询算法;LB负载均衡;loadbalancer和IRule组件;Ribbon和Ngin负载均衡的区别
- 【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign