Skip to content

FallbackHeaders Gateway Filter Factory

概述

FallbackHeaders 专门用于在熔断器降级场景中向转发请求添加异常详细信息的请求头。当后端服务出现故障触发熔断器时,该过滤器能够将异常信息传递给降级服务,帮助降级服务更好地处理异常情况。

解决的问题

在微服务架构中,服务之间的调用链路复杂,当某个服务出现故障时,如果没有合适的降级机制,可能会导致整个调用链路的雪崩效应。Spring Cloud Gateway 通过集成 Spring Cloud CircuitBreaker 提供熔断降级功能,但降级服务往往需要了解原始请求失败的具体原因才能提供更好的用户体验。

FallbackHeaders 过滤器解决了以下关键问题:

  • 异常信息传递:将原始服务的异常类型和消息传递给降级服务
  • 根因追踪:提供异常的根本原因信息,便于问题排查
  • 降级服务优化:让降级服务能够根据不同的异常类型提供差异化的降级响应

工作原理

配置示例

基础配置

yaml
spring:
  cloud:
    gateway:
      routes:
        # 主要路由配置 - 包含熔断器
        - id: ingredients
          uri: lb://ingredients
          predicates:
            - Path=/ingredients/**
          filters:
            - name: CircuitBreaker
              args:
                name: fetchIngredients
                fallbackUri: forward:/fallback

        # 降级路由配置 - 使用 FallbackHeaders
        - id: ingredients-fallback
          uri: http://localhost:9994
          predicates:
            - Path=/fallback
          filters:
            - name: FallbackHeaders
              args:
                executionExceptionTypeHeaderName: Test-Header
kotlin
@Configuration
class GatewayConfig {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            // 主要路由 - 包含熔断器
            .route("ingredients") { r ->
                r.path("/ingredients/**")
                    .filters { f ->
                        f.circuitBreaker { config ->
                            config.name = "fetchIngredients"
                            config.fallbackUri = "forward:/fallback"
                        }
                    }
                    .uri("lb://ingredients")
            }
            // 降级路由 - 使用 FallbackHeaders
            .route("ingredients-fallback") { r ->
                r.path("/fallback")
                    .filters { f ->
                        f.fallbackHeaders { config ->
                            config.executionExceptionTypeHeaderName = "Custom-Exception-Type"
                            config.executionExceptionMessageHeaderName = "Custom-Exception-Message"
                        }
                    }
                    .uri("http://localhost:9994")
            }
            .build()
    }
}

自定义头部名称配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: order-fallback
          uri: http://localhost:8081
          predicates:
            - Path=/order-fallback
          filters:
            - name: FallbackHeaders
              args:
                # 自定义异常类型头部名称
                executionExceptionTypeHeaderName: "X-Order-Exception-Type"
                # 自定义异常消息头部名称
                executionExceptionMessageHeaderName: "X-Order-Exception-Message"
                # 自定义根因异常类型头部名称
                rootCauseExceptionTypeHeaderName: "X-Order-Root-Cause-Type"
                # 自定义根因异常消息头部名称
                rootCauseExceptionMessageHeaderName: "X-Order-Root-Cause-Message"

配置参数详解

参数名称默认值说明
executionExceptionTypeHeaderName"Execution-Exception-Type"执行异常类型的头部名称
executionExceptionMessageHeaderName"Execution-Exception-Message"执行异常消息的头部名称
rootCauseExceptionTypeHeaderName"Root-Cause-Exception-Type"根因异常类型的头部名称
rootCauseExceptionMessageHeaderName"Root-Cause-Exception-Message"根因异常消息的头部名称

所有参数都是可选的。如果不指定,将使用默认的头部名称。你可以根据业务需要自定义头部名称,避免与现有头部冲突。

实际业务场景示例

场景一:电商订单服务降级

在电商系统中,当订单服务出现异常时,需要根据不同的异常类型提供不同的降级策略:

kotlin
@RestController
@RequestMapping("/order")
class OrderController {

    @Autowired
    private lateinit var orderService: OrderService

    /**
     * 创建订单接口
     */
    @PostMapping("/create")
    fun createOrder(@RequestBody orderRequest: OrderRequest): ResponseEntity<OrderResponse> {
        return try {
            val order = orderService.createOrder(orderRequest)
            ResponseEntity.ok(OrderResponse.success(order))
        } catch (e: Exception) {
            // 这里的异常会被熔断器捕获,并通过 FallbackHeaders 传递给降级服务
            throw OrderCreationException("订单创建失败", e)
        }
    }
}
kotlin
@RestController
@RequestMapping("/fallback")
class FallbackController {

