微服务设计模式 – circuit breaker
circuit breaker 熔断器,在很多不同的领域都有这个定义,例如电路里面的熔断器,股票行业里面的熔断,当然我们这里的熔断指的是在计算机编程里面的,设计模式里的”熔断”。
简单的说,这就是一个状态机,然后状态机之间的转换逻辑;这个状态机有三个状态,分别是:
- 开 open
- 关 closed
- 半开 half-open
这三个状态的转换关系,如下图:即正常状态下,熔断器是处于关闭的状态,内部维护一个错误数计数器,如果有错误发生,就增加这个计数器,直到 一段时间内错误率达到或者超过阈值(failure threshold),此时熔断器打开,处于开的状态。熔断器开的状态下,所有新的操作都不进行了,这是为了给集群一点时间进行恢复,开启状态会有一个超时时间,超过这个时间之后,熔断器转为半开的状态;在半开状态下,熔断器会允许一定数量的操作,如果这些操作全部成功,那么熔断器会重置错误计数器,并回到关闭的状态;如果有一次失败了,那么熔断器又会变为开的状态,开启下一次超时计时。
熔断器的思想也非常简单,只需要将你想要保护的方法包裹在一个熔断器里面,一旦熔断器开启了,后续请求都将返回error,或者其他降级处理,而根本不会再执行你保护的这个方法。一般熔断器开启也会配合告警一起使用。
参考资料:
circuit breaker
Resilience4j – circuit breaker
这里介绍一些resilience4j中的circuit breaker的基础用法
maven 引入
io.github.resilience4j
resilience4j-all
1.7.0
circuit breaker 初始化
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(20)
.waitDurationInOpenState(Duration.ofMillis(500))
.slidingWindowSize(5)
.permittedNumberOfCallsInHalfOpenState(3)
.recordException(new Predicate() {
@Override
public boolean test(Throwable throwable) {
return true;
}
})
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.build();
//注册
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("test");
几个熔断器的关键参数:
- failureRateThreshold 即失败阈值的百分比,默认是50
- waitDurationInOpenState 即熔断器开启时的超时时间设置
- slidingWindowSize 即上文所述一段时间的这个一段时间的定义
- permittedNumberOfCallsInHalfOpenState 即半开状态允许执行的操作数
- recordException 定义一个Predicate,来判断是否发生的一个exception需要被错误计数器计数,这里直接返回true,就是任何exception发生,都会增加错误计数器
- automaticTransitionFromOpenToHalfOpenEnabled 顾名思义,就是是否允许熔断器自动从开启状态超时后转到半开状态
保护我们的方法
假设我们需要保护的方法是:
public class RemoteService{
public CompletableFuture echoName(String name){
System.out.println(name);
return CompletableFuture.completedFuture(name);
}
}
用circuit breaker 包裹一下:
String name = "rachel";
RemoteService remoteService = new RemoteService();
CheckedFunction0> checkedSupplier = circuitBreaker.decorateCheckedSupplier(()->remoteService.echoName(name));
Try> result = Try.of(checkedSupplier)
.recover(CallNotPermittedException.class, throwable ->{
System.out.println("熔断器已经打开,拒绝访问被保护方法~");
return CompletableFuture.completedFuture("not permit");
});
recover
javadoc文档上recover的介绍是: 此处提供了让我们可以从错误发生后进行降级处理等的地方~
一个小验证
package com.example.http.resilience4j;
import io.github.resilience4j.circuitbreaker.CallNotPermittedException;
import io.github.resilience4j.circuitbreaker.CircuitBreaker;
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.vavr.CheckedFunction0;
import io.vavr.control.Try;
import org.junit.jupiter.api.Test;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
public class CircuitBreakerTest {
private EchoService echoService;
private CircuitBreakerRegistry circuitBreakerRegistry;
public class EchoService {
private AtomicInteger count = new AtomicInteger(0);
public CompletableFuture echoName(String name) {
int num = count.getAndIncrement();
if (num % 3 == 1) {
throw new RuntimeException(name);
}
System.out.println("hello " + name + " ~ " + num);
return CompletableFuture.completedFuture(name);
}
}
@Test
public void testCircuitBreaker() throws Throwable {
//配置
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(20)
.waitDurationInOpenState(Duration.ofMillis(500))
.slidingWindowSize(5)
.permittedNumberOfCallsInHalfOpenState(3)
.recordException(new Predicate() {
@Override
public boolean test(Throwable throwable) {
return true;
}
})
.automaticTransitionFromOpenToHalfOpenEnabled(true)
.build();
//注册
circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
echoService = new EchoService();
for(int i= 0;i echoWrapper(String name) {
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker(name);
CheckedFunction0> checkedSupplier = circuitBreaker.decorateCheckedSupplier(()->echoService.echoName(name));
Try> result = Try.of(checkedSupplier)
.recover(CallNotPermittedException.class, throwable ->{
System.out.println("熔断器已经打开,拒绝访问被保护方法~");
getCircuitBreakerStatus("熔断器打开中:", circuitBreaker);
return CompletableFuture.completedFuture("not permit");
}).recover(throwable -> {
System.out.println(throwable.getLocalizedMessage() + ",方法被降级了~~");
getCircuitBreakerStatus("降级方法中:",circuitBreaker);
if("jojo".equals(name)){
return CompletableFuture.completedFuture("no available students");
}
return echoWrapper("jojo");
});
getCircuitBreakerStatus(circuitBreaker.getName() + "执行结束后:",circuitBreaker);
return result.get();
}
public static void getCircuitBreakerStatus(String msg, CircuitBreaker circuitBreaker){
CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
// Returns the failure rate in percentage.
float failureRate = metrics.getFailureRate();
// Returns the current number of buffered calls.
int bufferedCalls = metrics.getNumberOfBufferedCalls();
// Returns the current number of failed calls.
int failedCalls = metrics.getNumberOfFailedCalls();
// Returns the current number of successed calls.
int successCalls = metrics.getNumberOfSuccessfulCalls();
// Returns the current number of not permitted calls.
long notPermittedCalls = metrics.getNumberOfNotPermittedCalls();
System.out.println(msg + "state=" +circuitBreaker.getState() + " , metrics[ failureRate=" + failureRate +
", bufferedCalls=" + bufferedCalls +
", failedCalls=" + failedCalls +
", successCalls=" + successCalls +
", notPermittedCalls=" + notPermittedCalls +
" ]"
);
}
}
结果:
Process finished with exit code 0
Original: https://www.cnblogs.com/rachel-aoao/p/resilience4j_circuitbreaker.html
Author: rachel_aoao
Title: Resilience4j 实践
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/601482/
转载文章受原作者版权保护。转载请注明原作者出处!