resilience4j是github推出的一款弹性架构工具,其中有一项很好用的功能:熔断。
一、服务安装
服务要求JDK版本为JDK 1.8及以上。maven应用安装resilience4j-circuitBreaker服务:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>0.15.0</version>
</dependency>
需要注意的是,release版本和snapshot版本是通过仓库的配置来区分的。
release版本的仓库:
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>jcenter-releases</id>
<name>jcenter</name>
<url>http://jcenter.bintray.com</url>
</repository>
</repositories>
snapshot版本的仓库:
<repositories>
<repository>
<id>jcenter-snapshots</id>
<name>jcenter</name>
<url>http://oss.jfrog.org/artifactory/oss-snapshot-local/</url>
</repository>
</repositories>
二、获取熔断器实例:CircuitBreakerRegistry
熔断的核心是熔断器CircuitBreaker,想要使用熔断服务,需要先获取熔断器的实例。resilience4j提供了两种方式来获取熔断器:通过注册工具获取,以及通过熔断器的静态方法直接获取。这里推荐使用注册工具获取,优点有两个,一方面可以避免重复定制熔断器配置;另一方面便于熔断器实例的管理,避免出现重复创建的情况。
1、通过注册工具获取
resilience4j提供了一个注册工具CircuitBreakerRegistry,用来注册和管理熔断器的实例。CircuitBreakerRegistry的存储是基于ConcurrentHashMap来实现的,可以保证并发情况下的线程安全。
1、生成熔断器配置:
熔断器配置CircuitBreakerConfig相关的内容可以参考文章:CircuitBreakerConfig。这里提供一个定制化熔断器配置的例子:
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.ringBufferSizeInHalfOpenState(2)
.ringBufferSizeInClosedState(2)
.recordExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.enableAutomaticTransitionFromOpenToHalfOpen()
.build();
当然,也可以使用默认的熔断器配置,这样就不需要定制化熔断器了。
2、获取熔断器注册工具:
可以通过CircuitBreakerRegistry的静态方法获取CircuitBreakerRegistry的实例。
这里有两种方式获取注册工具实例。一种是获取默认的CircuitBreakerRegistry实例,默认实例使用默认的熔断器配置;另一种是根据定制化的熔断器配置获取CircuitBreakerRegistry实例,这种实例将会默认使用定制化的熔断器配置。
// 获取默认的熔断器注册工具的实例
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.ofDefaults();
// 根据定制化熔断器配置生成注册工具的实例
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
3、生成熔断器实例:
因为在上一步有两种情况:定制熔断器配置和使用默认配置,所以这一步也有不同的操作,来获取熔断器:
// 根据注册工具的默认配置创建名称为"otherName"的熔断器实例
CircuitBreaker circuitBreaker2 = circuitBreakerRegistry.circuitBreaker("otherName");
// 根据指定的熔断器配置,创建名称为"uniqueName"的熔断器实例
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("uniqueName", circuitBreakerConfig);
// 在创建熔断器实例的时候创建熔断器配置,并根据这里创建的配置去创建名称为"uniqueName"的熔断器实例
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("uniqueName2", () -> generateCircuitBreakerConfig());
这里需要注意的是第三种方式,这种方式的第二个参数是一个Supplier<CircuitBreakerConfig>的实例,即一个返回值类型为CircuitBreakerConfig的无参函数:
CircuitBreaker circuitBreaker(String name, Supplier<CircuitBreakerConfig> circuitBreakerConfigSupplier);
2、通过熔断器的静态方法直接获取
跟上一种方法类似,也有三种方式获取熔断器实例:
// 根据注册工具的默认配置创建名称为"otherName"的熔断器实例
CircuitBreaker defaultCircuitBreaker = CircuitBreaker.ofDefaults("testName");
// 根据指定的熔断器配置,创建名称为"uniqueName"的熔断器实例
CircuitBreaker customCircuitBreaker = CircuitBreaker.of("uniqueName", circuitBreakerConfig);
// 在创建熔断器实例的时候创建熔断器配置,并根据这里创建的配置去创建名称为"uniqueName"的熔断器实例
CircuitBreaker circuitBreaker = CircuitBreaker.of("uniqueName2", () -> generateCircuitBreakerConfig());
三、熔断器配置:CircuitBreakerConfig
熔断器配置请参考文章:CircuitBreakerConfig。
四、熔断器:CircuitBreaker
前面说过,熔断的核心是熔断器CircuitBreaker,用户通过CircuitBreaker的实例来实现熔断功能。
1、为目标方法配置熔断
1.1 基本应用
// 获取CircuitBreaker实例
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
// 装饰目标函数
// 第二个参数就是用户要添加熔断功能的函数,其返回值和CheckedFunction0约束的类型一致
CheckedFunction0<String> decoratedSupplier = CircuitBreaker
.decorateCheckedSupplier(circuitBreaker, () -> "This can be any method which returns: 'Hello");
// 使用Try来从上面的装饰函数中获取结果
Try<String> result = Try.of(decoratedSupplier)
.map(value -> value + " world'");
当然,如果目标函数需要入参,也可以这样实现:
// 获取CircuitBreaker实例
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
// 装饰目标函数
CheckedFunction0<String> decoratedSupplier = CircuitBreaker
.decorateCheckedSupplier(circuitBreaker, () -> "This can be any method which returns: 'Hello");
// 装饰目标函数。这里CheckedFunction1约束了输入参数和返回参数的类型
CheckedFunction1<String, String> decoratedFunction = CircuitBreaker
.decorateCheckedFunction(anotherCircuitBreaker, (input) -> input + " world");
// 使用Try来从上面的装饰函数中获取结果
// 先获取了decoratedSupplier的结果,然后将decoratedSupplier的结果做为入参传入到CheckedFunction1,获取了CheckedFunction1的计算结果
Try<String> result = Try.of(decoratedSupplier)
.mapTry(decoratedFunction::apply);
1.2 CircuitBreaker + RxJava
CircuitBreaker和RxJava的结合,指的是用RxJava的API来替代上面例子中的CheckedFunction0和CheckedFunction1,来对需要配置熔断的目标函数的结果进行装饰。
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("name");
// 实例化Callable,可以用需要熔断的目标函数来替换"callable result"
Callable<String> callable = () -> "callable result";
// 用RxJava的API来进行装饰,返回结果类型是Observable<R>,R是Callable约束的方法
io.reactivex.Observable.fromCallable(callable::call)
.compose(CircuitBreakerOperator.of(circuitBreaker))
1.3 CircuitBreaker + Reactor
和与RxJava结合类似,这里是使用Reactor的API来装饰目标函数的结果。
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
// 实例化Callable,可以用需要熔断的目标函数来替换"callable result"
Callable<String> callable = () -> "callable result";
// 使用Reactor的API来进行装饰,返回结果类型是Mono<V>,V是Callable约束的方法
reactor.core.publisher.Mono.fromCallable(callable::call)
.compose(CircuitBreakerOperator.of(circuitBreaker))
2、熔断器的状态
2.1 State内部枚举类
熔断器状态是CircuitBreaker中定义的一个内部类,源码如下:
enum State {
/** A DISABLED breaker is not operating (no state transition, no events)
and allowing all requests through. */
DISABLED(3, false),
/** A CLOSED breaker is operating normally and allowing
requests through. */
CLOSED(0, true),
/** An OPEN breaker has tripped and will not allow requests
through. */
OPEN(1, true),
/** A FORCED_OPEN breaker is not operating (no state transition, no events)
and not allowing any requests through. */
FORCED_OPEN(4, false),
/** A HALF_OPEN breaker has completed its wait interval
and will allow requests */
HALF_OPEN(2, true);
private final int order;
public final boolean allowPublish;
/**
* Order is a FIXED integer, it should be preserved regardless of the ordinal number of the enumeration.
* While a State.ordinal() does mostly the same, it is prone to changing the order based on how the
* programmer sets the enum. If more states are added the "order" should be preserved. For example, if
* there is a state inserted between CLOSED and HALF_OPEN (say FIXED_OPEN) then the order of HALF_OPEN remains
* at 2 and the new state takes 3 regardless of its order in the enum.
*
* @param order
* @param allowPublish
*/
private State(int order, boolean allowPublish){
this.order = order;
this.allowPublish = allowPublish;
}
public int getOrder(){
return order;
}
}
2.2 熔断器的状态
熔断器有5种状态,其中3中是public的,包括CLOSED,OPEN和HALF_OPEN。初始状态是CLOSED,这时候目标函数可以正常调用。状态转换相关的描述,可以参考文章CircuitBreakerConfig,其中有简要的描述。
熔断器提供了一个方法来查看熔断器当前的状态:
/**
* Returns the state of this CircuitBreaker.
*
* @return the state of this CircuitBreaker
*/
State getState();
2.3 熔断器的状态转换
CircuitBreaker还定义一系列transition方法用于状态转换,可以直接将熔断器转换到目标状态:
2.4 熔断器重置
熔断器提供了一个reset()方法,用于重置熔断器的状态、清空测量指标等:
/**
* Returns the circuit breaker to its original closed state, losing statistics.
*
* Should only be used, when you want to want to fully reset the circuit breaker without creating a new one.
*/
void reset();
3、熔断器的状态转换
2.1 熔断器事件
这里的熔断器事件,指的是目标函数执行触发的结果,包括执行成功和执行失败,所以事件也就包括两种。
// 执行这个方法一次,熔断器就记录一次失败的目标函数调用
// 入参durationInNanos指的是目标函数执行消耗的实际,单位是纳秒
// 入参throwable指的是目标函数抛出的异常
void onError(long durationInNanos, Throwable throwable);
// 执行这个方法一次,熔断器就记录一次成功的目标函数调用
void onSuccess(long durationInNanos);
我们可以通过调用这两个函数来手动模拟熔断器熔断的场景进行测试:
// 熔断器配置,这里将触发熔断器开启的缓冲器大小设置为2,也就是说目标函数调用失败2次就会导致熔断器开启
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.ringBufferSizeInClosedState(2)
.waitDurationInOpenState(Duration.ofMillis(1000))
.build();
CircuitBreaker circuitBreaker = CircuitBreaker.of("testName", circuitBreakerConfig);
// 确定初始状态熔断器是关闭的
assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.CLOSED);
// 模拟两次目标函数调用失败
circuitBreaker.onError(0, new RuntimeException());
circuitBreaker.onError(0, new RuntimeException());
// 检查熔断器是否开启(开启)
assertThat(circuitBreaker.getState()).isEqualTo(CircuitBreaker.State.OPEN);
// 调用配置了熔断器的目标方法,测试开启状态的熔断器是否能使目标函数调用成功
Try<String> result = Try.of(CircuitBreaker.decorateCheckedSupplier(circuitBreaker, () -> "Hello"))
.map(value -> value + " world");
// 检测目标函数能否调用成功(调用失败)
assertThat(result.isFailure()).isTrue();
// 异常的类型是CircuitBreakerOpenException
assertThat(result.failed().get()).isInstanceOf(CircuitBreakerOpenException.class);
4、熔断器的事件发布者
EventPublisher是CircuitBreaker内部定义的一个接口。当CircuitBreaker发生一些时间的时候,EventPublisher中的相应方法就会被回调。用户可以通过获取CircuitBreaker中成员属性EventPublisher的实例,并复写该实例的相应方法,来实现在特定事件发生的时候进行特定的操作。
4.1 内部接口EventPublisher
/**
* An EventPublisher can be used to register event consumers.
*/
interface EventPublisher extends io.github.resilience4j.core.EventPublisher<CircuitBreakerEvent> {
EventPublisher onSuccess(EventConsumer<CircuitBreakerOnSuccessEvent> eventConsumer);
EventPublisher onError(EventConsumer<CircuitBreakerOnErrorEvent> eventConsumer);
EventPublisher onStateTransition(EventConsumer<CircuitBreakerOnStateTransitionEvent> eventConsumer);
EventPublisher onReset(EventConsumer<CircuitBreakerOnResetEvent> eventConsumer);
EventPublisher onIgnoredError(EventConsumer<CircuitBreakerOnIgnoredErrorEvent> eventConsumer);
EventPublisher onCallNotPermitted(EventConsumer<CircuitBreakerOnCallNotPermittedEvent> eventConsumer);
}
4.2 熔断器获取事件发布者
CircuitBreaker定义了一个方法,用来获取其成员属性事件发布者:
/**
* Returns an EventPublisher which can be used to register event consumers.
*
* @return an EventPublisher
*/
EventPublisher getEventPublisher();
4.3 EventPublisher应用:
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
circuitBreaker.getEventPublisher.onStateTransition(circuitBreakerOnStateTransitionEvent -> {
if (State.OPEN == circuitBreakerOnStateTransitionEvent.getStateTransition().getToState()) {
System.out.println("Circuit Breaker is OPEN!");
}
});
5、熔断器的指标
Metrics也是CircuitBreaker内部定义的一个接口,主要用来记录熔断器的测量指标。
5.1 内部接口Metrics:
Metrics中定义了一系列get方法,可以通过这些get方法获取当前熔断器的指标。
interface Metrics {
/**
* Returns the failure rate in percentage. If the number of measured calls is below the minimum number of measured calls,
* it returns -1.
*
* @return the failure rate in percentage
*/
float getFailureRate();
/**
* Returns the current number of buffered calls.
*
* @return he current number of buffered calls
*/
int getNumberOfBufferedCalls();
/**
* Returns the current number of failed calls.
*
* @return the current number of failed calls
*/
int getNumberOfFailedCalls();
/**
* Returns the current number of not permitted calls, when the state is OPEN.
*
* The number of denied calls is always 0, when the CircuitBreaker state is CLOSED or HALF_OPEN.
* The number of denied calls is only increased when the CircuitBreaker state is OPEN.
*
* @return the current number of not permitted calls
*/
long getNumberOfNotPermittedCalls();
/**
* Returns the maximum number of buffered calls.
*
* @return the maximum number of buffered calls
*/
int getMaxNumberOfBufferedCalls();
/**
* Returns the current number of successful calls.
*
* @return the current number of successful calls
*/
int getNumberOfSuccessfulCalls();
}
5.2 熔断器获取指标
CircuitBreaker定义了一个方法,用来获取其成员属性指标:
/**
* Returns the Metrics of this CircuitBreaker.
*
* @return the Metrics of this CircuitBreaker
*/
Metrics getMetrics();