Skip to content

Hystrix 详解:延迟与容错库(熔断器)

Hystrix 是 Netflix 开源的延迟和容错库,核心思想是断路器模式(Circuit Breaker Pattern)。在分布式系统中,服务间调用不可避免会出现失败,Hystrix 通过隔离服务间的访问点、阻止级联失败、提供降级兜底,来保证系统的弹性和稳定性。

在 Spring Cloud 生态中,Hystrix 曾是最主流的熔断器组件,后被 Sentinel 取代,但它的设计思想(断路器、线程隔离、请求合并)至今仍是分布式容错领域的经典范式。

一、为什么需要 Hystrix

1.1 服务雪崩效应

在微服务架构中,一个请求往往需要调用链上多个服务。如果链路中某个服务不可用,调用方会不断重试,线程被阻塞等待超时,最终线程池耗尽,导致整个系统崩溃——这就是服务雪崩

请求 → 服务A → 服务B → 服务C (故障)


              服务B 线程阻塞等待超时


              服务B 线程池耗尽


              服务A 不可用


              整个系统崩溃

1.2 没有熔断器时会发生什么

客户端请求 ──→ 订单服务 ──→ 库存服务 (响应慢,假设超时 5s)

                  │  Tomcat 线程池 (200 个线程)
                  │  每个请求等待库存服务 5s
                  │  200 个线程 × 5s = 全部阻塞


              订单服务完全不可用
              甚至影响同一 JVM 中的其他健康接口

1.3 Hystrix 提供的核心能力

能力说明解决的问题
断路器失败达到阈值自动熔断,快速失败防止级联失败,避免线程长时间等待
服务降级调用失败时返回 fallback 兜底结果保证核心链路可用,非核心功能有损服务
线程隔离每个依赖服务独立线程池某个服务出问题不拖垮其他服务
超时控制调用超时自动中断防止线程无限等待
请求缓存同一请求上下文内缓存结果减少重复调用,提升性能
请求合并将多个单次请求合并为一个批量请求减少网络开销,提高吞吐量

二、核心原理:断路器模式

2.1 断路器状态机

断路器是 Hystrix 最核心的设计,本质上是一个三态状态机

                        ┌──────────────────────────────┐
                        │                              │
                        ▼                              │
                  ┌──────────┐                         │
          ┌──────→│  CLOSED  │  ← 正常状态              │
          │       │  (关闭)   │    请求正常通过           │
          │       └─────┬────┘    统计失败次数           │
          │             │                               │
          │             │ 失败次数 ≥ 阈值 (默认 20 次)     │
          │             │ 且失败率 ≥ 阈值 (默认 50%)       │
          │             │                               │
          │             ▼                               │
          │       ┌──────────┐                         │
          │       │   OPEN   │  ← 熔断状态              │
          │       │  (打开)   │    所有请求直接拒绝       │
          │       └─────┬────┘    不调用真实服务          │
          │             │                               │
          │             │ 休眠窗口结束 (默认 5s)          │
          │             │                               │
          │             ▼                               │
          │       ┌──────────┐                         │
          │       │HALF_OPEN │  ← 半开状态              │
          │       │  (半开)   │    放行少量请求试探       │
          │       └─────┬────┘                         │
          │             │                               │
          │    ┌────────┼────────┐                      │
          │    │ 成功             │ 失败                  │
          │    ▼                  │                      │
          │  CLOSED               └──────────────────────┘
          │  恢复正常

          └── 断路器重新关闭

2.2 状态转换详解

状态行为触发条件
CLOSED请求正常通过;统计失败次数和失败率默认状态
CLOSED → OPEN断路器打开,所有请求直接失败时间窗口内失败次数 ≥ requestVolumeThreshold(20) 且失败率 ≥ errorThresholdPercentage(50%)
OPEN请求直接快速失败,不调用真实服务一直保持,直到休眠窗口结束
OPEN → HALF_OPEN放行部分请求探测服务是否恢复休眠窗口时间到 (sleepWindowInMilliseconds,默认 5s)
HALF_OPEN → CLOSED断路器关闭,恢复正常探测请求成功
HALF_OPEN → OPEN断路器重新打开探测请求失败,继续熔断

2.3 配置示例

