0.什么是Hystrix
Hystrix是一个用于处理分布式系统中的延迟和容错的开源库,在分布式系统中,很多依赖会不可避免的出现调用失败的情况,如超时、异常等,Hystrix可以保证一个依赖出现问题的时候,不会导致整体的服务出错,避免了级联故障,避免出现链式影响,提高分布式系统的弹性
“断路器”本身是一个开关装置,当某个服务单元出现故障后,通过断路器的故障监控(类似熔断保险丝),来向调用方返回一个备选的可处理的响应(FallBack),而不是去长时间的等待,或者是返回一个调用方无法处理的异常,这样能够保证调用方服务不会长时间的等待,不必要的占用,避免了异常错误在系统中的蔓延,乃至雪崩
1.Hystrix三个重要概念
1.服务降级
服务忙请稍后再试,不让客户端等待,直接返回一个有好的提示,Fallback
什么情况下会发生服务降级?
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池打满触发
2.服务熔断
类似保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级方法返回友好提示
3.服务限流
秒杀等高并发业务,严禁一窝蜂地过来拥挤,限制一次过N个,进行排队,有序进行
2.Hystrix项目构建
①新建一个提供者项目,并导入web所需依赖以及hystrix和eureka的依赖
Hystrix的依赖如下
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
然后新建一个服务类以及一个controller用于测试,各有一个正常方法和一个带有休眠3秒的模拟超时的方法。
service类:
@Service
public class PaymentService {
public String paymentOk(String serverPort){
return "Server Port:"+serverPort+",is OK";
}
public String paymentTimeout(String serverPort){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Server Port:"+serverPort+",is Timeout";
}
}
controller类:
@RestController
@Slf4j
@RequestMapping("/payment")
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@Autowired
private PaymentService paymentService;
/** 正常 */
@GetMapping("/hystrix/ok")
public String paymentOk(){
return paymentService.paymentOk(serverPort);
}
/** 测试超时 */
@GetMapping("/hystrix/timeout")
public String paymentTimeout(){
return paymentService.paymentTimeout(serverPort);
}
}
这时候开启项目,进行测试没有任何问题。接下来使用jmeter进行压力测试。直接设置200*100次请求,这个时候自己再去浏览器进行访问ok方法,发现ok方法也开始卡顿,接下来加入消费者进行远程调用。
②创建消费者项目,并导入eureka、feign、hystrix
eureka等配置跟之前一样,然后创建feign的接口类和控制器类进行测试。
Feign的接口类:
@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE")
public interface FeignService {
@GetMapping("/payment/hystrix/ok")
public String paymentOk();
@GetMapping("/payment/hystrix/timeout")
public String paymentTimeout();
}
controller类:
@RestController
@Slf4j
@RequestMapping("/consumer/payment")
public class OrderController {
/** 注入feign接口 */
@Autowired
FeignService feignService;
@GetMapping("/hystrix/ok")
public String orderOk(){
return feignService.paymentOk();
}
/** 超时控制 */
@GetMapping("/hystrix/timeout")
public String orderTimeout(){
return feignService.paymentTimeout();
}
}
首先正常测试,ok方法一切正常,秒回,当jmeter对提供者的服务进行高并发测试时,消费者远程调用时也非常慢,并且有可能会出现超时的错误。
③解决这些问题
问题1. 超时导致服务器变慢
超时不在一直等待,必须有服务降级,返回一个可处理的响应。
问题2.出错(宕机或应用程序出错)
提供者出错,消费者端不能一直等待,必须有服务降级,有兜底
3.使用服务降级
①提供者服务加入fallback
在提供者的服务上对所需要服务降级的服务加上@HystrixCommand
注解,并指定fallbackMethod
属性为自己设置的用于回调的一个应急方法,并指定commandProperties
属性,这个属性是包括@HystrixProperty
注解的数组,用于指定服务降级所需要的条件。
payment_fallback
方法就是用于出现异常出错的情况,来返回的备用方法,这里的条件设置的是超时2s
@Service
public class PaymentService {
public String paymentOk(String serverPort){
return "Server Port:"+serverPort+",is OK";
}
@HystrixCommand(fallbackMethod = "payment_fallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public String paymentTimeout(String serverPort){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Server Port:"+serverPort+",is Timeout";
}
public String payment_fallback(String serverPort){
return "Server Port:"+serverPort+",is FALLBACK_METHOD..........";
}
}
然后需要在主启动类上加入@EnableCircuitBreaker
注解,开启断路器。
接下来,开启项目,先直接访问这个提供者的方法,进行测试,先不在消费者端测试。超时后,自动给返回了设置的fallback方法,服务降级测试成功。
接下来进行测试如果是运行时出现异常,能否触发服务降级?
首先注释掉sleep休眠语句,然后加入一个会导致运行时异常的语句。
接下来开启项目进行测试。
可以看到,出现运行时异常后,直接触发了服务降级。
②在消费者端加入fallback
一般来说服务降级是在消费者客户端进行设置的,所以接下来在消费者进行配置。
首先需要在主启动类加入@EnableHystrix
注解,开启Hystrix服务,然后在controller类使用@HystrixCommand
注解进行配置服务降级。
controller类:
/** 超时控制 */
@GetMapping("/hystrix/timeout")
@HystrixCommand(fallbackMethod = "orderFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public String orderTimeout(){
return feignService.paymentTimeout();
}
public String orderFallback(){
return "服务繁忙或出现错误,请稍后再试";
}
然后开启项目进行测试,可以看到超时后的确触发了服务降级,返回了fallback方法内容。
但是,将服务提供端的服务休眠时间改成1.5s后,发现没到设置的2s超时,仍旧触发了服务降级,推测是因为,hystrix默认的全局超时时间是1s,然后自己设置的是2s,会自动选择时间短的,也就是1s的超时时间,所以需要在yml配置文件中,修改默认的超时时间,下面修改为3s,这样就会使用自己设置的2s。
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 3000
设置完成后,发送请求,发现的确等待了2s
③配置全局的服务降级
使用@DefaultProperties
注解,加到需要配置的类上,就会给这个类下的加入了@HystrixCommand
注解但是没指定特别的fallback的方法开启默认的全局配置的服务降级,指定DefaultFallback
属性为全局的fallback方法。
@RestController
@Slf4j
@RequestMapping("/consumer/payment")
@DefaultProperties(defaultFallback = "globalFallback")
public class OrderController {
/** 注入feign接口 */
@Autowired
FeignService feignService;
@GetMapping("/hystrix/ok")
@HystrixCommand
public String orderOk(){
int g=1/0;
return feignService.paymentOk();
}
/** 超时控制 */
@GetMapping("/hystrix/timeout")
@HystrixCommand(fallbackMethod = "orderFallback",commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "2000")
})
public String orderTimeout(){
return feignService.paymentTimeout();
}
public String orderFallback(){
return "服务繁忙或出现错误,请稍后再试";
}
public String globalFallback(){
return "全局配置的服务降级生效!";
}
}
在ok方法上加入@HystrixCommand
注解,但是不配置具体属性,然后加入一个导致运行时异常,然后开启项目,进行测试。可以看到,全局配置的fallback被返回了回来。
④对Feign的接口类服务降级
对项目中已有的FeignService这个Feign的接口类,创建一个类(FeignFallbackService)实现该接口,这个类统一的对接口中的方法进行异常处理,并在接口的@FeignClient
注解上配置Fallback属性为该实现类。
Feign的接口类:
@Component
@FeignClient(value = "CLOUD-PAYMENT-HYSTRIX-SERVICE",fallback = FeignFallbackService.class)
public interface FeignService {
@GetMapping("/payment/hystrix/ok")
public String paymentOk();
@GetMapping("/payment/hystrix/timeout")
public String paymentTimeout();
}
实现了Feign的接口类的Fallback类:
@Component
public class FeignFallbackService implements FeignService {
@Override
public String paymentOk() {
return "------------paymentOK method Fallback-----------";
}
@Override
public String paymentTimeout() {
return "------------paymentTimeout method Fallback-----------";
}
}
然后在yml文件中,开启feign对hystrix的支持
feign:
hystrix:
enabled: true
进行测试,直接将服务提供者8011关闭掉,模拟宕机的情况,然后再次调用接口测试,可以看到的确使用了实现类中的对应的方法实现作为fallback方法。
4.服务熔断
什么是服务熔断?
服务熔断是为了应对雪崩效应的一个保护机制,当扇出链路的某一个微服务出现错误或者等待时间过长,会进行服务降级,进而熔断该节点微服务的调用,快速返回错误信息,防止整个链路连锁崩溃。当检测到该节点正常后,会恢复链路。
①在提供者服务添加触发熔断的方法
提供者的服务类:
详细的配置解释请看HystrixCommand配置信息
@HystrixCommand(fallbackMethod = "paymentBreaker_fallback",commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled",value = "true"),//开启熔断器,默认为true
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),//开启熔断器所需要的最小请求
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),//错误请求的百分比,超过这个百分比才能开启
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "5000")//检查是否可用的间隔
})
public String paymentBreaker(@PathVariable("id") Integer id){
if (id<0){
throw new RuntimeException("id不能小于0");
}
return "paymentBreaker成功,id:"+id;
}
public String paymentBreaker_fallback(@PathVariable("id") Integer id){
return "id不能小于0,id:"+id;
}
提供者的controller类:
调用breaker服务进行测试
@GetMapping("/hystrix/breaker/{id}")
public String paymentBreaker(@PathVariable Integer id){
return paymentService.paymentBreaker(id);
}
接下来开启项目,直接访问提供者的接口进行测试。
设置的是最少10次请求,所以快速的访问10次,每次都抛出异常。
10次之后,开启了熔断器这时候,就算传个大于0的值,依旧会快速失败,返回fallback方法。
过几秒后,熔断器进行检测,发现服务正常,再次进行调用,可以看到,熔断器已经关闭,服务正常,这就是熔断器的自动恢复。
②熔断器的三个重要参数
- 快照时间窗:熔断器是否打开需要统计请求和错误的数据,统计的时间范围就是快照时间窗,默认为最近10秒
- 请求总数阈值:在快照时间窗内,请求数至少要满足请求总数阈值,才能打开熔断器,例如:设置为20,在10秒内有19个请求,而且全部错误,一样不会打开熔断器
- 错误百分比阈值:当请求总数在快照时间窗内,错误的请求满足了请求总数的设定的百分比,则打开熔断器,例如:有30个请求,已经超过了请求总数阈值,然后其中15个错误请求,错误百分比阈值设置的是50%,则此时就会打开熔断器
5.图形化Dashboard的搭建
①新建一个项目,并导入相关依赖。
首先需要导入springboot web相关的依赖,然后导入Hystrix的dashboard依赖,dashboard依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
然后配置项目端口为9001,并在yml配置文件加入以下配置:
hystrix:
dashboard:
proxy-stream-allow-list: "*"
接着在主启动类上加入@EnableHystrixDashboard
注解
@SpringBootApplication
@EnableHystrixDashboard
public class CloudConsumerHystrixDashboard9001Application {
public static void main(String[] args) {
SpringApplication.run(CloudConsumerHystrixDashboard9001Application.class, args);
}
}
然后开启项目,输入网址localhost:9001/hystrix进入图形化界面。
需要注意的是需要被监控的项目,一定要有actuator的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
②配置需要被监控的服务
在提供者服务的主启动类中,配置监控的默认地址为/hystrix.stream,代码如下
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableCircuitBreaker
public class CloudProviderHystrixPayment8011Application {
public static void main(String[] args) {
SpringApplication.run(CloudProviderHystrixPayment8011Application.class, args);
}
@Bean
public ServletRegistrationBean getServlet(){
HystrixMetricsStreamServlet hystrix=new HystrixMetricsStreamServlet();
ServletRegistrationBean bean=new ServletRegistrationBean(hystrix);
bean.setLoadOnStartup(1);
bean.addUrlMappings("/hystrix.stream");
bean.setName("HystrixMetricsStreamServlet");
return bean;
}
}
然后开启提供者服务,打开Dashboard,然后输入 项目地址/hystrix.stream
,如下所示
然后点击监控