基于 Spring Cloud Gateway 的 LLM API 性能监控组件,专为大语言模型 API 网关设计,提供精确的请求时序追踪和 Prometheus 指标暴露。
-
精确的时序追踪
- TTFT (Time To First Token):首字节响应时间
- 总响应时间:请求到响应完成的全链路耗时
- 响应数据量:实时统计响应字节数
-
流式和非流式支持
- 自动识别 SSE (Server-Sent Events) 流式响应
- 兼容标准 JSON 非流式响应
- 支持
text/event-stream和其他流式 Content-Type
-
灵活的路由匹配
- 基于 Route ID 的精确匹配
- 基于 Route ID 前缀的模糊匹配
- 基于路径模式的 Ant 风格匹配
-
多维度标签
- Request ID:唯一请求标识
- Model:LLM 模型名称(低基数,适合作为 Prometheus 标签)
- Tenant:租户标识(高基数,默认仅记录日志)
- Status Code:HTTP 状态码及状态类别(2xx, 4xx, 5xx)
- Outcome:请求结果(success, client_error, server_error, cancelled, error)
-
双通道输出
- Prometheus Metrics:通过
/actuator/prometheus端点暴露,支持 Grafana 可视化 - Structured Logging:结构化日志输出,便于日志分析和告警
- Prometheus Metrics:通过
- JDK 21+
- Maven 3.6+
- Spring Boot 3.4+
- Spring Cloud Gateway 2024.0.2+
./mvnw clean package./mvnw spring-boot:run- 健康检查: http://localhost:8080/actuator/health
- 指标查询: http://localhost:8080/actuator/metrics
- Prometheus: http://localhost:8080/actuator/prometheus
llm:
gateway:
timing:
# 全局开关
enabled: true
# 过滤器顺序(默认在 NettyWriteResponseFilter 之前)
order: -2
# 精确匹配的路由ID列表
route-ids:
- llm-openai-route
- llm-anthropic-route
# 路由ID前缀匹配(满足其一即生效)
route-id-prefixes:
- llm
- ai
# 路径模式匹配(Ant 风格)
path-patterns:
- /v1/chat/completions
- /v1/completions
- /v1/responses
- /**/v1/chat/completions
- /**/v1/completions
# 自定义请求头名称
header-names:
request-id: X-Request-Id
model: X-LLM-Model
tenant: X-Tenant-Id
# Prometheus 指标配置
metrics:
enabled: true
meter-prefix: llm.gateway
include-model-tag: true # model 作为标签(低基数)
include-tenant-tag: false # tenant 不作为标签(高基数)
# 日志配置
logging:
enabled: true
# Spring Boot Actuator 配置
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
llm.gateway.timing.enabled |
Boolean | true | 全局开关 |
llm.gateway.timing.order |
Integer | -2 | GlobalFilter 执行顺序 |
llm.gateway.timing.route-ids |
List | [] | 精确匹配的路由ID |
llm.gateway.timing.route-id-prefixes |
List | ["llm"] | 路由ID前缀 |
llm.gateway.timing.path-patterns |
List | 见配置文件 | Ant 风格路径模式 |
llm.gateway.timing.header-names.request-id |
String | X-Request-Id | 请求ID的请求头名称 |
llm.gateway.timing.header-names.model |
String | X-LLM-Model | 模型名称的请求头名称 |
llm.gateway.timing.header-names.tenant |
String | X-Tenant-Id | 租户ID的请求头名称 |
llm.gateway.timing.metrics.enabled |
Boolean | true | 是否启用 Prometheus 指标 |
llm.gateway.timing.metrics.meter-prefix |
String | llm.gateway | 指标名称前缀 |
llm.gateway.timing.metrics.include-model-tag |
Boolean | true | 是否将 model 作为标签 |
llm.gateway.timing.metrics.include-tenant-tag |
Boolean | false | 是否将 tenant 作为标签 |
llm.gateway.timing.logging.enabled |
Boolean | true | 是否启用日志记录 |
组件会自动注册以下 Prometheus 指标:
Time To First Token (TTFT) - 首字节响应时间
- 类型: Timer
- 标签:
route_id: 路由IDmethod: HTTP 方法(GET, POST)status_class: 状态类别(2xx, 4xx, 5xx)stream_mode: 响应模式(stream, non_stream, unknown)model: LLM 模型名称(当include-model-tag=true)tenant: 租户ID(当include-tenant-tag=true)
Total Response Time - 总响应时间
- 类型: Timer
- 标签: 同上
Response Bytes - 响应数据量
- 类型: Distribution Summary
- 标签: 同上
{
"event": "llm_timing_first_output",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"route_id": "llm-openai-route",
"method": "POST",
"path": "/v1/chat/completions",
"model": "gpt-4",
"tenant": "customer-001",
"status": 200,
"status_class": "2xx",
"stream_mode": "stream",
"start_epoch_millis": 1717516800000,
"ttft_nanos": 125000000,
"ttft_ms": 125.0,
"response_bytes": 0,
"event_type": "first_output"
}{
"event": "llm_timing_completed",
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"route_id": "llm-openai-route",
"method": "POST",
"path": "/v1/chat/completions",
"model": "gpt-4",
"tenant": "customer-001",
"status": 200,
"status_class": "2xx",
"stream_mode": "stream",
"start_epoch_millis": 1717516800000,
"ttft_nanos": 125000000,
"ttft_ms": 125.0,
"total_nanos": 3500000000,
"total_ms": 3500.0,
"response_bytes": 2048,
"signal_type": "ON_COMPLETE",
"outcome": "success"
}spring:
cloud:
gateway:
routes:
- id: llm-openai-route
uri: https://api.openai.com
predicates:
- Path=/openai/**
filters:
- StripPrefix=1curl -X POST http://localhost:8080/openai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "X-Request-Id: test-001" \
-H "X-LLM-Model: gpt-4" \
-H "X-Tenant-Id: customer-001" \
-d '{
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Hello"}
]
}'# 查询 TTFT P95
curl http://localhost:8080/actuator/prometheus | grep llm_gateway_ttft
# 示例输出
llm_gateway_ttft_seconds{model="gpt-4",route_id="llm-openai-route",status_class="2xx",stream_mode="stream",quantile="0.95"} 0.15在 Grafana 中使用以下 PromQL 查询:
# TTFT P95 分模型统计
histogram_quantile(0.95,
sum(rate(llm_gateway_ttft_seconds_bucket[5m])) by (le, model)
)
# 总响应时间 P99
histogram_quantile(0.99,
sum(rate(llm_gateway_total_seconds_bucket[5m])) by (le)
)
# 响应数据量趋势
rate(llm_gateway_response_bytes_sum[5m])
LlmTimingGlobalFilter (GlobalFilter)
↓
├─ LlmGatewayTimingProperties (配置)
│
└─ LlmTimingRecorder (接口)
├─ MicrometerLlmTimingRecorder (Prometheus)
└─ LoggingLlmTimingRecorder (日志)
- 请求拦截:
LlmTimingGlobalFilter作为GlobalFilter在路由前拦截请求 - 响应装饰: 使用
ServerHttpResponseDecorator包装原始响应 - 流式监听: 通过
writeWith和writeAndFlushWith监听响应数据流 - 时序记录:
- 记录请求开始时间(
startNanos) - 捕获首字节到达时间(
firstOutputNanos) - 记录请求结束时间(
endNanos)
- 记录请求开始时间(
- 数据统计: 累加响应字节数(
responseBytes) - 多通道输出: 调用所有
LlmTimingRecorder实现,输出到 Prometheus 和日志
- 非侵入式: 基于 Spring Cloud Gateway 的 GlobalFilter 机制,无需修改业务代码
- 响应式: 完全兼容 WebFlux 响应式编程模型,支持背压和流式处理
- 高性能: 使用
AtomicLong和LongAdder实现无锁统计 - 容错性: 统计逻辑异常不会影响业务请求(try-catch 保护)
- 低基数: 默认将高基数字段(如 tenant)排除在 Prometheus 标签外,避免指标爆炸
- Spring Boot: 3.4.6
- Spring Cloud Gateway: 2024.0.2
- Java: 21
- Micrometer: Registry Prometheus
- Reactive Streams: Project Reactor
- Maven: 3.6+
src/main/java/com/glmapper/llm/metrics/
├── LlmGatewayMetricsApplication.java # 主启动类
├── LlmGatewayTimingConfiguration.java # 自动配置类
├── LlmGatewayTimingProperties.java # 配置属性
├── LlmTimingGlobalFilter.java # 核心过滤器
├── LlmTimingRecorder.java # 记录器接口
├── LlmTimingSnapshot.java # 时序快照
├── MicrometerLlmTimingRecorder.java # Prometheus 记录器
└── LoggingLlmTimingRecorder.java # 日志记录器
llm:
gateway:
timing:
metrics:
include-model-tag: true # ✅ model 通常只有几十个值
include-tenant-tag: false # ❌ tenant 可能有成千上万个值优先使用 Route ID 匹配,性能更好:
llm:
gateway:
timing:
# 优先级1: 精确匹配
route-ids:
- llm-openai-route
# 优先级2: 前缀匹配
route-id-prefixes:
- llm
# 优先级3: 路径匹配(较慢)
path-patterns:
- /v1/chat/completionslogging:
level:
com.glmapper.llm.metrics.LoggingLlmTimingRecorder: INFOscrape_configs:
- job_name: 'llm-gateway'
metrics_path: '/actuator/prometheus'
scrape_interval: 15s
static_configs:
- targets: ['localhost:8080']