yaml
hystrix:
  command:
    default:
      circuitBreaker:
        # 请求量阈值:时间窗口内至少多少个请求才触发统计
        requestVolumeThreshold: 20
        # 失败率阈值:失败百分比超过该值触发熔断
        errorThresholdPercentage: 50
        # 休眠窗口:熔断后多久进入 HALF_OPEN 状态(毫秒)
        sleepWindowInMilliseconds: 5000
        # 是否强制开启熔断(测试用)
        forceOpen: false
        # 是否强制关闭熔断(测试用)
        forceClosed: false

2.4 断路器状态判断逻辑(伪代码)

java
public boolean isCircuitBreakerOpen() {
    HealthCounts health = metrics.getHealthCounts();
    // 总请求数未达到阈值,不熔断
    if (health.getTotalRequests() < properties.requestVolumeThreshold().get()) {
        return false;
    }
    // 失败率未达到阈值,不熔断
    if (health.getErrorPercentage() < properties.errorThresholdPercentage().get()) {
        return false;
    }
    // 达到熔断条件,检查是否在休眠窗口内
    if (circuitOpened.get() == -1) {
        circuitOpened.set(System.currentTimeMillis());
        return true;
    }
    // 休眠窗口已过,进入 HALF_OPEN
    if (System.currentTimeMillis() > circuitOpened.get() + properties.sleepWindowInMilliseconds().get()) {
        circuitOpened.set(-1);
        return false; // 允许请求通过(HALF_OPEN 逻辑)
    }
    return true;
}

三、线程隔离

3.1 为什么需要隔离

在单体应用中,多个功能共享同一个线程池。在微服务中,如果不做隔离,一个下游服务的延迟会导致所有调用该服务的线程被阻塞,进而影响其他正常的下游调用。

Hystrix 提供两种隔离策略:

3.2 线程池隔离(THREAD)

每个依赖服务分配独立的线程池,调用该服务时从对应线程池获取线程执行。

┌─────────────────────────────────────────────────────────┐
│                     调用方服务                            │
│                                                         │
│  ┌─────────────────┐  ┌─────────────────┐               │
│  │ 商品服务线程池    │  │ 订单服务线程池    │              │
│  │ coreSize: 10    │  │ coreSize: 10    │              │
│  │ maxSize: 10     │  │ maxSize: 10     │              │
│  │ queueSize: 10   │  │ queueSize: 10   │              │
│  └────────┬────────┘  └────────┬────────┘              │
│           │                    │                        │
└───────────┼────────────────────┼────────────────────────┘
            ▼                    ▼
      ┌──────────┐        ┌──────────┐
      │ 商品服务  │        │ 订单服务  │
      └──────────┘        └──────────┘

优点:

  • 完全隔离,一个服务慢不会拖垮其他服务
  • 支持超时控制,可设置超时时间
  • 支持异步调用,不阻塞主线程

缺点:

  • 线程池有创建和切换开销
  • 需要根据服务数量合理配置线程池大小

3.3 信号量隔离(SEMAPHORE)

不使用额外线程池,而是通过信号量(计数器)限制并发数。

请求线程 ──→ 尝试获取信号量

              ├── 获取成功 → 执行远程调用 → 释放信号量

              └── 获取失败 → 直接降级

优点:

  • 轻量级,无线程切换开销
  • 适合低延迟、高并发场景

缺点:

  • 不支持异步,调用方线程会阻塞
  • 不支持超时控制(超时由调用方自己控制)

3.4 对比总结

维度线程池隔离 (THREAD)信号量隔离 (SEMAPHORE)
隔离级别完全隔离,独立线程池并发数限制,共享线程
超时支持支持,可中断执行不支持 Hystrix 层超时
异步支持支持 queue() / observe()不支持
线程切换有上下文切换开销无上下文切换开销
适用场景网络调用、耗时操作低延迟、高并发访问
配置复杂度需配置线程池参数仅需配置并发数
默认策略
并发限制线程池大小 + 队列大小maxConcurrentRequests

3.5 配置示例

