SpringCloud CircuitBreaker是一组用于微服务的断路器API,它底层支持Resilience4j、SpringRetry等多种实现,其中Resilience4j功能相对更为强大,因此实际开发中我们常使用它。
首先我们了解一下什么是微服务架构中的断路器(Circuit Breaker)以及它能够解决的问题。在一个庞大的微服务系统中,假如某一个微服务出现故障,请求的数据迟迟不能返回,这可能造成其它依赖该功能的微服务出现阻塞,甚至耗尽服务器的线程资源,进而造成更大面积的问题。断路器就像保险丝,能够在服务出现问题时及时切断问题根源直接进行服务降级,而不是让其它服务苦苦等待,造成整个系统中的大面积故障。
总而言之,断路器主要能实现以下两个功能:
断路器的工作机制:断路器有3种工作状态,关闭、打开和半打开。一段时间内,某个微服务请求失败次数在一定阈值内,熔断器关闭;超过一定阈值后,说明服务存在故障,熔断器打开,请求该服务将立即失败;处于打开状态的熔断器一段时间后将处于半打开状态,并执行一定量的请求,如果请求成功了,熔断器会再次切换为关闭状态。
这里不得不再补充一些历史。在SpringCloud早期,断路器的实现是Netflix开源的Hystrix,但由于Netflix公司弃坑了该项目,SpringCloud转而选择集成其它实现,而且这里Spring选择了一种更通用的方式,Spring官方定义了一组抽象API即SpringCloud CircuitBreaker,其它库需要实现该API来作为断路器集成到SpringCloud,而Resilience4j就是这样的一种实现。
SpringCloud CircuitBreaker集成Resilience4j的起步依赖库是spring-cloud-starter-circuitbreaker-resilience4j
,不过Resilience4j还有很多额外的功能,因此Resilience4j官方提供了spring-cloud-starter-resilience4j
起步依赖库,如果我们只是用SpringCloud CircuitBreaker API,那么我们引入前者就行了,如果我们需要用到Resilience4j中相比SpringCloud CircuitBreaker多出来的那部分功能,就需要使用后者。
要使用SpringCloud CircuitBreaker和Resilience4j,我们需要引入以下依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
SpringCloud CircuitBreaker能够和SpringCloud OpenFeign集成在一起使用,我们可以在application.properties
中进行配置,下面例子包含一些常用配置。
# 在OpenFeign中启用CircuitBreaker
feign.circuitbreaker.enabled=true
feign.circuitbreaker.alphanumeric-ids.enabled=true
# CircuitBreaker的超时时间
resilience4j.timelimiter.configs.default.timeout-duration=3s
# 断路器打开的失败阈值
resilience4j.circuitbreaker.configs.default.failure-rate-threshold=50
# 断路器打开的最小请求数
resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=10
# 触发熔断后等待多久开始尝试恢复
resilience4j.circuitbreaker.configs.default.wait-duration-in-open-state=5000ms
其中我们用到了很多default
配置,它指定了所有的断路器实例的配置,我们也可以专门配置一个断路器名,例如resilience4j.timelimiter.instances.ProductClient.timeout-duration=1s
,该配置就单独为ProductClient
这个名字对应的断路器进行配置,且具有更高的优先级。
下面例子中,我们使用SpringCloud CircuitBreaker实现了快速失败功能,当order
服务调用productClient.findProductById()
接口触发熔断时,在指定时间内该服务对此接口的调用会直接返回null
。
package com.gacfox.demo.order.controller;
import com.gacfox.demo.product.api.ProductClient;
import com.gacfox.demo.product.model.Product;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.function.Function;
import java.util.function.Supplier;
@RestController
@RequestMapping("/api/v1/order")
public class OrderController {
@Resource
private Resilience4JCircuitBreakerFactory circuitBreakerFactory;
@Resource
private ProductClient productClient;
@GetMapping("/callProductService")
public Product callProductService() {
CircuitBreaker circuitBreaker = circuitBreakerFactory.create("ProductClient");
Supplier<Product> productSupplier = () -> productClient.findProductById(1L);
Function<Throwable, Product> fallbackFunc = throwable -> null;
return circuitBreaker.run(productSupplier, fallbackFunc);
}
}
SpringCloud CircuitBreaker的API设计吸收了很多Java8函数式编程的风格。代码中,我们首先使用Resilience4JCircuitBreakerFactory
工厂类创建了CircuitBreaker
对象,它需要指定一个名字以便与前面application.properties
中的配置关联;之后我们提供了一个Supplier
对象,其中的代码就是用OpenFeign请求微服务接口;我们还创建了一个Function
对象,其中包含的是失败的回退逻辑,这里我们只是简单的直接返回null
,实际开发中可能采用一些其它降级方式,比如返回一个通用的错误对象,或是一个老的缓存等;最后我们调用circuitBreaker.run()
方法,传入Supplier
和回退Function
。
此时SpringCloud CircuitBreaker就生效了,我们可以在ProductClient
对应的微服务中尝试插入一些Thread.sleep()
来模拟接口迟迟不能返回的状况,并不断访问/callProductService
,查看断路器熔断效果。
Resilience4J没有直接提供类似Hystrix的监控图形界面来观察相关的状态信息,但Resilience4J能够集成到Actuator中,我们可以通过监控端点来实时查看Resilience4J的状态,注意这需要引入Actuator的依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在application.properties
进行如下配置后,我们就可以在/actuator/health
中通过Health端点查看断路器的状态。
management.endpoint.health.show-details=always
management.health.circuitbreakers.enabled=true
resilience4j.circuitbreaker.configs.default.register-health-indicator=true
此外,我们还可以通过访问/actuator/metrics
查看到Resilience4J相关的Metrics监控端点名,我们可以访问这些具体的监控端点,来查看Resilience4J的其它状态信息。