虽然已进入维护阶段但是一些理念还是可以了解一下的。
一、Hystrix 是什么?
在分布式环境中,许多服务依赖中的一些服务发生失败是不可避免的。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止跨服务的级联故障以及提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。也就是说Hystrix是一种类似于熔断保险丝的开关装置。在消费者端安装一个Hystrix熔断器。当Hystrix监控到某个服务发生故障后熔断器就会开启,将此服务访问链路断开。不过Hystrix并不会将该服务的消费者阻塞,或向消费者抛出异常,而是向消费者返回一个符合预期的备选响应(FallBack)。通过Hystrix的熔断与降级功能,避免了服务雪崩的发生,同时也考虑到了用户体验,故Hystrix是系统的一种防御机制。
二、Hystrix 能帮我们做什么?
2.1、服务降级
服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。
2.2、服务熔断
所谓的服务熔断指的是某个服务故障或异常一起类似显示世界中的“保险丝”当某个异常条件被触发就直接熔断整个服务,而不是一直等到此服务超时。
2.3、服务限流
三、如何在业务中使用 Hystrix?
3.1、使用Hystirx 为服务端进行服务降级
步骤一:在主启功类上添加注解@EnableCircuitBreaker
在SpringBoot的主启动类上添加注解以开启 Hystrix 相关特性,代码如下。
@SpringBootApplication
@EnableCircuitBreaker
public class CloudProviderHystrixPayment8001Application {
public static void main(String[] args) {
SpringApplication.run(CloudProviderHystrixPayment8001Application.class, args);
}
}
步骤二:在业务代码中使用@HystrixCommand
为业务方法指定降级方法
通过例子来说明每个注解的作用,代码如下。
使用
@HystrixCommand
表示该方法受到 Hystrix 的保护- 属性
fallbackMethod
用于指定当前方法满足服务降价的条件时执行的方法名。 属性
commandProperties
用于指定除方法报错以外的服务降级条件。 ```java @Service public class PaymentService { /**- 超时访问,设置自身调用超时的峰值,峰值内正常运行,超过了峰值需要服务降级 自动调用fallbackMethod 指定的方法
- 超时异常或者运行异常 都会进行服务降级 *
- @param id
- @return
*/
@HystrixCommand(fallbackMethod = “paymentInfoTimeOutHandler”, commandProperties = {
//用于指定方法执行超时时间
@HystrixProperty(name = “execution.isolation.thread.timeoutInMilliseconds”, value = “5000”)
})
public String paymentInfoTimeOut(Integer id) {
int age = 10/0;
return “线程池: “ + Thread.currentThread().getName() + “ paymentInfoTimeOut,id: “ + id + “\t”
}+ "O(∩_∩)O哈哈~" + " 耗时(秒): " + second;
/**
- paymentInfoTimeOut 方法失败后 自动调用此方法 实现服务降级 告知调用者 paymentInfoTimeOut 目前无法正常调用 *
- @param id
- @return */ public String paymentInfoTimeOutHandler(Integer id) { return “线程池: “ + Thread.currentThread().getName() + “ paymentInfoTimeOutHandler8001系统繁忙或者运行报错,请稍后再试,id: “ + id + “\t”+ “o(╥﹏╥)o”; } }
- 属性
<a name="elMVa"></a>
### 3.2、使用Hystirx 为消费端进行服务降级
服务降级一般都是在消费端去做的,那么在使用 Open Feign 的情况下,下面我们一起来看一下 Hystrix 是如何配合 Open Feign实现服务降级的。
<a name="FPgpa"></a>
##### 步骤一:添加 Hystrix 依赖
在`pom.xml`文件中添加如下内容,引入 Hystrix 相关功能。
```xml
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
步骤二:在主启功类上添加注解@EnableHystrix
在主启动类添加注解以开启 Hystrix
@SpringBootApplication
@EnableFeignClients // 启动 feign
@EnableHystrix // 启动 hystrix
public class CloudConsumerFeignHystrixOrder80Application {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerFeignHystrixOrder80Application.class, args);
System.out.println("启动成功");
}
步骤三:修改 application.yaml 以开启 Hystrix
通过以下属性使得在 Open Feign 执行 RPC 调用时实现 Hystrix 的相关功能,其中就包括服务降级。
feign:
hystrix:
enabled: true #如果处理自身的容错就开启。开启方式与生产端不一样。
步骤四:在业务代码中使用服务降级
此处使用方式和在服务端进行服务降级一样,具体属性说明参考服务端服务降级相关内容。
@RestController
public class OrderHystrixController {
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
//int age = 10/0;
return paymentHystrixService.paymentInfo_TimeOut(id);
}
/**
* hystrix 服务降级方法
*
* @return
*/
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
}
3.3、服务降级进一步优化
3.3.1、问题一:解决每个方法都需要配置一个 fallback 属性
为了应对该问题 Hystrix 提供了一种方案,使用@DefaultProperties
合理的减少了代码量。
@RestController
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
//int age = 10/0;
return paymentHystrixService.paymentInfo_TimeOut(id);
}
@GetMapping("/consumer/payment/hystrix/global/{id}")
@HystrixCommand
public String paymentInfoGlobal(@PathVariable("id") Integer id) {
int age = 10/0;
return paymentHystrixService.paymentInfo_TimeOut(id);
}
public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
return "我是消费者80,对方支付系统繁忙请10秒种后再试或者自己运行出错请检查自己,o(╥﹏╥)o";
}
/**
* hystrix 全局fallback方法
* @return
*/
public String payment_Global_FallbackMethod() {
return "Global异常处理信息,请稍后再试,/(ㄒoㄒ)/~~";
}
}
通过使用@DefaultProperties
注解,我们在使用@HystrixComman
注解时不需要再具体配置相关属性也可以实现服务降级,从而减少代码量。
3.3.2、问题二:在消费端做服务降级,降级方法和业务方法掺杂在一起导致代码混乱
解决这个问题,首先我们定义一个类实现 Open Feign 调用的接口,然后使用注解
指定服务降级的类,这样就可以把降级代码从业务代码中抽离出来。
步骤一:实现 OpenFeign 接口并定义降级逻辑
@Component
public class PaymentFallbackServiceImpl implements PaymentHystrixService {
@Override
public String paymentInfoOK(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_OK ,o(╥﹏╥)o";
}
@Override
public String paymentInfoTimeOut(Integer id) {
return "-----PaymentFallbackService fall back-paymentInfo_TimeOut ,o(╥﹏╥)o";
}
}
步骤二:在 OpenFeign 接口上指定服务降级class
使用 fallback
属性来定义服务降级类
/**
* Fallback class for the specified Feign client interface. The fallback class must
* implement the interface annotated by this annotation and be a valid spring bean.
* @return fallback class for the specified Feign client interface
*/
Class<?> fallback() default void.class;
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT", fallback = PaymentFallbackServiceImpl.class)
public interface PaymentHystrixService {
@GetMapping("/payment/hystrix/ok/{id}")
String paymentInfoOK(@PathVariable("id") Integer id);
@GetMapping("/payment/hystrix/timeout/{id}")
String paymentInfoTimeOut(@PathVariable("id") Integer id);
}
3.4、服务降级与全局异常捕获
通过以上的例子我们发现,其实Hystrix的服务降级和 SpringBoot 提供的@ControllerAdvice
全局异常处理类似,这里补充说明一下,服务降级的优先等级高于全局异常处理。
3.5、服务熔断在代码中的实现
在@HystrixCommand
的commandProperties
按照如下方式配置即可。circuitBreaker.enabled
属性用于配置是否开启服务熔断circuitBreaker.requestVolumeThreshold
属性用于配置请求参数circuitBreaker.sleepWindowInMilliseconds
用于配置时间窗口期,单位为mscircuitBreaker.errorThresholdPercentage
用于配置服务熔断条件,错误率达到百分之多少
@HystrixCommand(fallbackMethod = "paymentCircuitBreakerFallback", 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(Integer id) {
if (id < 0) {
throw new RuntimeException("******id 不能负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + "\t" + "调用成功,流水号: " + serialNumber;
}