    /**
     * 订单服务降级处理
     * 根据 FallbackHeaders 传递的异常信息提供差异化降级响应
     */
    @PostMapping("/order")
    fun orderFallback(
        request: HttpServletRequest,
        @RequestBody orderRequest: OrderRequest
    ): ResponseEntity<OrderResponse> {

        // 获取 FallbackHeaders 添加的异常信息
        val exceptionType = request.getHeader("Execution-Exception-Type")
        val exceptionMessage = request.getHeader("Execution-Exception-Message")
        val rootCauseType = request.getHeader("Root-Cause-Exception-Type")
        val rootCauseMessage = request.getHeader("Root-Cause-Exception-Message")

        // 记录异常信息用于监控和排查
        log.warn("订单服务降级触发 - 异常类型: $exceptionType, 消息: $exceptionMessage")

        // 根据异常类型提供差异化降级策略
        return when {
            exceptionType?.contains("TimeoutException") == true -> {
                // 超时异常 - 提示用户稍后重试
                ResponseEntity.ok(OrderResponse.fallback(
                    "系统繁忙,请稍后重试",
                    FallbackType.RETRY_LATER
                ))
            }
            exceptionType?.contains("ValidationException") == true -> {
                // 验证异常 - 返回具体的验证错误信息
                ResponseEntity.badRequest().body(OrderResponse.fallback(
                    "订单信息验证失败:$exceptionMessage",
                    FallbackType.VALIDATION_ERROR
                ))
            }
            exceptionType?.contains("DatabaseException") == true -> {
                // 数据库异常 - 提示系统维护
                ResponseEntity.ok(OrderResponse.fallback(
                    "系统正在维护中,请稍后重试",
                    FallbackType.MAINTENANCE
                ))
            }
            else -> {
                // 其他异常 - 通用降级响应
                ResponseEntity.ok(OrderResponse.fallback(
                    "服务暂时不可用,请联系客服",
                    FallbackType.GENERAL_ERROR
                ))
            }
        }
    }

    companion object {
        private val log = LoggerFactory.getLogger(FallbackController::class.java)
    }
}

场景二:支付服务异常处理

支付服务的异常信息对于用户体验和业务决策都非常重要:

kotlin
@RestController
@RequestMapping("/payment")
class PaymentFallbackController {

    @PostMapping("/process")
    fun paymentFallback(
        request: HttpServletRequest,
        @RequestBody paymentRequest: PaymentRequest
    ): ResponseEntity<PaymentResponse> {

        val exceptionType = request.getHeader("Execution-Exception-Type")
        val exceptionMessage = request.getHeader("Execution-Exception-Message")

        // 记录支付异常用于风控分析
        paymentAuditService.recordPaymentFailure(
            userId = paymentRequest.userId,
            amount = paymentRequest.amount,
            exceptionType = exceptionType,
            exceptionMessage = exceptionMessage
        )

        return when {
            exceptionType?.contains("InsufficientBalanceException") == true -> {
                ResponseEntity.ok(PaymentResponse.failure(
                    code = "INSUFFICIENT_BALANCE",
                    message = "账户余额不足,请充值后重试"
                ))
            }
            exceptionType?.contains("RiskControlException") == true -> {
                ResponseEntity.ok(PaymentResponse.failure(
                    code = "RISK_CONTROL",
                    message = "交易存在风险,已暂停处理"
                ))
            }
            exceptionType?.contains("NetworkException") == true -> {
                ResponseEntity.ok(PaymentResponse.failure(
                    code = "NETWORK_ERROR",
                    message = "网络连接异常,请检查网络后重试"
                ))
            }
            else -> {
                ResponseEntity.ok(PaymentResponse.failure(
                    code = "SYSTEM_ERROR",
                    message = "系统异常,请稍后重试"
                ))
            }
        }
    }
}

与其他过滤器的配合使用

与熔断器过滤器配合

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
          filters:
            # 1. 首先应用熔断器
            - name: CircuitBreaker
              args:
                name: userServiceCircuitBreaker
                fallbackUri: forward:/user-fallback
                # 熔断器配置
                failureRateThreshold: 50
                slowCallRateThreshold: 50
                slowCallDurationThreshold: 2s
                minimumNumberOfCalls: 10
                slidingWindowSize: 10
            # 2. 添加请求日志
            - name: RequestLogging

        - id: user-fallback
          uri: http://localhost:8082
          predicates:
            - Path=/user-fallback
          filters:
            # 1. 添加异常头部信息
            - name: FallbackHeaders
              args:
                executionExceptionTypeHeaderName: "User-Exception-Type"
                executionExceptionMessageHeaderName: "User-Exception-Message"
            # 2. 添加降级标识头部
            - name: AddRequestHeader
              args:
                name: X-Fallback-Reason
                value: user-service-circuit-breaker
            # 3. 限流保护降级服务
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100
                redis-rate-limiter.burstCapacity: 200

