Appearance
HTTP 超时配置
概述
在微服务架构中,网关作为所有外部请求的入口点,承担着路由转发、负载均衡等重要职责。HTTP 超时配置是网关稳定性的关键因素,它能有效防止因下游服务响应缓慢而导致的资源耗尽和服务雪崩。
Spring Cloud Gateway 提供了灵活的超时配置机制,支持全局超时配置和针对特定路由的超时配置,让开发者能够根据不同业务场景的需求进行精细化的超时控制。
HTTP 超时配置包括连接超时(connect-timeout)和响应超时(response-timeout),两者协同工作来保障网关的稳定性和可用性。
业务场景分析
为什么需要超时配置?
在实际业务中,超时配置解决以下问题:
- 防止资源耗尽:避免因慢服务占用过多连接池资源
- 提升用户体验:快速失败,避免用户长时间等待
- 系统稳定性:防止服务雪崩效应
- 容错处理:配合断路器实现优雅降级
全局超时配置
全局超时配置适用于所有路由,作为默认的超时策略。
配置说明
connect-timeout:连接超时时间,必须以毫秒为单位指定response-timeout:响应超时时间,必须以java.time.Duration格式指定
配置示例
yaml
spring:
cloud:
gateway:
httpclient:
connect-timeout: 1000 # 连接超时:1秒
response-timeout: 5s # 响应超时:5秒kotlin
@Configuration
class GatewayConfig {
/**
* 全局 HTTP 客户端超时配置
* 通过配置 HttpClient 实现全局超时设置
*/
@Bean
fun httpClient(): HttpClient {
return HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000) // 连接超时:1秒
.responseTimeout(Duration.ofSeconds(5)) // 响应超时:5秒
.doOnConnected { conn ->
conn.addHandlerLast(ReadTimeoutHandler(5)) // 读取超时:5秒
.addHandlerLast(WriteTimeoutHandler(5)) // 写入超时:5秒
}
}
/**
* 自定义网关过滤器工厂,用于超时处理
*/
@Bean
fun reactorNettyClientConfiguration(): ReactorNettyHttpClientFactory {
return ReactorNettyHttpClientFactory { httpClient() }
}
}全局配置建议根据你的业务特点来设置,通常连接超时设置为 1-3 秒,响应超时设置为 5-30 秒。
针对路由的超时配置
对于不同的业务场景,可能需要不同的超时策略。例如,文件上传接口需要更长的超时时间,而健康检查接口需要更短的超时时间。
配置说明
在路由级别的超时配置中:
connect-timeout:连接超时时间,以毫秒为单位response-timeout:响应超时时间,以毫秒为单位
注意路由级别的 `response-timeout` 单位是毫秒,而全局配置的单位是 Duration 格式。
通过配置文件实现
yaml
spring:
cloud:
gateway:
routes:
# 文件上传路由 - 需要较长超时时间
- id: file_upload_route
uri: https://file-service.example.com
predicates:
- Path=/api/upload/**
metadata:
response-timeout: 30000 # 30秒响应超时
connect-timeout: 2000 # 2秒连接超时
# 健康检查路由 - 需要快速响应
- id: health_check_route
uri: https://health-service.example.com
predicates:
- Path=/health/**
metadata:
response-timeout: 1000 # 1秒响应超时
connect-timeout: 500 # 500毫秒连接超时
# 支付服务路由 - 中等超时时间
- id: payment_route
uri: https://payment-service.example.com
predicates:
- Path=/api/payment/**
metadata:
response-timeout: 10000 # 10秒响应超时
connect-timeout: 1000 # 1秒连接超时通过 Java DSL 实现
kotlin
@Configuration
class RouteConfiguration {
/**
* 使用 Kotlin DSL 配置不同业务场景的路由超时
*/
@Bean
fun customRouteLocator(routeBuilder: RouteLocatorBuilder): RouteLocator {
return routeBuilder.routes()
// 文件上传路由配置
.route("file_upload_route") { r ->
r.path("/api/upload/**")
.filters { f ->
f.addRequestHeader("Service-Type", "file-upload")
.addResponseHeader("Cache-Control", "no-cache")
}
.uri("https://file-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, 30000) // 30秒响应超时
.metadata(CONNECT_TIMEOUT_ATTR, 2000) // 2秒连接超时
}
// 实时数据查询路由
.route("realtime_data_route") { r ->
r.path("/api/realtime/**")
.and()
.method(HttpMethod.GET)
.filters { f ->
f.addRequestHeader("Priority", "high")
.retry { config ->
config.setRetries(2)
.setMethods(HttpMethod.GET)
}
}
.uri("https://realtime-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, 5000) // 5秒响应超时
.metadata(CONNECT_TIMEOUT_ATTR, 1000) // 1秒连接超时
}
// 批处理任务路由
.route("batch_processing_route") { r ->
r.path("/api/batch/**")
.and()
.header("Content-Type", "application/json")
.filters { f ->
f.addRequestHeader("Processing-Mode", "batch")
.requestSize(10 * 1024 * 1024) // 限制请求大小为10MB
}
.uri("https://batch-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, 60000) // 60秒响应超时
.metadata(CONNECT_TIMEOUT_ATTR, 3000) // 3秒连接超时
}
.build()
}
companion object {
// 导入超时配置常量
private const val RESPONSE_TIMEOUT_ATTR = "response-timeout"
private const val CONNECT_TIMEOUT_ATTR = "connect-timeout"
}
}java
@Configuration
public class RouteConfiguration {
/**
* 使用 Java DSL 配置路由超时
*/
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder routeBuilder) {
return routeBuilder.routes()
.route("file_upload_route", r -> {
return r.path("/api/upload/**")
.filters(f -> f.addRequestHeader("Service-Type", "file-upload"))
.uri("https://file-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, 30000)
.metadata(CONNECT_TIMEOUT_ATTR, 2000);
})
.route("health_check_route", r -> {
return r.path("/health/**")
.filters(f -> f.addRequestHeader("Check-Type", "health"))
.uri("https://health-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, 1000)
.metadata(CONNECT_TIMEOUT_ATTR, 500);
})
.build();
}
}高级超时配置技巧
禁用全局超时
当某个路由需要无限制的响应时间时,可以通过设置负值来禁用全局超时配置:
yaml
spring:
cloud:
gateway:
routes:
- id: long_running_task
uri: https://batch-processing.example.com
predicates:
- Path=/api/long-task/**
metadata:
response-timeout: -1 # 禁用响应超时动态超时配置
kotlin
@Component
class DynamicTimeoutFilter : GatewayFilter, Ordered {
/**
* 基于请求头动态设置超时时间的过滤器
*/
override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
val request = exchange.request
val timeoutHeader = request.headers.getFirst("X-Timeout")
return if (timeoutHeader != null) {
// 根据请求头设置动态超时
val timeout = Duration.ofMillis(timeoutHeader.toLong())
chain.filter(exchange)
.timeout(timeout)
.onErrorResume(TimeoutException::class.java) {
handleTimeoutError(exchange, it)
}
} else {
chain.filter(exchange)
}
}
/**
* 超时错误处理
*/
private fun handleTimeoutError(
exchange: ServerWebExchange,
ex: TimeoutException
): Mono<Void> {
val response = exchange.response
response.statusCode = HttpStatus.REQUEST_TIMEOUT
response.headers.add("Content-Type", "application/json")
val errorBody = """
{
"error": "Request Timeout",
"message": "请求超时,请稍后重试",
"timestamp": "${Instant.now()}"
}
""".trimIndent()
val buffer = response.bufferFactory().wrap(errorBody.toByteArray())
return response.writeWith(Mono.just(buffer))
}
override fun getOrder(): Int = -1
}kotlin
@Configuration
class TimeoutConfiguration {
/**
* 基于业务类型的超时配置映射
*/
@Bean
fun businessTimeoutConfig(): Map<String, TimeoutConfig> {
return mapOf(
"payment" to TimeoutConfig(connectTimeout = 1000, responseTimeout = 15000),
"upload" to TimeoutConfig(connectTimeout = 2000, responseTimeout = 60000),
"query" to TimeoutConfig(connectTimeout = 500, responseTimeout = 3000),
"report" to TimeoutConfig(connectTimeout = 3000, responseTimeout = 120000)
)
}
/**
* 业务感知的路由配置
*/
@Bean
fun businessAwareRoutes(
routeBuilder: RouteLocatorBuilder,
timeoutConfig: Map<String, TimeoutConfig>
): RouteLocator {
return routeBuilder.routes()
.route("payment_route") { r ->
val config = timeoutConfig["payment"]!!
r.path("/api/payment/**")
.filters { f -> f.addRequestHeader("Business-Type", "payment") }
.uri("https://payment-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, config.responseTimeout)
.metadata(CONNECT_TIMEOUT_ATTR, config.connectTimeout)
}
.route("upload_route") { r ->
val config = timeoutConfig["upload"]!!
r.path("/api/upload/**")
.filters { f -> f.addRequestHeader("Business-Type", "upload") }
.uri("https://upload-service.example.com")
.metadata(RESPONSE_TIMEOUT_ATTR, config.responseTimeout)
.metadata(CONNECT_TIMEOUT_ATTR, config.connectTimeout)
}
.build()
}
data class TimeoutConfig(
val connectTimeout: Long,
val responseTimeout: Long
)
companion object {
private const val RESPONSE_TIMEOUT_ATTR = "response-timeout"
private const val CONNECT_TIMEOUT_ATTR = "connect-timeout"
}
}超时监控与告警
超时指标收集
kotlin
@Component
class TimeoutMetricsFilter : GatewayFilter, Ordered {
private val meterRegistry: MeterRegistry = Metrics.globalRegistry
private val timeoutCounter = Counter.builder("gateway.timeout.count")
.description("网关超时请求计数")
.register(meterRegistry)
override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
val startTime = System.currentTimeMillis()
return chain.filter(exchange)
.doOnError(TimeoutException::class.java) { ex ->
// 记录超时指标
timeoutCounter.increment(
Tags.of(
Tag.of("route", getRouteId(exchange)),
Tag.of("uri", exchange.request.uri.path),
Tag.of("method", exchange.request.method.name())
)
)
// 记录超时时长
Timer.Sample.start(meterRegistry)
.stop(Timer.builder("gateway.timeout.duration")
.description("网关超时请求持续时间")
.register(meterRegistry))
log.warn("请求超时: {} {} 耗时: {}ms",
exchange.request.method,
exchange.request.uri,
System.currentTimeMillis() - startTime)
}
}
private fun getRouteId(exchange: ServerWebExchange): String {
return exchange.getAttribute<Route>(GATEWAY_ROUTE_ATTR)?.id ?: "unknown"
}
override fun getOrder(): Int = 0
companion object {
private val log = LoggerFactory.getLogger(TimeoutMetricsFilter::class.java)
}
}最佳实践
1. 超时时间设置原则
2. 分层超时策略
建议采用分层的超时配置策略:
- 全局默认:设置保守的默认值
- 业务分类:根据业务特点分类配置
- 特殊路由:对特殊需求进行个性化配置
3. 配置示例模板
Details
完整的超时配置示例
yaml
spring:
cloud:
gateway:
# 全局超时配置
httpclient:
connect-timeout: 1000 # 全局连接超时:1秒
response-timeout: 10s # 全局响应超时:10秒
routes:
# 健康检查 - 快速响应
- id: health_route
uri: lb://health-service
predicates:
- Path=/health/**
metadata:
response-timeout: 1000
connect-timeout: 500
# API查询 - 标准响应
- id: api_query_route
uri: lb://api-service
predicates:
- Path=/api/query/**
metadata:
response-timeout: 5000
connect-timeout: 1000
# 文件上传 - 长时间响应
- id: upload_route
uri: lb://file-service
predicates:
- Path=/api/upload/**
metadata:
response-timeout: 60000
connect-timeout: 3000
# 报表生成 - 超长响应
- id: report_route
uri: lb://report-service
predicates:
- Path=/api/report/**
metadata:
response-timeout: 120000
connect-timeout: 5000
# 实时通信 - 禁用超时
- id: websocket_route
uri: lb://websocket-service
predicates:
- Path=/ws/**
metadata:
response-timeout: -1 # 禁用响应超时4. 监控告警配置
建议配置以下监控指标:
- 超时请求数量和比率
- 平均响应时间趋势
- 不同路由的超时分布
- 连接池使用情况
常见问题排查
问题 1:频繁超时告警
症状:大量请求出现超时,响应时间过长
排查步骤:
- 检查下游服务健康状态
- 分析网络延迟情况
- 检查连接池配置是否合理
- 确认超时设置是否过于严格
解决方案:
kotlin
// 调整超时配置并增加重试机制
@Bean
fun resilientRouteLocator(routeBuilder: RouteLocatorBuilder): RouteLocator {
return routeBuilder.routes()
.route("resilient_route") { r ->
r.path("/api/critical/**")
.filters { f ->
f.retry { config ->
config.setRetries(3) // 重试3次
.setMethods(HttpMethod.GET) // 只对GET请求重试
.setExceptions(TimeoutException::class.java)
}
.circuitBreaker { config ->
config.setName("critical-service-cb")
.setFallbackUri("forward:/fallback/critical")
}
}
.uri("lb://critical-service")
.metadata(RESPONSE_TIMEOUT_ATTR, 15000) // 增加超时时间
.metadata(CONNECT_TIMEOUT_ATTR, 2000)
}
.build()
}问题 2:连接池耗尽
症状:出现连接池满的错误,新请求无法获取连接
解决方案:
kotlin
@Bean
fun optimizedHttpClient(): HttpClient {
val connectionProvider = ConnectionProvider.builder("custom")
.maxConnections(200) // 增加最大连接数
.maxIdleTime(Duration.ofSeconds(20)) // 设置空闲超时
.maxLifeTime(Duration.ofSeconds(60)) // 设置连接生命周期
.pendingAcquireTimeout(Duration.ofSeconds(5)) // 获取连接超时
.evictInBackground(Duration.ofSeconds(120)) // 后台清理间隔
.build()
return HttpClient.create(connectionProvider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
.responseTimeout(Duration.ofSeconds(10))
.doOnConnected { conn ->
conn.addHandlerLast(ReadTimeoutHandler(10))
.addHandlerLast(WriteTimeoutHandler(10))
}
}总结
Spring Cloud Gateway 的 HTTP 超时配置是确保微服务架构稳定性的重要机制。通过合理的全局配置和针对性的路由配置,我们可以:
- 提升系统稳定性:防止慢服务影响整体性能
- 优化用户体验:快速失败,避免长时间等待
- 精细化控制:根据不同业务场景设置合适的超时策略
- 增强可观测性:通过监控指标及时发现和解决问题
超时配置需要根据实际业务需求和系统性能进行调优,建议在生产环境中通过监控数据持续优化配置参数。
TIP
下一步学习
- Route Metadata Configuration - 学习路由元数据配置
- Global Filters - 了解全局过滤器的使用
- GatewayFilter Factories - 掌握网关过滤器工厂