系统运行过程中,如果服务提供者响应非常缓慢,那么消费者对提供者的请求就会被强制等待,直到提供者响应或超时。在高负载情况下,如果不做任何处理,此类问题可能会导致服务消费者的资源耗竭甚至整个系统的崩溃。
一、雪崩效应
把“基础服务故障”导致“级联故障”的现象称为雪崩效应。雪崩效应描述的是提供者不可用导致消费者不可用,并将不可用逐渐放大的过程。
产生原因:在应用中,默认所有的请求都使用一个线程池处理,如下
圈住的是线程池名称,后面的数字是线程编号。
这样的话,假如一个服务提供者崩溃了或者响应很慢,就会导致消费者调用这个服务的线程一直等待,它的请求多了之后,逐渐地就把线程池中的线程都占用了。然后,消费者调用其它正常的提供者的服务时,也需要等到有空闲的线程才能处理。
二、如何容错
容错机制需要实现以下两点
- 为网络请求设置超时:让资源尽快释放
- 使用断路器模式:快速失败
三、使用Hystrix实现容错
1、Hystrix简介
Hystrix是由NetFlix开源的一个延迟和容错库,用于隔离访问远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性和容错性。Hystrix主要通过以下几点实现延迟和容错
- 包裹请求:使用HystrixCommand(或者HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中执行。这使用到了设计模式种的“命令模式”。
- 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或者手动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池(或者信号量)。如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加快失败判定。
- 监控:Hystrix可以近乎实时地监控运行指标和配置的变化,例如成功、失败、超时、以及被拒绝的请求等。
- 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可由开发人员自行提供,例如返回一个缺省值。
- 自我修复:当依赖的服务不正常时打开断路器快速失败,从而防止雪崩效应;当发现依赖的服务恢复正常时,又会恢复请求。
2、通用方式整合Hystrix
1)项目的pom.xml文件引入Hystrix的依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2)在启动类添加注解@EnableHystrix或@EnableCircuitBreaker,从而为项目启用断路器支持
3)修改服务消费者的OrderController,增加一个API方法以及它的回退方法。
@GetMapping("/order/hystrix_pay")
@HystrixCommand(fallbackMethod="hystrixPayFallback"
// commandProperties = {@HystrixProperty(name="属性名", value="属性值")}
)
public String hystrixPay() {
System.out.println("订单服务开始调用支付服务...");
return restTemplate.getForObject("http://provider-pay/pay", String.class);
}
public String hystrixPayFallback() {
return "目标服务请求失败啦!";
}
API方法加上了@HystrixCommand注解,并设置fallbackMethod属性为回退方法名称。@HystrixCommand还可以通过commandProperties属性设置更多的配置,具体可以设置哪些配置,大家可以网上去找或者通过在com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager类的initializeProperties方法打断点查看。
4)停掉服务提供者项目,启动这个项目,浏览器访问http://localhost:8082/order/hystrix_pay,会展示如下结果
说明当微服务不可用时,进入了回退方法。
3、Hystrix断路器的监控与深入理解
在pom.xml引入如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
重新启动项目,访问http://localhost:8082/actuator/health可以看到断路器的状态。
强调一下:执行回退逻辑并不代表断路器已经打开(因为可能还没到达阈值)。请求失败、超时、被拒绝以及断路器打开时等都会执行回退逻辑。
4、Hystrix线程隔离策略与传播上下文
Hystrix的隔离策略有两种
THREAD(线程隔离):使用该方式,HystrixCommand将会在单独的线程上执行,并发请求受线程池中的线程数量限制。
SEMAPHOEW(信号量隔离):使用该方式,HystrixCommand将会在调用线程上执行,开销相对较小,并发请求受到信号量个数的限制。
Hystrix默认推荐使用线程隔离,因为这种方式有一个除网络超时以外的额外保护层。如果发生找不到上下文的运行时异常,可考虑将隔离策略设置为信号量隔离。
可使用execution.isolation.strategy属性指定隔离策略。
5、Feign使用Hystrix
将之前编写的feign接口修改成如下内容
/**
* 支付服务的feign客户端
* 用fallback属性指定回退类
* @author z_hh
* @time 2019年2月17日
*/
@FeignClient(value="provider-pay", fallback=PayFeignClientFallback.class)
public interface PayFeignClient {
@GetMapping("/pay_with_params")
public String payWithParams(@RequestParam String orderCode, @RequestParam int totalPrice);
}
/**
* PayFeignClient对应的回退类,需要实现PayFeignClient接口
* @author z_hh
* @time 2019年2月17日
*/
@Component
class PayFeignClientFallback implements PayFeignClient {
@Override
public String payWithParams(String orderCode, int totalPrice) {
return "目标服务请求失败啦!";
}
}
配置文件增加一以下内容。增加超时时间的设置(feign默认1秒钟超时),并打开feign-hystrix(默认是关),一般可以将hystrix的超时时间设置的比feign的超时时间长一些,否则feign的重试(如果配置了)将会失效。
feign:
client:
config:
default:
connectTimeout: 7000
readTimeout: 7000
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 8000
停掉服务提供者项目,启动这个项目,浏览器访问http://localhost:8082/order/feign_pay,同样会展示如下结果
说明当微服务不可用时,进入了回退方法。
6、通过Fallback Factory检查回退原因
很多场景下,需要了解回退的原因。
将Feign接口改为以下内容
/**
* 支付服务的feign客户端
* @author z_hh
* @time 2019年2月17日
*/
@FeignClient(value="provider-pay", fallbackFactory=PayFeignClientFallbackFactory.class)
public interface PayFeignClient3 {
@GetMapping("/pay_with_params")
public String payWithParams(@RequestParam String orderCode, @RequestParam int totalPrice);
}
/**
* 实现FallbackFactory接口,并重写create方法
* @author z_hh
* @time 2019年2月17日
*/
@Component
class PayFeignClientFallbackFactory implements FallbackFactory<PayFeignClient3> {
@Override
public PayFeignClient3 create(Throwable t) {
return new PayFeignClient3() {
// 回退方法
@Override
public String payWithParams(String orderCode, int totalPrice) {
return "目标服务请求失败啦!原因:" + t.getMessage();
}
};
}
}
日志最好放在各个fallback方法中,而不要直接放在create方法中。
停掉服务提供者项目,启动这个项目,浏览器访问http://localhost:8082/order/feign_pay,会展示如下结果
说明当微服务不可用时,进入了回退方法,并获取到了回退的原因。
7、为Feign禁用Hystrix
配置文件将feign.hystrix.enabled设置为false。
四、使用Hystrix Dashboard可视化监控数据
1)新建一个maven项目microservice-hystrix-dashboard,pom.xml引入以下依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.zhh</groupId>
<artifactId>microservice-hystrix-dashboard</artifactId>
<version>1.0</version>
<!-- Spring Boot依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
<!-- Spring Cloud管理依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M7</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- hystrix-dashboard -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<!-- 注意: 这里必须要添加, 否则各种依赖有问题 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2)配置文件指定服务端口。
server:
port: 8083
3)启动类增加@EnableHystrixDashboard注解。
@SpringBootApplication
@EnableHystrixDashboard
public class MicroserviceHystrixDashboardApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceHystrixDashboardApplication.class, args);
}
}
4)启动项目,访问http://localhost:8083/actuator/hystrix,看到如下页面说明成功了。
五、使用Turbine聚合监控数据
前面使用/hystrix.stream端点只能监控单个微服务实例,每次查看需要在Hystrix Dashboard上切换想要监控的地址,这显然很不方便。
Turbine是一个聚合Hystrix监控数据的工具,它可将所有相关/hystrix.stream端点的数据聚合到一个组合的/turbine.stream中,从而让集群的监控更加方便。还可以使用消息中间件收集数据。
这里不做详细讲解了。