Skip to content

CircuitBreaker GatewayFilter 工厂 ​

Spring Cloud CircuitBreaker GatewayFilter 工厂使用 Spring Cloud CircuitBreaker API 将网关路由封装在断路器中。Spring Cloud CircuitBreaker 支持多种可与 Spring Cloud Gateway 配合使用的库。Spring Cloud 本身就支持 Resilience4J。

要启用 Spring Cloud CircuitBreaker 过滤器,需要将 spring-cloud-starter-circuitbreaker-reactor-resilience4j 添加到类路径中。

以下示例配置了一个 Spring Cloud CircuitBreaker GatewayFilter:

application.yml

yaml
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: https://example.org
        filters:
        - CircuitBreaker=myCircuitBreaker

要配置断路器本身,请参阅您正在使用的底层断路器实现(如 Resilience4J)的配置文档:

Spring Cloud CircuitBreaker 过滤器还可以接受一个可选的 fallbackUri 参数。目前,仅支持带有 forward: 协议(scheme)的 URI。如果触发了回退(Fallback)机制,请求将被转发到与该 URI 匹配的控制器。

以下示例配置了带有回退机制的路由:

application.yml

yaml
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
        - RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint

以下代码展示了如何使用 Java 配置实现相同的功能:

Application.java

java
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}

在此示例中,当断路器回退被触发时,请求会转发到 /inCaseOfFailureUseThis。请注意,此示例还演示了(可选的)Spring Cloud LoadBalancer 负载均衡(由目标 URI 的 lb 前缀定义)。

CircuitBreaker 的 fallbackUri 还支持 URI 变量。这允许更复杂的路由选项,例如使用 PathPattern 表达式 将原始主机或 URL 路径的一部分转发到回退处理器。

在下面的示例中,调用 /consumingServiceEndpoint/users/1 将被重定向到 /inCaseOfFailureUseThis/users/1。

application.yml

yaml
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint/{*segments}
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis/{segments}

主要场景是使用 fallbackUri 在网关应用程序内部定义一个内部控制器或处理程序。但是,您也可以将请求重定向到外部应用程序中的控制器或处理程序,如下所示:

application.yml

yaml
spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback

在这个例子中:

  1. 网关应用程序中没有 /fallback 端点或处理程序。
  2. 但是,在另一个应用程序(运行在 localhost:9994)中有一个处理程序。
  3. 网关定义了第二个路由 ingredients-fallback,用于匹配 /fallback 并将其转发到外部服务。

如果请求被转发到备用方案,Spring Cloud CircuitBreaker Gateway 过滤器还会提供导致该故障的原因。该异常原因会作为 ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR 属性添加到 ServerWebExchange 中,以便在处理备用方案时使用。

对于外部控制器/处理程序场景,可以添加包含异常详细信息的 Header。您可以在 FallbackHeaders GatewayFilter Factory 部分找到更多相关信息。

根据状态码触发熔断 (Tripping The Circuit Breaker On Status Codes) ​

在某些情况下,您可能需要根据路由返回的 HTTP 状态码来触发断路器。断路器配置对象接受一个状态码列表,当返回列表中的某个状态码时,断路器就会跳闸。 设置状态码时,您可以使用状态码的整数值(如 500),也可以使用 HttpStatus 枚举的字符串表示形式(如 "INTERNAL_SERVER_ERROR")。

application.yml

yaml
spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
            statusCodes:
              - 500
              - "NOT_FOUND"

Application.java

java
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis").addStatusCode("INTERNAL_SERVER_ERROR"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}

通俗易懂的教学 —— “家里的保险丝” ​

想象一下,你家里有很多电器(电视、冰箱、微波炉),这些就像是你的微服务。而 Spring Cloud Gateway 就像是你家里的总电闸/配电箱。

1. 什么是 CircuitBreaker(断路器)? ​

通俗解释: CircuitBreaker 就是保险丝。

  • 正常情况:电流顺畅通过,电器正常工作(请求正常转发给微服务)。
  • 出故障了:如果微波炉突然短路了(微服务挂了或者响应极慢),电流过大。
  • 没有保险丝:电线会烧坏,甚至引发火灾,把隔壁冰箱也烧坏(由于请求堆积,耗尽网关资源,导致整个网关崩溃,所有服务都不可用 —— 这叫雪崩效应)。
  • 有保险丝:保险丝“啪”的一声断开(熔断)。微波炉没电了,但是电视和冰箱还能正常用。网关会告诉用户:“微波炉暂时坏了,请稍后再试”,而不是让用户一直干等。

2. 配置里的关键参数是什么意思? ​

  • name: myCircuitBreaker:给这个保险丝起个名字。
  • fallbackUri: forward:/fallback:这就是备用方案(兜底)。
    • 场景:你点外卖,系统去调用“商家服务”获取菜单。
    • 故障:“商家服务”挂了,超时了。
    • 熔断发生:保险丝断开。
    • Fallback:网关自动把请求转发到 /fallback。这个 /fallback 可能返回一张图片:“哎呀,系统繁忙,请稍后再试”,或者返回一个缓存的旧菜单。
    • 好处:用户看到的是友好的提示,而不是由浏览器报出的 500 Internal Server Error 或者在那转圈圈转到死。

3. 什么是“根据状态码熔断”? ​

默认情况下,只有微服务完全连不上或者超时,保险丝才会断。 但是,有时候微服务虽然连上了,却返回了错误的信息,比如 500(服务器内部错误)。

  • 配置 statusCodes: 500:意思是告诉网关,“只要那个微服务敢给我返回 500 错误,我就当它坏了,直接拉闸!走备用方案!”

4. 总结 ​

在 Spring Cloud Gateway 中配置 CircuitBreaker 只有三步:

  1. 加依赖:买保险丝(引入 spring-cloud-starter-circuitbreaker-reactor-resilience4j)。
  2. 配过滤器:把保险丝装进电路里(在 yaml 里配置 filters: - CircuitBreaker=...)。
  3. 写兜底逻辑:想好保险丝断了该怎么办(写一个 /fallback 接口返回友好提示)。

这样,你的系统就从“一碰就倒”变成了“坚不可摧”的堡垒!


Based on Spring Framework.