yaml
hystrix:
  command:
    # 针对特定 command 的配置
    "ProductService#getProduct":
      execution:
        isolation:
          # 隔离策略:THREAD(线程池)或 SEMAPHORE(信号量)
          strategy: THREAD
          thread:
            timeoutInMilliseconds: 3000
          semaphore:
            maxConcurrentRequests: 20
    # 全局默认配置
    default:
      execution:
        isolation:
          strategy: THREAD
          thread:
            timeoutInMilliseconds: 1000
  threadpool:
    # 针对特定线程池的配置
    "ProductService":
      coreSize: 10          # 核心线程数
      maxQueueSize: 20      # 等待队列最大大小
      queueSizeRejectionThreshold: 15  # 队列大小拒绝阈值
    default:
      coreSize: 10
      maxQueueSize: -1      # -1 表示使用 SynchronousQueue
      queueSizeRejectionThreshold: 5

四、服务降级(Fallback)

4.1 基本用法

@HystrixCommand 注解通过 fallbackMethod 指定降级方法:

java
@Service
public class ProductService {

    @HystrixCommand(
        fallbackMethod = "getProductFallback",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
        }
    )
    public Product getProduct(Long id) {
        // 调用远程服务
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    // 降级方法:参数和返回值必须与原方法一致
    private Product getProductFallback(Long id) {
        return new Product(id, "降级默认商品", BigDecimal.ZERO);
    }
}

4.2 降级触发条件

以下情况会触发 fallback:

触发条件说明
断路器打开服务被熔断,直接走降级
线程池/信号量满无可用资源,快速失败
请求超时执行时间超过 timeoutInMilliseconds
执行异常业务方法抛出异常(非 HystrixBadRequestException

4.3 降级策略

策略一:返回默认值

java
@HystrixCommand(fallbackMethod = "getDefaultProduct")
public Product getProduct(Long id) {
    return productClient.getProduct(id);
}

private Product getDefaultProduct(Long id) {
    // 返回一个兜底的默认商品
    return Product.builder()
        .id(id)
        .name("商品暂时不可用")
        .price(BigDecimal.ZERO)
        .build();
}

策略二:返回缓存数据

java
@HystrixCommand(fallbackMethod = "getProductFromCache")
public Product getProduct(Long id) {
    return productClient.getProduct(id);
}

private Product getProductFromCache(Long id) {
    // 从本地缓存或 Redis 中获取旧数据
    Product cached = cacheManager.get("product:" + id);
    if (cached != null) {
        log.warn("服务降级,返回缓存数据,productId={}", id);
        return cached;
    }
    return new Product(id, "商品不存在", null);
}

策略三:返回空列表

java
@HystrixCommand(fallbackMethod = "getEmptyList")
public List<Product> listProducts(Long categoryId) {
    return productClient.listProducts(categoryId);
}

private List<Product> getEmptyList(Long categoryId) {
    log.warn("商品列表查询降级,返回空列表,categoryId={}", categoryId);
    return Collections.emptyList();
}

策略四:调用备用服务

java
@HystrixCommand(fallbackMethod = "callBackupService")
public OrderResult createOrder(OrderRequest request) {
    return primaryOrderService.create(request);
}

private OrderResult callBackupService(OrderRequest request) {
    // 主服务不可用,降级到备用服务
    log.warn("主订单服务不可用,切换到备用服务");
    return backupOrderService.create(request);
}

4.4 获取异常信息

降级方法可以接收 Throwable 参数,用于区分不同异常的处理逻辑:

java
@HystrixCommand(fallbackMethod = "handleFailure")
public Result<String> placeOrder(OrderRequest request) {
    return orderClient.place(request);
}

private Result<String> handleFailure(OrderRequest request, Throwable e) {
    if (e instanceof HystrixTimeoutException) {
        log.warn("下单超时,订单号暂未返回,请稍后查询");
        return Result.fail("系统繁忙,请稍后重试");
    }
    if (e instanceof HystrixRuntimeException) {
        log.error("下单失败,原因:{}", e.getMessage());
        return Result.fail("下单失败,请重试");
    }
    return Result.fail("未知错误");
}

4.5 降级配置

yaml
hystrix:
  command:
    default:
      fallback:
        isolation:
          semaphore:
            # Fallback 的最大并发请求数(默认 10)
            # 如果降级方法也调用远程服务,需适当调大
            maxConcurrentRequests: 20
        # 是否启用降级
        enabled: true

五、超时控制

5.1 超时如何工作

Hystrix 在线程池隔离模式下,通过独立线程执行远程调用,并在主线程中设置超时定时器。一旦超时,执行线程被中断,主线程立即触发 fallback。

时间轴 ─────────────────────────────────────────────────────→

主线程: 提交任务 ─→ 等待... ─→ 超时检测 ─→ 触发 Fallback ─→ 返回

执行线程:  ───────────→ 执行远程调用 ───────────→ 被中断
                       ↑                    ↑
                      0ms               timeoutInMilliseconds (默认 1000ms)

5.2 配置示例

yaml
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            # 超时时间(毫秒),默认 1000ms
            timeoutInMilliseconds: 3000
            # 是否启用超时
            timeoutEnabled: true
            # 超时时是否中断执行线程
            interruptOnTimeout: true
            # 取消时是否中断执行线程
            interruptOnCancel: true

5.3 超时 vs 断路器

维度超时控制断路器
粒度单个请求级别服务级别
判断依据执行时间失败率统计
行为超时后中断请求,走 fallback熔断后所有请求直接走 fallback
恢复方式下一个请求正常执行需要等待休眠窗口结束
配置项timeoutInMillisecondserrorThresholdPercentage / sleepWindowInMilliseconds

两者配合使用: 超时是单次请求的"保镖",断路器是服务的"医生"。超时发现单个请求的问题,断路器从统计层面发现服务的整体健康状态。

六、请求缓存(Request Cache)

6.1 原理

在同一个 HystrixRequestContext 中,如果多次调用同一个 HystrixCommand 且参数相同,Hystrix 会直接返回缓存结果,不再重复执行远程调用。

同一个请求上下文内:

  第一次调用 getProduct(1001)  → 执行远程调用 → 缓存结果
  第二次调用 getProduct(1001)  → 命中缓存 → 直接返回
  第三次调用 getProduct(1001)  → 命中缓存 → 直接返回

6.2 代码示例

java
@Service
public class ProductService {

    @CacheResult
    @HystrixCommand(fallbackMethod = "getProductFallback")
    public Product getProduct(@CacheKey Long id) {
        log.info("执行远程调用,productId={}", id);
        return restTemplate.getForObject("http://product-service/product/" + id, Product.class);
    }

    /**
     * 更新商品后,清除缓存
     */
    @CacheRemove(commandKey = "getProduct")
    @HystrixCommand
    public void updateProduct(@CacheKey("id") Product product) {
        restTemplate.put("http://product-service/product/" + product.getId(), product);
    }

    private Product getProductFallback(Long id) {
        return new Product(id, "降级商品", null);
    }
}

// 使用时必须初始化 HystrixRequestContext
@RestController
public class OrderController {

    @Autowired
    private ProductService productService;

    @GetMapping("/order/detail")
    public Result<OrderDetail> getOrderDetail(@RequestParam Long orderId) {
        // 初始化请求上下文(通常在 Filter 中统一处理)
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            Order order = orderService.getOrder(orderId);
            // 多次调用同一个 productId,仅第一次发起远程调用
            Product product1 = productService.getProduct(order.getProductId());
            Product product2 = productService.getProduct(order.getProductId());
            // product1 == product2,且只打印一次 "执行远程调用"
            return Result.success(new OrderDetail(order, product1));
        } finally {
            context.shutdown();
        }
    }
}

6.3 统一初始化(Servlet Filter)

java
@Component
public class HystrixRequestContextFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HystrixRequestContext context = HystrixRequestContext.initializeContext();
        try {
            chain.doFilter(request, response);
        } finally {
            context.shutdown();
        }
    }
}

6.4 注意事项

  • 缓存仅在同一请求上下文内有效,跨请求不共享
  • 缓存 Key 默认使用所有参数,可通过 @CacheKey 指定
  • 缓存不适用于数据实时性要求高的场景
  • 需确保 HystrixRequestContext 被正确初始化和清理

七、请求合并(Request Collapsing)

7.1 原理

将短时间内多个独立的单个请求合并为一个批量请求,减少网络连接开销,提高吞吐量。

合并前(N 次网络调用):

  请求1: getProduct(1)  ──→ 网络 ──→ 商品服务
  请求2: getProduct(2)  ──→ 网络 ──→ 商品服务   ← N 次网络开销
  请求3: getProduct(3)  ──→ 网络 ──→ 商品服务