监控和调试

启用请求日志

kotlin
@Component
class FallbackHeadersLoggingFilter : GlobalFilter, Ordered {

    override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
        return chain.filter(exchange).doFinally {
            val request = exchange.request
            val headers = request.headers

            // 记录 FallbackHeaders 添加的异常信息
            if (headers.containsKey("Execution-Exception-Type")) {
                log.info("降级请求处理 - 路径: ${request.path}, " +
                        "异常类型: ${headers.getFirst("Execution-Exception-Type")}, " +
                        "异常消息: ${headers.getFirst("Execution-Exception-Message")}")
            }
        }
    }

    override fun getOrder(): Int = -1

    companion object {
        private val log = LoggerFactory.getLogger(FallbackHeadersLoggingFilter::class.java)
    }
}

健康检查端点

kotlin
@RestController
@RequestMapping("/actuator")
class FallbackHealthController {

    @GetMapping("/fallback-stats")
    fun getFallbackStats(request: HttpServletRequest): Map<String, Any> {
        return mapOf(
            "timestamp" to System.currentTimeMillis(),
            "fallback_headers" to mapOf(
                "execution_exception_type" to request.getHeader("Execution-Exception-Type"),
                "execution_exception_message" to request.getHeader("Execution-Exception-Message"),
                "root_cause_exception_type" to request.getHeader("Root-Cause-Exception-Type"),
                "root_cause_exception_message" to request.getHeader("Root-Cause-Exception-Message")
            )
        )
    }
}

最佳实践

IMPORTANT

降级服务保护

降级服务是系统的最后一道防线,必须确保其高可用性。建议为降级服务配置独立的资源池,并设置适当的限流策略。

TIP

异常信息安全

在生产环境中,要注意异常信息的安全性。避免在异常消息中暴露敏感信息,如数据库连接信息、内部服务地址等。

WARNING

头部大小限制

HTTP 头部有大小限制,确保异常消息不会过长。建议对异常消息进行截断或编码处理。

异常信息脱敏

kotlin
@Component
class SensitiveInfoSanitizer {

    private val sensitivePatterns = listOf(
        "password=\\w+".toRegex(),
        "token=\\w+".toRegex(),
        "jdbc:mysql://[\\w.]+:\\d+".toRegex()
    )

    fun sanitizeExceptionMessage(message: String?): String? {
        if (message == null) return null

        var sanitized = message
        sensitivePatterns.forEach { pattern ->
            sanitized = pattern.replace(sanitized, "[REDACTED]")
        }

        // 限制异常消息长度
        return if (sanitized.length > 200) {
            sanitized.substring(0, 200) + "..."
        } else {
            sanitized
        }
    }
}

常见问题解决

问题一:降级服务接收不到异常头部

原因分析

  • 路由配置错误,FallbackHeaders 过滤器未正确配置
  • 降级 URI 不匹配

解决方案

yaml
# 确保降级路由正确配置
spring:
  cloud:
    gateway:
      routes:
        - id: main-route
          filters:
            - name: CircuitBreaker
              args:
                fallbackUri: forward:/fallback # 确保与降级路由匹配

        - id: fallback-route
          predicates:
            - Path=/fallback # 确保路径匹配
          filters:
            - name: FallbackHeaders # 确保过滤器正确配置

问题二:异常信息丢失

原因分析

  • 异常被其他过滤器拦截并处理
  • 异常信息为空或格式不正确

解决方案

kotlin
// 自定义异常处理器确保异常信息完整
@Component
class CustomExceptionHandler : ResponseEntityExceptionHandler() {

    @ExceptionHandler(Exception::class)
    fun handleGenericException(e: Exception): ResponseEntity<ErrorResponse> {
        // 确保异常信息被正确设置
        val errorResponse = ErrorResponse(
            message = e.message ?: "Unknown error",
            type = e.javaClass.simpleName,
            timestamp = System.currentTimeMillis()
        )

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(errorResponse)
    }
}

总结

FallbackHeaders 过滤器工厂是 Spring Cloud Gateway 中一个强大而实用的工具,它通过将异常信息传递给降级服务,使得系统能够提供更加智能和用户友好的降级响应。

主要优势

  • 🔍 异常信息透明:完整传递异常类型和消息
  • 🎯 差异化降级:支持根据异常类型定制降级策略
  • 🛡️ 系统稳定性:提高整体系统的容错能力
  • 📊 可观测性:便于监控和问题排查

在微服务架构中合理使用 FallbackHeaders 过滤器,能够显著提升用户体验和系统的健壮性。