稳定性设计
1. 这是什么
稳定性设计是指系统面对异常流量、依赖故障、资源不足和局部失效时,仍能保持核心能力可用的一套设计方法。
它关注的是系统在坏情况下如何尽量不失控。
一句话理解:
- 稳定性设计不是为了“永不出错”
- 而是为了“出错时别把整套系统一起拖下去”
2. 为什么重要
真实系统一定会遇到故障。
稳定性设计做得越早,系统面对突发问题时就越可控。
最常见的稳定性事故通常都不是单点功能 bug,而是:
- 流量过高
- 下游变慢
- 重试风暴
- 资源被打满
所以稳定性设计本质上是在做:
- 风险控制
- 影响范围控制
3. 先建立直觉:稳定性不是一个开关,而是一组保护动作
很多人会把稳定性理解成:
- “加个限流”
其实远远不够。
真正的稳定性体系通常由这些动作组合起来:
- 限流
- 超时
- 重试
- 熔断
- 降级
- 隔离
它们解决的问题并不相同。
4. 核心内容
4.1 限流
限流解决的是:
- 流量超过系统承载能力时,如何控制入口压力
它的本质不是“不让别人用”,而是:
- 用一部分请求被拒绝,换整个系统不崩
4.2 超时控制
超时控制解决的是:
- 依赖慢了以后,不能一直傻等
如果没有超时:
- 线程会被长时间占用
- 上游请求会继续堆积
4.3 重试策略
重试适合:
- 临时性失败
但重试必须有边界,因为如果失败是持续性的,再重试只会:
- 放大压力
- 雪上加霜
4.4 熔断
熔断解决的是:
- 当某个依赖明显持续失败时,先临时切断调用,避免继续拖垮系统
它更像电路里的保险丝。
4.5 降级
降级解决的是:
- 核心功能优先,非核心能力可以暂时退化
例如:
- 推荐服务挂了,但核心下单还能继续
- 详情页评论区暂时不展示,但商品主信息仍可访问
4.6 隔离
隔离解决的是:
- 一个模块出问题,不要把别的模块也拖死
常见隔离方式包括:
- 独立线程池
- 独立连接池
- 独立资源配额
5. 学习重点
这一章最重要的是掌握:
- 稳定性设计的核心是控制影响范围
- 限流、超时、重试、熔断、降级、隔离各自解决不同问题
- 重试不是无脑增强手段
- 降级和熔断本质上都是系统自我保护
6. 常见问题
6.1 没有限流导致流量高峰把服务打垮
入口没有保护时,系统会直接被高峰流量压穿。
6.2 依赖慢却没有超时控制
下游一慢,上游线程就会被拖住,问题很快扩散。
6.3 失败就无限重试导致雪上加霜
这是非常典型的稳定性反模式。
7. 动手验证
这一节我用一个纯 Java demo,把“限流、有限重试、熔断后降级”这几个关键动作直接跑出来。
7.1 准备一个可运行示例
新建文件 StabilityDesignDemo.java,内容如下:
java
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
public class StabilityDesignDemo {
static class SimpleRateLimiter {
private final Semaphore semaphore;
SimpleRateLimiter(int permits) {
this.semaphore = new Semaphore(permits);
}
boolean tryAcquire() {
return semaphore.tryAcquire();
}
void release() {
semaphore.release();
}
}
static class RetryExecutor {
String executeWithRetry(int maxAttempts, Task task) {
for (int i = 1; i <= maxAttempts; i++) {
try {
return task.run(i);
} catch (RuntimeException e) {
if (i == maxAttempts) {
throw e;
}
}
}
throw new IllegalStateException("unreachable");
}
}
interface Task {
String run(int attempt);
}
static class CircuitBreaker {
private final int threshold;
private int failures;
private boolean open;
CircuitBreaker(int threshold) {
this.threshold = threshold;
}
String execute(Task task, String fallback) {
if (open) {
return fallback;
}
try {
return task.run(1);
} catch (RuntimeException e) {
failures++;
if (failures >= threshold) {
open = true;
}
throw e;
}
}
boolean isOpen() {
return open;
}
}
public static void main(String[] args) {
SimpleRateLimiter limiter = new SimpleRateLimiter(2);
boolean first = limiter.tryAcquire();
boolean second = limiter.tryAcquire();
boolean third = limiter.tryAcquire();
System.out.println("rateLimitFirst=" + first);
System.out.println("rateLimitSecond=" + second);
System.out.println("rateLimitThird=" + third);
limiter.release();
limiter.release();
RetryExecutor retryExecutor = new RetryExecutor();
AtomicInteger attempts = new AtomicInteger(0);
String retryResult = retryExecutor.executeWithRetry(3, attempt -> {
attempts.incrementAndGet();
if (attempt < 3) {
throw new RuntimeException("temporary fail");
}
return "retry-success";
});
System.out.println("retryAttempts=" + attempts.get());
System.out.println("retryResult=" + retryResult);
CircuitBreaker breaker = new CircuitBreaker(2);
for (int i = 0; i < 2; i++) {
try {
breaker.execute(attempt -> {
throw new RuntimeException("downstream fail");
}, "fallback-response");
} catch (Exception e) {
System.out.println("breakerFailure=" + e.getMessage());
}
}
String degraded = breaker.execute(attempt -> "should-not-run", "fallback-response");
System.out.println("breakerOpen=" + breaker.isOpen());
System.out.println("degradedResult=" + degraded);
}
}7.2 编译并运行
bash
javac StabilityDesignDemo.java
java StabilityDesignDemo7.3 你应该观察到什么
输出应包含这些关键信息:
text
rateLimitFirst=true
rateLimitSecond=true
rateLimitThird=false
retryAttempts=3
retryResult=retry-success
breakerFailure=downstream fail
breakerOpen=true
degradedResult=fallback-response7.4 每一行在验证什么
rateLimitThird=false:说明限流的作用是超额请求直接被挡住retryAttempts=3、retryResult=retry-success:说明重试应该有限且面向临时失败breakerFailure=downstream fail:说明熔断前仍会记录真实失败breakerOpen=true:说明连续失败达到阈值后,熔断器进入打开状态degradedResult=fallback-response:说明熔断打开后应优先走降级兜底,而不是继续打下游
8. 练习建议
下面这些练习做完,这一章会更扎实:
- 设计一个接口限流方案
- 分析某个依赖服务变慢时的保护措施
- 总结超时、重试、熔断之间的配合关系
- 画一张“故障扩散路径图”,看哪些地方应该做隔离
9. 自测问题
- 限流、熔断、降级分别适合解决什么问题?
- 为什么失败重试必须有边界?
- 稳定性设计为什么本质上是在做风险控制?
- 为什么隔离能阻止问题扩散?
10. 自测核对要点
如果你的回答能覆盖下面这些点,说明这一章基本掌握到位了:
- 稳定性设计的核心是控制影响范围和资源消耗
- 限流用于控制入口压力
- 超时避免线程和资源长期被占住
- 重试只适合部分临时失败场景,而且必须有限
- 熔断和降级是系统自我保护的重要手段