合并后(1 次网络调用):

  请求1: getProduct(1)  ─┐
  请求2: getProduct(2)  ─┼──→ 网络 ──→ 商品服务 /products?ids=1,2,3
  请求3: getProduct(3)  ─┘


                     拆分结果返回给各自请求

7.2 代码示例

java
@Service
public class ProductService {

    /**
     * 批量查询方法(被合并后的实际调用)
     */
    @HystrixCommand
    public List<Product> getProducts(List<Long> ids) {
        log.info("批量查询商品,ids={}", ids);
        return restTemplate.getForObject(
            "http://product-service/products?ids=" + 
            ids.stream().map(String::valueOf).collect(Collectors.joining(",")),
            new ParameterizedTypeReference<List<Product>>() {}
        );
    }

    /**
     * 请求合并器:将单个 getProduct 调用合并为 getProducts 批量调用
     */
    @HystrixCollapser(
        batchMethod = "getProducts",               // 批量方法
        collapserProperties = {
            @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"),  // 合并时间窗口
            @HystrixProperty(name = "maxRequestsInBatch", value = "200")        // 最大合并数
        }
    )
    public Product getProduct(Long id) {
        // 此方法不会被真正调用,Hystrix 会拦截并合并后调用 batchMethod
        return null;
    }
}

7.3 配置详解

yaml
hystrix:
  collapser:
    default:
      # 合并时间窗口(毫秒):等待多久后执行批量请求
      timerDelayInMilliseconds: 20
      # 最大合并请求数:达到这个数量立即执行,不等时间窗口
      maxRequestsInBatch: 200
      # 是否启用请求缓存(合并器范围内的缓存)
      requestCache:
        enabled: true

7.4 适用场景

适用不适用
高频调用同一个接口,参数不同低频调用,合并窗口内凑不够批量
下游服务支持批量查询下游服务不支持批量接口
网络延迟是主要瓶颈需要极低延迟(合并会增加等待时间)
读操作(幂等,可合并)写操作(语义不同,慎用)

八、核心配置大全

8.1 断路器配置

yaml
hystrix:
  command:
    default:
      circuitBreaker:
        # 是否启用断路器
        enabled: true
        # 时间窗口内最少请求数,不足则不触发熔断
        requestVolumeThreshold: 20
        # 休眠窗口:熔断后多久进入 HALF_OPEN(毫秒)
        sleepWindowInMilliseconds: 5000
        # 错误率阈值(百分比),超过触发熔断
        errorThresholdPercentage: 50
        # 强制开启熔断(测试用)
        forceOpen: false
        # 强制关闭熔断(测试用)
        forceClosed: false

8.2 线程池配置

yaml
hystrix:
  threadpool:
    default:
      # 核心线程数
      coreSize: 10
      # 最大线程数(仅当 maxQueueSize == -1 时,此配置无效)
      maximumSize: 10
      # 空闲线程存活时间(分钟)
      keepAliveTimeMinutes: 1
      # 最大队列大小
      # -1: 使用 SynchronousQueue(无缓冲,直接交给线程)
      # >0: 使用 LinkedBlockingQueue
      maxQueueSize: -1
      # 队列大小拒绝阈值(即使队列未满,达到此值也拒绝)
      queueSizeRejectionThreshold: 5
      # 等待队列的超时时间(毫秒)
      allowMaximumSizeToDivergeFromCoreSize: false

8.3 执行配置

yaml
hystrix:
  command:
    default:
      execution:
        isolation:
          # 隔离策略:THREAD 或 SEMAPHORE
          strategy: THREAD
          thread:
            # 超时时间(毫秒)
            timeoutInMilliseconds: 1000
            # 是否启用超时
            timeoutEnabled: true
            # 超时时是否中断线程
            interruptOnTimeout: true
          semaphore:
            # 信号量最大并发数
            maxConcurrentRequests: 10

8.4 降级配置

yaml
hystrix:
  command:
    default:
      fallback:
        # 是否启用降级
        enabled: true
        isolation:
          semaphore:
            # Fallback 方法的最大并发数
            maxConcurrentRequests: 10

8.5 指标采集配置

