熔断器的实现

本文最后更新于 2024年7月25日 上午

熔断器

在一个服务作为调用端调用另外一个服务时,为了防止被调用的服务出现问题而影响到作为调用端的这个服务,这个服务也需要进行自我保护。而最有效的自我保护方式就是熔断。

引出问题

举个例子,假如我要发布一个服务 B,而服务 B 又依赖服务 C,当一个服务 A 来调用服务 B 时,服务 B 的业务逻辑调用服务 C,而这时服务 C 响应超时了,由于服务 B 依赖服务 C,C 超时直接导致 B 的业务逻辑一直等待,而这个时候服务 A 在频繁地调用服务 B,服务 B 就可能会因为堆积大量的请求而导致服务宕机。

这还只是 A->B->C 的情况,试想一下 A->B->C->D->……呢?在整个调用链中,只要中间有一个服务出现问题,都可能会引起上游的所有服务出现一系列的问题,甚至会引起整个调用链的服务都宕机,这是非常恐怖的。

熔断逻辑

熔断设计来源于日常生活中的电路系统,在电路系统中存在一种熔断器(Circuit Breaker),它的作用就是在电流过大时自动切断电路。熔断器一般要实现三个状态:闭合、断开和半开。

熔断器原理

  • 闭合:正常情况,后台会对调用失败次数进行积累,到达一定阈值或比例时则自动启动熔断机制。
  • 断开:一旦对服务的调用失败次数达到一定阈值时,熔断器就会打开,这时候对服务的调用将直接返回一个预定的错误,而不执行真正的网络调用。同时,熔断器需要设置一个固定的时间间隔,当处理请求达到这个时间间隔时会进入半熔断状态。
  • 半开:在半开状态下,熔断器会对通过它的部分请求进行处理,如果对这些请求的成功处理数量达到一定比例则认为服务已恢复正常,就会关闭熔断器,反之就会打开熔断器。

设计思路:在请求失败 N 次后在 X 时间内不再请求,进行熔断;然后再在 X 时间后恢复 M% 的请求,如果 M% 的请求都成功则恢复正常,关闭熔断,否则再熔断 Y 时间,依此循环。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class CircuitBreaker {
//当前状态
private CircuitBreakerState state = CircuitBreakerState.CLOSED;
private AtomicInteger failureCount = new AtomicInteger(0);
private AtomicInteger successCount = new AtomicInteger(0);
private AtomicInteger requestCount = new AtomicInteger(0);
//失败次数阈值
private final int failureThreshold;
//半开启-》关闭状态的成功次数比例
private final double halfOpenSuccessRate;
//恢复时间
private final long retryTimePeriod;
//上一次失败时间
private long lastFailureTime = 0;

public CircuitBreaker(int failureThreshold, double halfOpenSuccessRate,long retryTimePeriod) {
this.failureThreshold = failureThreshold;
this.halfOpenSuccessRate = halfOpenSuccessRate;
this.retryTimePeriod = retryTimePeriod;
}
//查看当前熔断器是否允许请求通过
public synchronized boolean allowRequest() {
long currentTime = System.currentTimeMillis();
switch (state) {
case OPEN:
//进行时间比较,如果满足恢复时间,则变为半开状态
if (currentTime - lastFailureTime > retryTimePeriod) {
state = CircuitBreakerState.HALF_OPEN;
resetCounts();
return true;
}
return false;
case HALF_OPEN:
requestCount.incrementAndGet();
return true;
case CLOSED:
default:
return true;
}
}
//记录成功
public synchronized void recordSuccess() {
if (state == CircuitBreakerState.HALF_OPEN) {
successCount.incrementAndGet();
if (successCount.get() >= halfOpenSuccessRate * requestCount.get()) {
//半开状态下请求成功次数达到预定阈值
state = CircuitBreakerState.CLOSED;
resetCounts();
}
} else {
resetCounts();
}
}
//记录失败
public synchronized void recordFailure() {
failureCount.incrementAndGet();
lastFailureTime = System.currentTimeMillis();
if (state == CircuitBreakerState.HALF_OPEN) {
state = CircuitBreakerState.OPEN;
lastFailureTime = System.currentTimeMillis();
} else if (failureCount.get() >= failureThreshold) {
state = CircuitBreakerState.OPEN;
}
}
//重置次数
private void resetCounts() {
failureCount.set(0);
successCount.set(0);
requestCount.set(0);
}

public CircuitBreakerState getState() {
return state;
}
}

enum CircuitBreakerState {
//关闭,开启,半开启
CLOSED, OPEN, HALF_OPEN
}

参考资料


熔断器的实现
https://love-enough.github.io/2024/07/25/熔断器的实现/
作者
GuoZihan
发布于
2024年7月25日
更新于
2024年7月25日
许可协议