熔断器
本页是熔断、隔离和降级的基础导读。HystrixCommand内部执行、线程/信号量隔离、Rolling Metrics、取消边界以及迁移到Resilience4j的完整过程,请继续阅读:
Hystrix 用来解决微服务中的故障扩散问题。一个服务调用另一个服务时,如果下游响应变慢或持续失败,上游线程会被大量占用,最终可能把整个系统拖垮。这种现象通常叫服务雪崩。
Hystrix 提供超时、隔离、熔断、降级等能力,让系统在部分服务异常时仍能保持基本可用。
阅读入口:先理解故障如何被隔离
按“资源隔离 → 超时 → Rolling Metrics → 熔断状态 → fallback”阅读。先掌握 Hystrix 的历史模型,再对照 Resilience4j 的模块化实现和迁移边界。
核心原理:命令封装、资源隔离与熔断统计
Hystrix 把远程调用封装成 HystrixCommand,根据配置选择线程池隔离或信号量隔离;执行结果进入滚动统计窗口,错误率达到阈值后打开熔断器,后续请求直接走 fallback,等待休眠窗口后再半开探测。
flowchart TD
A["HystrixCommand.execute"] --> B["线程池/信号量隔离"]
B --> C{"熔断器是否 OPEN?"}
C -- "是" --> D["短路并执行fallback"]
C -- "否" --> E["执行远程调用"]
E --> F["超时/异常/成功写入Rolling Metrics"]
F --> G{"达到失败阈值?"}
G -- "是" --> H["OPEN,拒绝新调用"]
G -- "否" --> I["返回正常结果"]
H --> J["休眠窗口后HALF_OPEN探测"]线程隔离能限制故障线程池,不能撤销已经提交的下游写操作;fallback 只能返回业务允许的兜底,不能把扣款或扣库存伪装成成功。
服务雪崩流程
flowchart TD
A[用户请求进入订单服务] --> B[订单服务调用库存服务]
B --> C{库存服务变慢}
C --> D[订单服务线程阻塞]
D --> E[线程池被占满]
E --> F[订单服务也不可用]
F --> G[故障继续向上游扩散]Hystrix 如何保护系统
flowchart TD
A[发起远程调用] --> B[进入隔离资源]
B --> C{是否超时或失败过多?}
C -->|否| D[正常返回]
C -->|是| E[打开熔断器]
E --> F[直接执行降级逻辑]
F --> G[快速返回]核心概念
超时
远程调用不能无限等待。Hystrix 会为调用设置超时时间,超过时间直接失败并进入降级逻辑。
隔离
隔离是为了限制故障影响范围。常见方式:
- 线程池隔离:不同下游使用不同线程池,一个下游慢不会占满所有业务线程。
- 信号量隔离:限制并发数,开销较小,但隔离能力弱于线程池。
熔断
当某个下游服务失败率达到阈值时,Hystrix 会打开熔断器。熔断打开后,请求不再真正调用下游,而是直接走降级逻辑。
经过一段时间后,熔断器进入半开状态,放少量请求试探。如果成功,关闭熔断;如果失败,继续打开。
flowchart TD
A["CLOSED正常放行"] --> B{"失败率和请求量达到阈值"}
B -- "否" --> A
B -- "是" --> C["OPEN快速失败"]
C --> D["等待Sleep Window"]
D --> E["允许单个或少量试探请求"]
E --> F{"试探是否成功"}
F -- "成功" --> A
F -- "失败" --> C降级
降级是在失败时返回可接受的结果。例如:
- 商品评价服务不可用时,返回空评价。
- 推荐服务不可用时,返回默认推荐。
- 库存服务不可用时,提示稍后重试。
降级要符合业务语义,不能把失败伪装成成功。
开发建议
- 所有远程调用都要设置超时。
- 重要下游要配置隔离资源,避免互相影响。
- 降级逻辑要简单,不要在 fallback 中再调用一堆不稳定服务。
- 写操作降级要谨慎,不能默认成功。
- 监控熔断次数、失败率、超时数量和线程池使用率。
Hystrix 通常用于保护 Feign 远程调用,和 Ribbon 负载均衡一起构成服务调用的稳定性基础。
代码 Demo:HystrixCommand 降级
public class StockCommand extends HystrixCommand<String> {
private final RestTemplate restTemplate;
private final Long productId;
public StockCommand(RestTemplate restTemplate, Long productId) {
super(HystrixCommandGroupKey.Factory.asKey("stock"));
this.restTemplate = restTemplate;
this.productId = productId;
}
@Override
protected String run() {
return restTemplate.getForObject(
"http://inventory-service/api/stocks/" + productId,
String.class
);
}
@Override
protected String getFallback() {
return "{\"available\":false,\"message\":\"库存服务暂不可用\"}";
}
}使用:
String result = new StockCommand(restTemplate, 1001L).execute();实际新项目更多会使用 Sentinel 或 Resilience4j,但 Hystrix 的超时、隔离、熔断、降级思想仍然值得掌握。
Hystrix、Sentinel、Resilience4j 对比
Spring Cloud 容错治理经历过几个常见阶段:
Hystrix 经典熔断隔离
-> Sentinel 流量治理
-> Resilience4j 轻量模块化容错组件| 对比项 | Hystrix | Sentinel | Resilience4j |
|---|---|---|---|
| 主要定位 | 熔断、隔离、降级 | 限流、熔断、热点参数、系统保护 | 熔断、限流、重试、隔离、超时 |
| 常见生态 | Spring Cloud Netflix 老项目 | Spring Cloud Alibaba 常见 | Spring Cloud CircuitBreaker 常见 |
| 隔离方式 | 线程池隔离、信号量隔离 | 并发线程数控制、规则拦截 | Bulkhead、ThreadPoolBulkhead |
| 限流能力 | 较弱 | 很强 | 有 RateLimiter 模块 |
| 控制台 | Hystrix Dashboard | Sentinel Dashboard | 通常结合 Actuator/Micrometer |
| 当前建议 | 老项目维护理解 | 流量治理强需求优先考虑 | Spring 官方生态和轻量场景常用 |
三者的共同目标都是防止故障扩散,但关注点不同:
- Hystrix 更强调隔离和熔断。
- Sentinel 更强调实时流量控制和规则治理。
- Resilience4j 更强调轻量、模块化、函数式组合。
Resilience4j 的核心模块
Resilience4j 不是一个单一熔断器,而是一组容错组件。
本节只用于从Hystrix过渡到现代组件。Java 17、Spring Boot 3、Spring Cloud CircuitBreaker、滑动窗口Subtract-on-Evict、状态并发转换、Spring AOP顺序、Cloud Factory真实执行链和商业Demo统一进入Resilience4j独立专栏,不要把下面的概念示例当成完整生产实现。
| 模块 | 作用 |
|---|---|
| CircuitBreaker | 熔断器,根据失败率或慢调用比例打开熔断 |
| RateLimiter | 限流,控制单位时间允许多少请求 |
| Retry | 重试,处理短暂失败 |
| Bulkhead | 舱壁隔离,限制并发调用数 |
| TimeLimiter | 超时控制,限制异步调用等待时间 |
它的思想是把不同能力拆成小模块,需要什么组合什么。
flowchart TD
A["业务调用"] --> B["RateLimiter 限流"]
B --> C["Bulkhead 隔离"]
C --> D["CircuitBreaker 熔断判断"]
D --> E["Retry 有限重试"]
E --> F["TimeLimiter 超时控制"]
F --> G["调用下游服务"]Resilience4j 熔断状态
Resilience4j 的熔断器也有类似状态:
flowchart TD
A["CLOSED 正常放行"] --> B{"失败率或慢调用比例超阈值"}
B -->|"否"| A
B -->|"是"| C["OPEN 快速拒绝"]
C --> D["等待一段时间"]
D --> E["HALF_OPEN 放行少量探测请求"]
E --> F{"探测成功"}
F -->|"是"| A
F -->|"否"| C核心指标通常包括:
- 失败率。
- 慢调用比例。
- 最小调用数量。
- 滑动窗口大小。
- 熔断打开持续时间。
- 半开状态允许探测请求数。
Resilience4j 配置 Demo
resilience4j:
circuitbreaker:
instances:
userService:
slidingWindowSize: 20
minimumNumberOfCalls: 10
failureRateThreshold: 50
slowCallRateThreshold: 50
slowCallDurationThreshold: 2s
waitDurationInOpenState: 10s
permittedNumberOfCallsInHalfOpenState: 3
retry:
instances:
userService:
maxAttempts: 3
waitDuration: 200ms
bulkhead:
instances:
userService:
maxConcurrentCalls: 50业务代码示例:
@Service
public class UserQueryService {
private final UserClient userClient;
public UserQueryService(UserClient userClient) {
this.userClient = userClient;
}
@CircuitBreaker(name = "userService", fallbackMethod = "fallback")
@Retry(name = "userService")
@Bulkhead(name = "userService")
public UserDTO getUser(Long userId) {
return userClient.getUser(userId);
}
public UserDTO fallback(Long userId, Throwable ex) {
return new UserDTO(userId, "unknown");
}
}注意:这个 fallback 只适合用户展示类场景。支付、扣库存、权限校验这类业务不能随便返回默认成功。
为什么只会熔断还不够
熔断只是容错治理的一部分。
| 问题 | 只靠熔断是否够 | 还需要 |
|---|---|---|
| 瞬时大流量 | 不够 | 限流、排队、削峰 |
| 某下游慢 | 部分够 | 超时、慢调用熔断、隔离 |
| 写接口失败 | 不够 | 幂等、事务、补偿 |
| 线程池被打满 | 不够 | 舱壁隔离、限流 |
| 偶发网络抖动 | 不够 | 有限重试和退避 |
容错治理要组合使用,不能把所有问题都交给一个组件。