yaml
hystrix:
  command:
    default:
      metrics:
        # 滚动统计窗口时间(毫秒),每次滚动 1/N
        rollingStats:
          timeInMilliseconds: 10000
          numBuckets: 10
        # 滑动窗口的桶数,和 timeInMilliseconds 配合使用
        # 每个桶的时间 = timeInMilliseconds / numBuckets
        # 此处每个桶为 1 秒
        rollingPercentile:
          enabled: true
          timeInMilliseconds: 60000
          numBuckets: 6
          bucketSize: 100
        # 健康检查间隔(毫秒)
        healthSnapshot:
          intervalInMilliseconds: 500

8.6 Java 配置方式

java
@Configuration
public class HystrixConfig {

    @Bean
    public HystrixCommandAspect hystrixCommandAspect() {
        return new HystrixCommandAspect();
    }

    /**
     * 也可以通过代码方式配置
     */
    @PostConstruct
    public void init() {
        ConfigurationManager.getConfigInstance().setProperty(
            "hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds", 3000);
        ConfigurationManager.getConfigInstance().setProperty(
            "hystrix.command.default.circuitBreaker.requestVolumeThreshold", 20);
        ConfigurationManager.getConfigInstance().setProperty(
            "hystrix.threadpool.default.coreSize", 10);
    }
}

九、Hystrix Dashboard + Turbine 监控

9.1 启用 Hystrix 监控端点

xml
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
yaml
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream
  endpoint:
    hystrix:
      stream:
        enabled: true
java
// 启动类
@SpringBootApplication
@EnableCircuitBreaker   // 启用断路器
@EnableHystrixDashboard // 启用 Dashboard
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

9.2 Dashboard 访问

  • Dashboard 面板: http://localhost:8080/hystrix
  • 监控数据流: http://localhost:8080/actuator/hystrix.stream

在 Dashboard 面板中输入本服务的 stream 地址,即可看到实时的熔断器监控面板。

9.3 Turbine 聚合监控

当有多个服务实例时,Turbine 可以将多个 /actuator/hystrix.stream 聚合为一个 stream,Dashboard 只需订阅 Turbine 的聚合流即可看到所有服务的熔断状态。

┌──────────────────────────────────────────────────────────┐
│                   Hystrix Dashboard                      │
│                    http://localhost:8080/hystrix          │
└────────────────────────┬─────────────────────────────────┘
                         │ 订阅

┌──────────────────────────────────────────────────────────┐
│                    Turbine Server                        │
│              http://localhost:9000/turbine.stream         │
└────────────────────────┬─────────────────────────────────┘
                         │ 聚合
           ┌─────────────┼─────────────┐
           ▼             ▼             ▼
    ┌──────────┐  ┌──────────┐  ┌──────────┐
    │ 服务实例1 │  │ 服务实例2 │  │ 服务实例3 │
    │ :8081    │  │ :8082    │  │ :8083    │
    │ /actuator │  │ /actuator │  │ /actuator │
    │ /hystrix  │  │ /hystrix  │  │ /hystrix  │
    │ .stream   │  │ .stream   │  │ .stream   │
    └──────────┘  └──────────┘  └──────────┘

Turbine 配置:

xml
<!-- Turbine 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-turbine</artifactId>
</dependency>
java
@SpringBootApplication
@EnableTurbine
public class TurbineApplication {
    public static void main(String[] args) {
        SpringApplication.run(TurbineApplication.class, args);
    }
}
yaml
# Turbine application.yml
spring:
  application:
    name: turbine-server

server:
  port: 9000

turbine:
  # 聚合的服务名列表
  app-config: order-service,product-service
  # 集群名称
  cluster-name-expression: new String("default")
  # 同一主机上的实例是否合并
  combine-host-port: true

# 注册中心
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

十、Hystrix vs Sentinel

10.1 对比总览

维度HystrixSentinel
隔离策略线程池隔离 / 信号量隔离信号量隔离(线程池隔离不推荐)
熔断策略异常比例(仅此一种)慢调用比例 / 异常比例 / 异常数
流量控制无(仅熔断,不提供限流)丰富:QPS / 线程 / 关联 / 链路 / 热点参数
系统保护支持:Load / CPU / RT / 入口 QPS
实时监控Dashboard + TurbineSentinel 控制台(功能更强大)
规则动态修改有限支持(需配置中心辅助)原生支持,实时生效
规则持久化不原生支持支持 Nacos / Apollo / ZooKeeper
自适应支持系统自适应保护
维护状态停止维护(2018 年进入维护模式)活跃维护(阿里巴巴持续投入)
开源社区Netflix(已停止)阿里巴巴(活跃)
性能线程池模式有线程切换开销信号量模式,轻量级
中文生态英文文档为主中文文档丰富,国内社区活跃
Spring Cloud 集成spring-cloud-starter-netflix-hystrixspring-cloud-starter-alibaba-sentinel

