断路器
Spring Cloud Netflix Hystrix 是Spring Cloud Netflix 子项目的核心组件之一。该框架的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。实现了断路器、线程隔离等一系列服务保护功能。具备服务降级、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。
在微服务架构中,服务与服务之间通过远程调用的方式进行通信,一旦某个被调用的服务发生了故障,其依赖服务也会发生故障,此时就会发生故障的蔓延,最终导致系统瘫痪。Hystrix实现了断路器模式,当某个服务发生故障时,通过断路器的监控,给调用方返回一个错误响应,而不是长时间的等待,这样就不会使得调用方由于长时间得不到响应而占用线程,从而防止故障的蔓延。Hystrix具备服务降级、服务熔断、线程隔离、请求缓存、请求合并及服务监控等强大功能。
Hystrix 特性
- 断路器机制:当Hystrix Command请求后端服务失败数量超过一定比例(默认50%),断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况,如果请求成功,断路器切回闭路状态(CLOSED),否则重新切换到开路状态(OPEN)。Hystrix的断路器就像我们家庭电路中的保险丝,一旦后端服务不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量,并且断路器有自我检测并恢复的能力。
- Fallback:Fallback相当于是降级操作。对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值。 fallback方法的返回值一般是设置的默认值或者来自缓存。
- 资源隔离:在Hystrix中,主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分出多个线程池。例如调用产品服务的Command放入A线程池,调用账户服务的Command放入B线程池。这样做的主要优点是运行环境被隔离开了。这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时,不会对系统的其他服务造成影响。但是带来的代价就是维护多个线程池会对系统带来额外的性能开销。如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话,可以使用Hystrix的信号模式(Semaphores)来隔离资源。
一、准备工作
创建启动一个注册中心,一个服务消费者,一个服务提供者。
请参考:Spring Cloud Eureka 服务注册中心3:Ribbon服务消费者
- piao-server:注册中心服务端,端口2000。
- piao-client:客户端服务提供者,端口2010。
- piao-ribbon:客户端服务消费者,端口2005。
当我们请求消费者接口的时候:http://127.0.0.1:2005/consumer。服务消费者会调用服务提供者的接口,返回服务提供者的端口。但是当我们关闭服务提供者之后,再去调用服务消费者会出现如下异常:
二、使用断路器
修改 piao-ribbon 服务消费者。
2.1、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.2、添加注解
@EnableCircuitBreaker 注解开启断路器功能。
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class PiaoRibbonApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(PiaoRibbonApplication.class, args);
}
}
也可以使用 @SpringCloudApplication 注解来修饰主类。该注解就已经包含了 @SpringBootApplication、@EnableDiscoveryClient、@EnableCircuitBreaker 这三个注解。
2.3、编辑Controller类。
通过 @HystrixCommand 注解来实现服务降级,当服务调用失败会执行指定回调 consumerFallback 方法。
@RestController
public class ConsumerController {
@Autowired
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "consumerFallback")
@GetMapping("/consumer")
public String helloConsumer() {
// 通过服务名调用服务提供者的接口
return restTemplate.getForEntity("http://PIAO-CLIENT/client", String.class).getBody();
}
/**
* 失败时的回调
* @return
*/
public String consumerFallback() {
return "is error...";
}
}
2.4、验证结果
我们重启服务消费者 piao-ribbon。
访问接口地址:http://127.0.0.1:2005/consumer
这里没有报出500异常,执行我们制定的回调方法。
三、设置服务超时触发熔断
改造 piao-client 服务提供者。
Hystrix 默认超时时间为 1000 毫秒,这里我们采用线程休眠 Thread.sleep() 来模拟访问超时。
3.1、编辑Controller类
@RestController
public class ClientController {
@Value("${server.port}")
private String port;
@GetMapping("/client")
public String client() throws InterruptedException {
Thread.sleep(3000);
return "my port is " + port;
}
}
这里我们设置当前线程休眠3秒,然后消费者等待3秒钟。
3.2、启动服务
我们看到 piao-clinet 已经注册进来了。
访问地址:http://127.0.0.1:2005/consumer
我们看到断路器生效成功了。
3.3、配置服务消费者超时时间
现在我们需要修改 piao-ribbon 服务消费者 application.properties 配置文件,配置超时时间为5秒。
#将断路器的超时时长设为5秒
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
重启并访问地址:http://127.0.0.1:2005/consumer
我们等待了3秒多之后,成功访问到了 piao-client 的接口。