核心概念
Hystrix是Netflix开源的容错库,用于处理分布式系统中的延迟和故障,通过熔断、降级、隔离、限流等机制防止服务雪崩效应。
注意:Hystrix已于2018年停止维护,官方推荐使用Resilience4j或Alibaba Sentinel替代。
服务雪崩效应
服务A → 服务B → 服务C → 服务D(故障)
↑ ↑ ↑
请求堆积 请求堆积 请求堆积
↓ ↓ ↓
线程耗尽 线程耗尽 线程耗尽
↓ ↓ ↓
服务崩溃 服务崩溃 服务崩溃
Hystrix解决方案:
- 熔断器(Circuit Breaker):快速失败,防止请求堆积
- 服务降级(Fallback):提供备用响应
- 资源隔离(Isolation):线程池/信号量隔离
- 请求缓存(Request Cache):减少重复请求
熔断器工作原理
三种状态
┌─────────────┐
失败达到阈值 │ CLOSED │ 正常状态
├──────> │ (闭合) │ 请求正常通过
│ └──────┬──────┘
│ │
│ │ 失败率过高
│ ↓
│ ┌─────────────┐
│ │ OPEN │ 熔断状态
│ │ (打开) │ 快速失败
│ └──────┬──────┘
│ │
│ │ 等待一段时间
│ ↓
│ ┌─────────────┐
│ │ HALF_OPEN │ 半开状态
└────── │ (半开) │ 尝试恢复
└─────────────┘
│
成功则CLOSED
失败则OPEN
状态转换逻辑
- CLOSED(闭合):
- 初始状态,请求正常通过
- 记录成功/失败次数
- 失败率达到阈值 → OPEN
- OPEN(打开):
- 拒绝所有请求,直接返回降级结果
- 等待窗口期(默认5秒)
- 窗口期结束 → HALF_OPEN
- HALF_OPEN(半开):
- 允许少量请求通过
- 请求成功 → CLOSED
- 请求失败 → OPEN
核心使用方式
1. 基本使用
<!-- 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
// 启动类
@SpringBootApplication
@EnableCircuitBreaker // 或 @EnableHystrix
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// 服务类
@Service
public class UserService {
@HystrixCommand(
fallbackMethod = "getUserFallback", // 降级方法
commandKey = "getUserById", // 命令键
groupKey = "UserService", // 分组键
threadPoolKey = "UserServicePool" // 线程池键
)
public User getUserById(Long id) {
// 模拟远程调用
String url = "http://user-service/api/users/" + id;
return restTemplate.getForObject(url, User.class);
}
// 降级方法(参数和返回值必须与原方法一致)
public User getUserFallback(Long id) {
return new User(id, "降级用户", "fallback@example.com");
}
// 支持获取异常信息的降级方法
public User getUserFallback(Long id, Throwable throwable) {
log.error("调用失败: {}", throwable.getMessage());
return new User(id, "降级用户", "fallback@example.com");
}
}
2. 详细配置
@HystrixCommand(
commandProperties = {
// ========== 熔断配置 ==========
// 是否开启熔断器
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
// 熔断触发最小请求数(10秒内至少20个请求才计算熔断)
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
// 熔断错误比例阈值(错误率达到50%触发熔断)
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
// 熔断后休眠时间(5秒后进入半开状态)
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "5000"),
// ========== 超时配置 ==========
// 是否开启超时
@HystrixProperty(name = "execution.timeout.enabled", value = "true"),
// 超时时间(3秒)
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000"),
// 超时是否中断线程
@HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
// ========== 隔离策略 ==========
// 隔离策略:THREAD(线程池)或 SEMAPHORE(信号量)
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
// 信号量最大并发数(使用SEMAPHORE时有效)
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
// ========== 降级配置 ==========
// 降级信号量最大并发数
@HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "20"),
// ========== 统计配置 ==========
// 统计滚动窗口时间(10秒)
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "10000"),
// 统计窗口桶数量(10个桶,每个1秒)
@HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10")
},
threadPoolProperties = {
// 核心线程数
@HystrixProperty(name = "coreSize", value = "10"),
// 最大线程数
@HystrixProperty(name = "maximumSize", value = "20"),
// 是否允许最大线程数生效
@HystrixProperty(name = "allowMaximumSizeToDivergeFromCoreSize", value = "true"),
// 队列大小(-1使用SynchronousQueue,0使用LinkedBlockingQueue)
@HystrixProperty(name = "maxQueueSize", value = "100"),
// 队列拒绝阈值
@HystrixProperty(name = "queueSizeRejectionThreshold", value = "80"),
// 线程存活时间
@HystrixProperty(name = "keepAliveTimeMinutes", value = "2")
}
)
public User getUserById(Long id) {
// ...
}
3. 配置文件方式
hystrix:
command:
default: # 全局配置
execution:
isolation:
thread:
timeoutInMilliseconds: 3000 # 超时时间
circuitBreaker:
enabled: true
requestVolumeThreshold: 20 # 最小请求数
errorThresholdPercentage: 50 # 错误率阈值
sleepWindowInMilliseconds: 5000 # 休眠窗口
getUserById: # 针对特定命令
execution:
isolation:
thread:
timeoutInMilliseconds: 5000
threadpool:
UserServicePool: # 线程池配置
coreSize: 10
maximumSize: 20
maxQueueSize: 100
资源隔离策略
1. 线程池隔离(THREAD)- 默认推荐
原理:每个服务使用独立线程池
┌──────────────────────────────────┐
│ Tomcat线程池 (200) │
├──────────────────────────────────┤
│ UserService线程池 (10) │
│ OrderService线程池 (20) │
│ PaymentService线程池 (15) │
└──────────────────────────────────┘
优点:
- 完全隔离,某个服务故障不影响其他服务
- 支持超时,可以强制中断
- 异步执行,释放容器线程
缺点:
- 线程切换开销
- 内存占用较高
适用场景:
- 依赖网络调用(HTTP、RPC)
- 执行时间不确定
- 需要强隔离的场景
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
},
threadPoolKey = "UserServicePool",
threadPoolProperties = {
@HystrixProperty(name = "coreSize", value = "10")
}
)
public User getUserById(Long id) {
// 在独立线程池中执行
}
2. 信号量隔离(SEMAPHORE)
原理:使用计数器限制并发数,不创建额外线程
请求1 ──┐
请求2 ──┤
请求3 ──┼──> Semaphore(10) ──> 业务逻辑
... │
请求11 ─┘ (被拒绝)
优点:
- 无线程切换开销
- 性能更高
缺点:
- 无法超时中断
- 无法异步执行
- 隔离性较弱
适用场景:
- 本地计算或内存操作
- 执行时间可控
- 性能要求高
@HystrixCommand(
commandProperties = {
@HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE"),
@HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10")
}
)
public User getUserFromCache(Long id) {
// 在调用线程中执行,不创建新线程
return cache.get(id);
}
熔断器核心源码
1. HystrixCircuitBreaker接口
public interface HystrixCircuitBreaker {
// 是否允许请求通过
boolean allowRequest();
// 是否处于熔断状态
boolean isOpen();
// 尝试关闭熔断器
void markSuccess();
// 尝试打开熔断器
void markNonSuccess();
}
2. 核心实现
class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
// 熔断器状态(AtomicReference保证线程安全)
private AtomicReference<Status> status = new AtomicReference<>(Status.CLOSED);
// 熔断打开时间
private AtomicLong circuitOpened = new AtomicLong(-1);
@Override
public boolean allowRequest() {
// 强制打开,拒绝所有请求
if (properties.circuitBreakerForceOpen().get()) {
return false;
}
// 强制关闭,允许所有请求
if (properties.circuitBreakerForceClosed().get()) {
return true;
}
// 熔断器打开状态
if (circuitOpened.get() == -1) {
return true; // 初始状态,允许通过
} else {
// 判断是否超过休眠窗口期
if (isAfterSleepWindow()) {
// 尝试进入半开状态
if (status.compareAndSet(Status.OPEN, Status.HALF_OPEN)) {
return true; // 允许一个请求通过
} else {
return false; // 其他请求仍然拒绝
}
} else {
return false; // 休眠窗口期内,拒绝请求
}
}
}
@Override
public boolean isOpen() {
if (circuitOpened.get() == -1) {
return false;
}
if (isAfterSleepWindow()) {
return false; // 休眠窗口后不算打开状态
}
return true;
}
@Override
public void markSuccess() {
// 半开状态下请求成功,关闭熔断器
if (status.compareAndSet(Status.HALF_OPEN, Status.CLOSED)) {
// 重置统计信息
metrics.resetStream();
circuitOpened.set(-1L);
}
}
@Override
public void markNonSuccess() {
// 半开状态下请求失败,重新打开熔断器
if (status.compareAndSet(Status.HALF_OPEN, Status.OPEN)) {
circuitOpened.set(System.currentTimeMillis());
}
}
// 判断是否超过休眠窗口
private boolean isAfterSleepWindow() {
long circuitOpenTime = circuitOpened.get();
long currentTime = System.currentTimeMillis();
long sleepWindowTime = properties.circuitBreakerSleepWindowInMilliseconds().get();
return currentTime > circuitOpenTime + sleepWindowTime;
}
// 判断是否达到熔断条件
private boolean shouldTripCircuit() {
HealthCounts health = metrics.getHealthCounts();
// 请求数量不足,不熔断
if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) {
return false;
}
// 错误率达到阈值,触发熔断
if (health.getErrorPercentage() >= properties.circuitBreakerErrorThresholdPercentage().get()) {
return true;
}
return false;
}
}
3. 健康统计
class HealthCounts {
private final long totalCount; // 总请求数
private final long errorCount; // 错误请求数
private final int errorPercentage; // 错误率
public boolean isHealthy() {
return errorPercentage < 50; // 默认阈值50%
}
}
// 滑动窗口统计
class RollingNumber {
// 使用环形数组存储统计数据
private final BucketCircularArray buckets;
// 10秒滑动窗口,分为10个桶
// 每个桶统计1秒内的成功、失败、超时、拒绝等指标
}
Dashboard监控
1. 配置Dashboard
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
@SpringBootApplication
@EnableHystrixDashboard
public class DashboardApplication {
public static void main(String[] args) {
SpringApplication.run(DashboardApplication.class, args);
}
}
访问:http://localhost:8080/hystrix
2. 暴露监控端点
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
management:
endpoints:
web:
exposure:
include: hystrix.stream
监控地址:http://localhost:8080/actuator/hystrix.stream
Hystrix vs Sentinel
| 对比项 | Hystrix | Sentinel |
|---|---|---|
| 维护状态 | 已停更 | 活跃维护 |
| 隔离策略 | 线程池/信号量 | 信号量 |
| 熔断策略 | 基于错误率 | 多种策略(慢调用、错误率、异常比例) |
| 流控规则 | 不支持 | 支持(QPS、并发数、关联、链路) |
| 控制台 | Hystrix Dashboard | Sentinel Dashboard |
| 性能 | 较好 | 更好(无线程池开销) |
| 推荐度 | 不推荐新项目 | 推荐 |
Sentinel示例
@Service
public class UserService {
@SentinelResource(
value = "getUserById",
fallback = "getUserFallback",
blockHandler = "getUserBlockHandler"
)
public User getUserById(Long id) {
// 业务逻辑
}
// 降级方法(业务异常)
public User getUserFallback(Long id, Throwable ex) {
return new User(id, "降级用户");
}
// 限流方法(被限流/熔断)
public User getUserBlockHandler(Long id, BlockException ex) {
return new User(id, "系统繁忙");
}
}
最佳实践
1. 合理设置超时时间
超时时间 > (重试次数 + 1) × 单次超时时间
例如:Ribbon重试1次,单次超时1秒
Hystrix超时 > 2 × 1000 = 2000ms
2. 降级方法设计
// ❌ 错误:降级方法中调用其他服务
public User getUserFallback(Long id) {
return orderService.getDefaultUser(); // 可能再次失败
}
// ✅ 正确:返回静态数据或缓存
public User getUserFallback(Long id) {
return cache.get(id, () -> new User(id, "默认用户"));
}
3. 线程池隔离粒度
// 按服务分组,而非每个方法一个线程池
@HystrixCommand(threadPoolKey = "UserService") // ✅
@HystrixCommand(threadPoolKey = "getUserById") // ❌ 过细
面试总结
Hystrix熔断机制核心:
- 三种状态:CLOSED(闭合)、OPEN(打开)、HALF_OPEN(半开)
- 熔断条件:10秒内至少20个请求,且错误率达到50%
- 恢复机制:熔断5秒后进入半开状态,尝试放行请求,成功则恢复
- 资源隔离:线程池隔离(默认)或信号量隔离
关键配置:
requestVolumeThreshold:最小请求数(默认20)errorThresholdPercentage:错误率阈值(默认50%)sleepWindowInMilliseconds:休眠窗口(默认5秒)timeoutInMilliseconds:超时时间(默认1秒)
Hystrix已停更,新项目推荐:
- Sentinel(阿里开源,功能更强大)
- Resilience4j(轻量级,函数式编程风格)
核心价值:通过快速失败和服务降级防止服务雪崩,保证系统整体可用性。