10.2 为什么 Hystrix 被 Sentinel 取代

  1. Netflix 停止维护:2018 年 Netflix 宣布 Hystrix 进入维护模式,不再开发新功能,仅修复 Bug
  2. 功能单一:Hystrix 只有熔断,没有限流、系统保护等能力,需要配合其他组件
  3. Sentinel 功能丰富:流控、熔断、系统保护一体化,规则动态生效,控制台可视化强大
  4. 国内生态:Sentinel 是阿里巴巴开源项目,中文文档和社区支持更好
  5. 性能优势:Sentinel 默认信号量隔离,轻量无锁,资源占用更少

10.3 Hystrix 仍值得学习的原因

尽管 Hystrix 已停维,但其核心设计思想(断路器模式、线程隔离、请求合并)是分布式容错领域的经典范式。Sentinel 的熔断降级也借鉴了 Hystrix 的设计。理解 Hystrix 有助于深入理解 Sentinel 和任何断路器类组件。

十一、Hystrix 工作流程

11.1 完整请求流程图

     ┌─────────────────────────────────────────────────────────────┐
     │                    Hystrix 请求处理流程                       │
     │                                                             │
     │  1. 构造 HystrixCommand 或 HystrixObservableCommand          │
     │     │                                                       │
     │     ▼                                                       │
     │  2. 执行命令                                                │
     │     ├── execute()  → 同步阻塞,返回单个结果                   │
     │     ├── queue()    → 异步,返回 Future                       │
     │     └── observe()  → 异步,返回 Observable                   │
     │     │                                                       │
     │     ▼                                                       │
     │  3. 请求缓存是否命中?  ──── 是 ────→ 返回缓存结果             │
     │     │                                                       │
     │     否                                                      │
     │     │                                                       │
     │     ▼                                                       │
     │  4. 断路器是否打开?  ──── 是 ────→ 直接走 Fallback           │
     │     │                                                       │
     │     否                                                      │
     │     │                                                       │
     │     ▼                                                       │
     │  5. 线程池/信号量是否已满?  ──── 是 ────→ 直接走 Fallback    │
     │     │                                                       │
     │     否                                                      │
     │     │                                                       │
     │     ▼                                                       │
     │  6. 执行 HystrixCommand.run() 或 construct()                │
     │     │                                                       │
     │     ├── 执行成功 ──→ 7. 计算断路器健康指标                     │
     │     │                    │                                  │
     │     │                    ▼                                  │
     │     │              8. 返回正常结果                           │
     │     │                                                       │
     │     └── 执行失败/超时 ──→ 9. 走 Fallback                     │
     │                              │                              │
     │                              ├── Fallback 成功 ──→ 返回降级结果│
     │                              │                              │
     │                              └── Fallback 失败 ──→ 抛出异常   │
     │                                                             │
     └─────────────────────────────────────────────────────────────┘

11.2 各步骤说明

步骤说明关键类/方法
1. 构造命令创建 HystrixCommand 封装远程调用HystrixCommand, HystrixObservableCommand
2. 执行命令选择执行方式:同步/异步/响应式execute(), queue(), observe()
3. 检查缓存同一请求上下文内,相同参数是否已缓存RequestCache
4. 检查断路器断路器是否 OPEN,是则直接拒绝HystrixCircuitBreaker
5. 检查资源线程池/信号量是否有可用资源ThreadPoolExecutor, TryableSemaphore
6. 执行方法实际调用远程服务run(), construct()
7. 计算健康收集成功/失败/超时指标,更新断路器状态HealthCountsStream
8. 返回结果返回正常响应
9. 执行降级调用 getFallback() 获取兜底结果getFallback()

11.3 三种执行方式

java
// 1. 同步执行 - execute()
Product product = productCommand.execute();  // 阻塞直到返回

// 2. 异步执行 - queue()
Future<Product> future = productCommand.queue();  // 立即返回 Future
Product product = future.get();  // 需要时再阻塞获取

