Hystrix 出现的原因
当服务之间出现多级嵌套调用时 其中某些服务调用超时或者发生异常时,会导致整个调用链报错,无法提供正常的服务。所以Hystrix出现,它的触发机制是当某个服务调用超时或异常时,会返回一个符合预期,可处理的备选响应(fallback)来保证整个调用链正常,提供正常的服务。
Hystrix基本概念
- 服务降级:当服务异常时调用fallback返回给用户一个 服务器正忙 请稍后再试的画面提示。
- 服务熔断:当服务达到最大访问请求后,直接拒绝请求访问,调用fallback返回用户友好提示画面。
- 服务限流:设置每秒最大可以有多少个请求访问服务来防止同一时间大量请求访问服务导致系统崩溃。
Hystrix 处理什么样的情况
- 消费者服务调用生产者服务时返回超时的错误,消费者服务不能一直等待,要请求准备好的降级请求。
- 消费者服务调用生产者服务时,生产者服务宕机,消费者服务不能直接抛出错误给用户看。要降级处理。
- 消费者服务调用生产者服务时,发现生产者服务调用的时间超过消费者设置的最大超时时间,这时候消费者服务觉的等不到生产者服务返回值,要降级处理。
Hystrix 生产者设置降级方法
- 添加依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- yml文件没有配置,启动类添加注解@EnableCircuitBreaker 开启降级服务
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class PaymentHystrixMain8001
{
public static void main(String[] args) {
SpringApplication.run(PaymentHystrixMain8001.class, args);
}
}
- 业务方法添加降级方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
})
public String paymentInfo_TimeOut(Integer id)
{
//int age = 10/0;
try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
return "线程池: "+Thread.currentThread().getName()+" id: "+id+"\t"+"O(∩_∩)O哈哈~"+" 耗时(秒): ";
}
public String paymentInfo_TimeOutHandler(Integer id)
{
return "线程池: "+Thread.currentThread().getName()+" 8001系统繁忙或者运行报错,请稍后再试,id: "+id+"\t"+"o(╥﹏╥)o";
}
这个降级操作代表paymentInfo_TimeOut方法业务的时间超过5s就会走paymentInfo_TimeOutHandler降级方法。
注意降级方法还会处理异常的情况,即业务方法发生检查异常也会自动走降级方法
Hystrix 消费者服务降级方法处理
- 添加依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
- 由于服务之间调用用OpenFeign所以,yml文件添加配置
feign:
hystrix:
enabled: true
- 启动类添加注解开启 Hystrix 功能 @EnableHystrix
@SpringBootApplication
@EnableFeignClients
@EnableHystrix
public class OrderHystrixMain80
{
public static void main(String[] args)
{
SpringApplication.run(OrderHystrixMain80.class,args);
}
}
- 业务方法设置超时时间 和降级方法
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
})
//@HystrixCommand
public String paymentInfo_TimeOut(Integer id)
{
//int age = 10/0;
String result = paymentHystrixService.paymentInfo_TimeOut(id);
return result;
}
public String paymentTimeOutFallbackMethod(Integer id)
{
return "我是消费者80,对方支付系统繁忙请10秒钟后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
这样就配置成功降级方法
上述是每个业务方法都配置一个降级方法,后面有开发统一的降级方法和自定义降级方法配置
Hystrix 业务统一降级处理
使用注解@DefaultProperties和@HystrixCommand
消费者服务还可以通过Feign统一配置业务降级处理
- 依赖和yml文件的配置添加上
- @FeignClient注解上配置降级处理类
@Component
// 降级处理类 PaymentFallbackService.class
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT" ,fallback = PaymentFallbackService.class)
public interface PaymentHystrixService
{
@GetMapping("/payment/hystrix/ok/{id}")
public String paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}
- 编写降级处理类 PaymentFallbackService
@Component
public class PaymentFallbackService implements PaymentHystrixService
{
@Override
public String paymentInfo_OK(Integer id)
{
return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
}
@Override
public String paymentInfo_TimeOut(Integer id)
{
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
}
}
这样当业务出现异常或超时时会自动调降级处理。
统一降级和feign降级都可以配置超时时间
Hystrix业务方法开启熔断配置
- 依赖和yml和启动类没有变化
- 业务方法配置断路器开启设置和降级方法
//=====服务熔断 配置含义在 10s内10次请求的失败率达到百分之60 开启断路器
// 是否开启断路器由一定时间内请求成功的频率来决定 所以自动恢复调用链路
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",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 paymentCircuitBreaker(@PathVariable("id") Integer id)
{
if(id < 0)
{
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName()+"\t"+"调用成功,流水号: " + serialNumber;
}
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
{
return "id 不能负数,请稍后再试,/(ㄒoㄒ)/~~ id: " +id;
}