This commit is contained in:
ray zhou
2026-06-29 14:51:55 +08:00
parent 225fb2bd28
commit 2dd9f17da9
319 changed files with 29461 additions and 9412 deletions

167
plans/HTTP-c8f2b444.plan.md Normal file
View File

@@ -0,0 +1,167 @@
<!-- c8f2b444-d26a-4f73-b3e8-317e7228c8ab -->
---
todos:
- id: "redis-keys"
content: "RedisKeyManagerService 增加 duration buckets/sum/count 三个 key 常量"
status: pending
- id: "prometheus-service"
content: "PrometheusService 实现 recordHttpRequestDuration + render histogram 输出"
status: pending
- id: "middleware-timing"
content: "PrometheusMiddleware 计时并在 finally 记录 duration"
status: pending
- id: "grafana-panels"
content: "app_http_services_dashboard.json 增加 P50/P95/P99/Avg 与 route 耗时面板"
status: pending
- id: "verify"
content: "curl /metrics 验证 + 跑 completion-report 门禁"
status: pending
isProject: false
---
# slot_wallet HTTP 响应时长监控
## 现状
当前链路:
```mermaid
sequenceDiagram
participant Client
participant Middleware as PrometheusMiddleware
participant Handler
participant Redis
participant Metrics as MetricsController
Client->>Middleware: HTTP Request
Middleware->>Handler: handler()
Handler-->>Middleware: Response
Middleware->>Redis: hIncrBy requests_total
Middleware-->>Client: Response
Note over Metrics,Redis: Prometheus scrape
Metrics->>Redis: hGetAll
Metrics-->>Client: text/plain metrics
```
- 中间件:[`slot_wallet/app/middleware/PrometheusMiddleware.php`](slot_wallet/app/middleware/PrometheusMiddleware.php) 在 `finally` 中调用 `recordHttpRequest()`,只记请求数。
- 指标服务:[`slot_wallet/app/service/PrometheusService.php`](slot_wallet/app/service/PrometheusService.php) 用 Redis Hash 存 counter经 [`slot_wallet/app/controller/MetricsController.php`](slot_wallet/app/controller/MetricsController.php) 的 `/metrics` 输出。
- Grafana[`slot_wallet/doc/grafana/app_http_services_dashboard.json`](slot_wallet/doc/grafana/app_http_services_dashboard.json) 仅查询 `app_http_requests_total` / `app_business_responses_total`
**结论:可以加响应时长**,且与现有架构兼容;推荐新增标准 Prometheus Histogram而不是改现有 counter。
---
## 目标指标
新增 metric`app_http_request_duration_seconds`histogram
| 子指标 | 含义 |
|--------|------|
| `_bucket{le="..."}` | 各耗时桶累计次数 |
| `_sum` | 累计耗时(秒) |
| `_count` | 观测次数 |
**Labels** 与现有请求 counter 保持一致:`service`, `method`, `route`, `status`,便于在 Grafana 与请求量 join。
**Histogram buckets**Webman API 常用区间):
`0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10, +Inf`
---
## 代码改动
### 1. Redis Key — [`slot_wallet/app/service/RedisKeyManagerService.php`](slot_wallet/app/service/RedisKeyManagerService.php)
新增 3 个常量(与现有 `PROMETHEUS_*` 命名一致):
- `PROMETHEUS_HTTP_REQUEST_DURATION_BUCKETS``prometheus:http:duration:buckets`
- `PROMETHEUS_HTTP_REQUEST_DURATION_SUM``prometheus:http:duration:sum`
- `PROMETHEUS_HTTP_REQUEST_DURATION_COUNT``prometheus:http:duration:count`
### 2. 记录耗时 — [`slot_wallet/app/service/PrometheusService.php`](slot_wallet/app/service/PrometheusService.php)
新增 `recordHttpRequestDuration(string $method, string $route, int $status, float $durationSeconds)`
- field 仍用 `json_encode` 存 labelsbucket 额外带 `le`
- `_bucket` / `_count``Redis::hIncrBy(..., 1)`
- `_sum``Redis::hIncrByFloat(..., $durationSeconds)`(避免整数毫秒精度损失)
- 对每个 `le` bucket`$durationSeconds <= le` 则递增;最后递增 `+Inf` bucket
- 抽取私有方法 `buildHttpRequestLabels()`,供 `recordHttpRequest` 与 duration 复用,避免重复 JSON 拼装
扩展 `render()`,在现有 counter 之后输出:
```text
# HELP app_http_request_duration_seconds HTTP request latency in seconds.
# TYPE app_http_request_duration_seconds histogram
app_http_request_duration_seconds_bucket{...,le="0.1"} N
...
app_http_request_duration_seconds_sum{...} X.XXX
app_http_request_duration_seconds_count{...} N
```
### 3. 中间件计时 — [`slot_wallet/app/middleware/PrometheusMiddleware.php`](slot_wallet/app/middleware/PrometheusMiddleware.php)
`process()` 开头 `$startedAt = microtime(true)``finally` 中:
```php
$durationSeconds = microtime(true) - $startedAt;
PrometheusService::recordHttpRequestDuration($method, $route, $status, $durationSeconds);
```
优化:将 `resolveRoute()` 结果缓存到局部变量,避免 `finally` 里重复解析;`recordHttpRequest``recordHttpRequestDuration` 共用同一份 `method/route/status`
耗时口径:**中间件包裹的完整 handler 执行时间**(含业务逻辑,不含 Prometheus 写 Redis 本身;写在 `finally` 末尾,影响极小)。
---
## Grafana 仪表盘扩展
更新 [`slot_wallet/doc/grafana/app_http_services_dashboard.json`](slot_wallet/doc/grafana/app_http_services_dashboard.json)在现有请求量面板下方新增一行「Latency」区域`version` 递增为 3
| 面板 | 类型 | PromQL 示例 |
|------|------|-------------|
| P50 Latency | stat | `histogram_quantile(0.50, sum by (le) (rate(app_http_request_duration_seconds_bucket{service="$service"}[$__rate_interval])))` |
| P95 Latency | stat | 同上 `0.95` |
| P99 Latency | stat | 同上 `0.99` |
| Avg Latency | stat | `sum(rate(..._sum...)) / sum(rate(..._count...))` |
| Latency Trend (P50/P95/P99) | timeseries | 三条 quantile 曲线unit=`s` |
| Top Slow Routes (P95) | bargauge | `topk(10, histogram_quantile(0.95, sum by (route, le) (rate(..._bucket{service="$service"}[$__range]))))` |
| Route Latency Details | table | 合并 route 的 P95、Avg、Requests与现有 Route Request Details 面板并列或扩列) |
所有新面板 `unit` 设为 `s`threshold 可按业务再调。
---
## 数据流(改后)
```mermaid
sequenceDiagram
participant Middleware as PrometheusMiddleware
participant Redis
participant Grafana
Middleware->>Middleware: startedAt = microtime(true)
Middleware->>Middleware: handler()
Middleware->>Redis: incr requests_total
Middleware->>Redis: incr duration buckets/sum/count
Note over Grafana: rate + histogram_quantile
Grafana->>Grafana: P50/P95/P99/Avg
```
---
## 验证步骤
1. 本地发若干 HTTP 请求到 slot_wallet含快/慢接口)。
2. `curl /metrics`,确认出现 `app_http_request_duration_seconds_bucket/_sum/_count`
3. Prometheus scrape 后,在 Grafana 导入更新后的 dashboard JSON选择 `service=slot_wallet`,确认 P50/P95 有数据。
4.`slot-backend-completion-report` 门禁(`php -l` + verify 脚本)。
---
## 注意事项
- **仅改 slot_wallet**(按你的选择);`slot_console` / `slot_pwa` 结构相同,后续可 copy 同一套改动。
- Redis Hash 会随 `route × status × bucket` 增长;与现有 `app_http_requests_total` 同一量级,可接受。
- Histogram 在 Prometheus 侧用 `rate()` + `histogram_quantile()` 算分位Grafana 查询写法与标准 Prometheus 一致。
- 无需改 Prometheus scrape 配置metric 名新增,非替换)。