// 3. 响应式执行 - observe()
Observable<Product> observable = productCommand.observe();  // 立即返回 Observable
observable.subscribe(product -> {
    // 处理结果
});

十二、面试要点

Q1: 什么是服务雪崩?如何解决?

回答: 服务雪崩是指微服务调用链中,一个服务不可用导致调用方线程阻塞,进而耗尽线程池资源,最终拖垮整个系统的级联故障现象。

解决方案:

  • 熔断:当失败率达到阈值时,自动熔断,快速失败,不再调用故障服务
  • 降级:调用失败时返回兜底数据(默认值、缓存、空列表)
  • 隔离:线程池隔离或信号量隔离,防止一个服务拖垮其他服务
  • 超时:设置合理的超时时间,避免线程无限等待
  • 限流:限制单位时间内的请求量,超过阈值直接拒绝

Q2: Hystrix 断路器有哪几种状态?如何转换?

回答: 三种状态:

  1. CLOSED(关闭):正常状态,请求正常通过,统计失败次数和失败率
  2. OPEN(打开):熔断状态,所有请求直接走 Fallback,不调用真实服务
  3. HALF_OPEN(半开):休眠窗口结束后进入,放行少量请求探测服务是否恢复

转换流程:

  • CLOSED → OPEN:失败次数 ≥ requestVolumeThreshold 且失败率 ≥ errorThresholdPercentage
  • OPEN → HALF_OPEN:经过 sleepWindowInMilliseconds 休眠窗口
  • HALF_OPEN → CLOSED:探测请求成功,恢复正常
  • HALF_OPEN → OPEN:探测请求失败,继续熔断

Q3: Hystrix 线程池隔离 vs 信号量隔离,有什么区别?

回答:

维度线程池隔离信号量隔离
原理每个依赖服务独立线程池信号量计数器限制并发
超时支持,可中断执行线程不支持,调用方线程阻塞
异步支持 queue() / observe()不支持
开销有线程创建和上下文切换开销轻量,几乎无开销
适用场景网络调用、耗时操作低延迟、高并发(如缓存访问)

选型建议: 对外部依赖(网络调用)用线程池隔离;对内部低延迟依赖(如本地缓存)用信号量隔离。

Q4: Hystrix 和 Sentinel 的主要区别?为什么 Sentinel 更优?

回答:

  • Hystrix 已停止维护(2018 年),Sentinel 持续活跃开发
  • 功能丰富度:Hystrix 仅有熔断;Sentinel 额外提供限流(QPS/线程/关联/热点参数)、系统保护、自适应保护
  • 熔断策略:Hystrix 仅支持异常比例;Sentinel 支持慢调用比例、异常比例、异常数三种
  • 规则管理:Sentinel 支持规则动态修改、实时生效、持久化到 Nacos/Apollo
  • 控制台:Sentinel 控制台功能更强大,支持实时监控和规则管理
  • 性能:Sentinel 默认信号量隔离,轻量无锁,性能更优

Q5: Hystrix 请求合并的原理是什么?有哪些优缺点?

回答: 请求合并(Request Collapsing)将短时间内多个独立的单个请求合并为一个批量请求,减少网络连接次数。

原理: @HystrixCollapser 在指定的时间窗口内(timerDelayInMilliseconds)收集单个请求,达到最大数量(maxRequestsInBatch)或时间窗口到期后,一次性调用批量方法,再将结果拆分返回给各个请求。

优点:

  • 减少网络连接次数,降低网络开销
  • 提高下游服务吞吐量

缺点:

  • 增加单个请求的延迟(等待合并窗口)
  • 需要下游服务支持批量查询接口
  • 不适合需要极低延迟的实时场景

Q6: Hystrix 的降级策略有哪些?各自适用什么场景?

回答:

策略代码示例适用场景
返回默认值return new Product("默认商品")读操作,非关键数据
返回缓存数据return cache.get(key)有历史数据可用的场景
返回空列表return Collections.emptyList()列表查询,前端可处理空数据
调用备用服务return backupService.call()有主备架构的场景
抛出业务异常throw new BusinessException()写操作,不能返回假数据

核心原则: 降级结果必须对业务无害。写操作不能返回假成功,读操作返回空数据比抛异常更友好。


参考资源