简介
本文件为 DouPHP 构建“性能指标收集系统”的实施方案与实现说明,覆盖以下目标:
- 响应时间监控:API 接口响应时间、页面加载时间的统计方法。
- 吞吐量统计:QPS 监控、并发请求数统计、处理队列长度监控。
- 错误率监控:异常捕获、错误分类、错误趋势分析。
- 自定义指标:支持业务特定指标采集。
- 存储策略:选择时序数据库进行指标数据存储。
- 可视化方案:使用 Grafana 等工具展示监控数据。
本项目已具备日志基础设施与限流中间件,可作为指标采集的基础设施与触发点;同时存在按日聚合的统计能力(如聊天用量/日统计),可借鉴其模式扩展至通用性能指标。
项目结构
围绕性能指标收集,建议采用“采集—汇聚—存储—可视化”的分层设计:
- 采集层:在 HTTP 边界(中间件)和关键业务入口埋点,记录耗时、状态码、错误、资源占用等。
- 汇聚层:将高频指标以采样/批处理方式写入本地日志或消息通道,降低对主流程影响。
- 存储层:通过导出器/Agent 将指标写入时序数据库(如 Prometheus/TimescaleDB/OpenTelemetry)。
- 可视化层:Grafana 对接时序库,提供仪表盘与告警。
graph TB
A["HTTP 请求"] --> B["中间件<br/>ThrottleMiddleware / 安全头"]
B --> C["路由与控制器"]
C --> D["业务服务"]
D --> E["外部依赖<br/>DB/缓存/第三方API"]
B --> F["指标采集<br/>耗时/状态码/错误"]
F --> G["日志/事件输出<br/>Log::write()"]
G --> H["导出器/Agent<br/>Prometheus/OTel"]
H --> I["时序数据库"]
I --> J["Grafana 仪表盘"]
[该图为概念性架构图,不直接映射具体源码文件]
核心组件
- 日志与上下文:Log 类提供分级日志、自动上下文补全、敏感信息脱敏、采样与限流,适合作为指标事件的载体。
- 限流中间件:ThrottleMiddleware 在 API 边界拦截超限请求并返回 429,天然适合统计 QPS、拒绝率、重试后延迟等。
- 启动初始化:前端 Init 在引导阶段完成核心对象实例化与日志运行时初始化,是全局埋点的合适位置。
- 统计聚合范式:订单对账与聊天日统计展示了“批量扫描+聚合统计”的模式,可用于定时汇总 QPS、错误率、耗时分位等。
章节来源
- core/infra/log/Log.php:24-158
- core/infra/log/Log.php:281-342
- core/infra/log/Log.php:349-385
- core/infra/log/Log.php:518-571
- api/middleware/ThrottleMiddleware.php:25-90
- front/init/Init.php:187-212
- _'/module/order/core/service/order/OrderReconciliation.php:49-93
架构总览
下图展示从请求进入、指标采集到可视化的端到端流程,结合现有 Log 与 ThrottleMiddleware 作为采集点。
sequenceDiagram
participant C as "客户端"
participant M as "ThrottleMiddleware"
participant R as "路由/控制器"
participant S as "业务服务"
participant L as "Log 类"
participant P as "导出器/Agent"
participant T as "时序数据库"
participant G as "Grafana"
C->>M : 发起请求
M->>M : 计算窗口内计数/判定限流
M-->>C : 429/放行
M->>R : 继续管道
R->>S : 执行业务逻辑
S-->>R : 返回结果
R-->>C : 响应
M->>L : 记录指标(耗时/状态码/错误/路由)
L->>P : 写入日志/事件
P->>T : 写入时序数据
G->>T : 查询并渲染
图表来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:281-342
章节来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:281-342
详细组件分析
响应时间监控(API 与页面)
- 采集点
- API 边界:在 ThrottleMiddleware 中记录请求开始/结束时间,计算耗时,并附带 route、method、status_code、user_id、ip 等上下文。
- 页面加载:在前端 Init 引导阶段开启全局计时,或在视图渲染前后记录耗时。
- 指标字段
- duration_ms、http_status、route、method、module、action、user_id、ip、scene、channel。
- 写入方式
- 通过 Log::write 以结构化 context 输出,便于后续抽取为指标行。
- 注意事项
- 高吞吐场景下启用采样与每分钟同 key 限流,避免日志风暴。
- 敏感字段自动脱敏,避免泄露 token、密码等。
flowchart TD
Start(["请求进入"]) --> T1["记录开始时间"]
T1 --> Exec["执行路由/控制器/服务"]
Exec --> End["记录结束时间"]
End --> Calc["计算耗时/状态码"]
Calc --> Write["Log::write(context)"]
Write --> Done(["返回响应"])
图表来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:281-342
章节来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:281-342
- front/init/Init.php:187-212
吞吐量统计(QPS、并发、队列长度)
- QPS 监控
- 基于 ThrottleMiddleware 的窗口计数,统计单位时间内的请求量与拒绝次数,计算 QPS 与拒绝率。
- 可按 route/module/action 维度聚合。
- 并发请求数
- 在中间件入口处递增计数器,出口递减;或使用进程级/容器级指标(如 PHP-FPM 活跃 worker)。
- 处理队列长度
- 若引入异步任务队列(如 Redis/RabbitMQ),可在队列消费侧记录队列深度;当前代码未见统一队列,可复用“订单对账”的批量扫描思路,周期性统计待处理任务数量。
- 聚合与持久化
- 参考 OrderReconciliation 的批处理模式,定时任务扫描并汇总指标,再写入日志/导出器。
classDiagram
class ThrottleMiddleware {
+handle(next)
-throttleFor(module, action, sub) array
-reject(retryAfter) void
}
class Log {
+write(level, message, context) void
+setSampleRate(rate) void
+setMaxPerMinutePerKey(max) void
}
class OrderReconciliation {
+reconcilePendingPayments(options) array
}
ThrottleMiddleware --> Log : "记录QPS/拒绝"
OrderReconciliation ..> Log : "批处理统计范式"
图表来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:281-342
- _'/module/order/core/service/order/OrderReconciliation.php:49-93
章节来源
- api/middleware/ThrottleMiddleware.php:25-90
- _'/module/order/core/service/order/OrderReconciliation.php:49-93
错误率监控(异常捕获、分类、趋势)
- 异常捕获
- 在中间件/控制器层捕获异常,记录 error/critical 级别日志,包含堆栈、错误类型、路由、用户信息等。
- 错误分类
- 基于错误类型、HTTP 状态码、模块/路由维度进行分类;利用 Log 的 channel 与 scene 区分 admin/api/front。
- 趋势分析
- 通过日志导出器将错误事件写入时序库,按时间窗口聚合错误率;设置阈值告警。
- 安全与脱敏
- 利用 Log 的敏感键匹配与 URL 查询串脱敏,避免泄露凭据。
flowchart TD
EStart(["发生异常"]) --> Catch["捕获异常"]
Catch --> Classify{"分类"}
Classify --> |HTTP错误| H["记录状态码/路由"]
Classify --> |业务异常| B["记录错误类型/堆栈"]
H --> Write["Log::write(error/context)"]
B --> Write
Write --> Export["导出器/Agent"]
Export --> TSDB["时序数据库"]
图表来源
- core/infra/log/Log.php:281-342
- core/infra/log/Log.php:412-499
章节来源
- core/infra/log/Log.php:281-342
- core/infra/log/Log.php:412-499
自定义指标(业务特定性能指标)
- 设计原则
- 指标命名规范:metric_name{labels},标签包含 module、action、route、user_id、ip、scene、channel。
- 数值型指标:duration_ms、qps、error_count、queue_length、tokens_used 等。
- 事件型指标:request、response、error 等。
- 接入方式
- 在业务服务关键路径调用 Log::write,携带结构化 context;或通过专用指标写入函数封装。
- 在高吞吐路径启用采样与限流,避免影响性能。
- 示例(路径引用)
- 参考聊天日统计的语言键与控制器,理解“按维度聚合”的指标组织方式。
章节来源
- admin/controller/chat/DailyStatsController.php:26-51
- _'/module/chat/languages/zh_cn/admin/chat.lang.php:206-251
指标数据存储策略
- 推荐时序数据库
- Prometheus:适合拉取模型,易于与 Grafana 集成,适合 QPS、错误率、耗时分位等。
- TimescaleDB/InfluxDB:适合高写入吞吐与长周期保留,适合日志/事件转指标。
- OpenTelemetry:统一采集与导出,兼容多后端。
- 写入路径
- 应用层通过 Log 输出结构化日志 → 日志采集 Agent(如 Fluent Bit/Filebeat)→ 解析为指标 → 写入时序库。
- 或直接通过 SDK(Prometheus Client/OTel)写入。
- 保留与降采样
- 短期高精度(分钟级)、长期低精度(小时/天级)降采样,控制存储成本。
指标可视化方案(Grafana)
- 数据源
- 配置 Grafana 数据源指向时序数据库(Prometheus/TimescaleDB)。
- 仪表盘
- 核心面板:QPS、错误率、P50/P95/P99 耗时、拒绝率、队列长度。
- 维度切片:按 route/module/action/user_id/ip 分组。
- 告警
- 基于阈值(如错误率>1%、P99>2s、QPS突降)设置告警规则,通知渠道(邮件/IM)。
依赖关系分析
- 中间件依赖
- ThrottleMiddleware 依赖抽象限流基类与 ApiResponse,负责在 HTTP 边界拦截与统计。
- 日志依赖
- Log 依赖 Session、Request、Container,用于自动补全上下文(route、user_id、ip、scene 等)。
- 启动依赖
- front/init/Init 在引导阶段完成核心对象实例化与日志运行时初始化,确保全局可用。
- 系统常量
- config/system.php 定义固定模块与保留段,有助于路由与模块维度的指标聚合。
graph LR
TM["ThrottleMiddleware"] --> API["ApiResponse"]
TM --> LOG["Log"]
INIT["front/init/Init"] --> LOG
LOG --> REQ["Request/Session/Container"]
SYS["config/system.php"] --> ROUTE["路由/模块维度"]
图表来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:518-571
- front/init/Init.php:187-212
- config/system.php:11-34
章节来源
- api/middleware/ThrottleMiddleware.php:25-90
- core/infra/log/Log.php:518-571
- front/init/Init.php:187-212
- config/system.php:11-34
性能考量
- 采样与限流
- 使用 Log::setSampleRate 与 setMaxPerMinutePerKey 控制高频指标写入,避免阻塞主流程。
- 上下文开销
- 自动上下文补全会读取 Request/Session/Container,建议在非调试环境关闭不必要字段。
- 存储压力
- 日志文件按日滚动,定期清理;导出器应缓冲写入,避免频繁 IO。
- 指标粒度
- 高 QPS 接口优先使用聚合指标(如每秒计数、分位耗时),减少明细落盘。
故障排查指南
- 常见问题
- 指标缺失:检查中间件是否命中、Log 是否启用、采样率是否过高。
- 日志风暴:调整采样率与每分钟同 key 限制,确认敏感字段未导致重复 key。
- 错误分类不准:确认错误类型与状态码标注完整,channel/scene 是否正确。
- 定位步骤
- 查看当日日志文件,过滤 error/critical 级别,结合 request_id 追踪链路。
- 在 ThrottleMiddleware 中增加 debug 日志,验证限流判定与计数。
- 校验导出器/Agent 是否正常解析日志并写入时序库。
章节来源
- core/infra/log/Log.php:281-342
- core/infra/log/Log.php:349-385
- api/middleware/ThrottleMiddleware.php:25-90
结论
通过在 HTTP 边界(中间件)与关键业务入口埋点,结合现有 Log 类的结构化上下文与采样/限流能力,可以低成本地实现 DouPHP 的性能指标收集。配合时序数据库与 Grafana,能够实现对 QPS、错误率、耗时等核心指标的实时监控与可视化,支撑容量规划与故障快速定位。
附录
- 指标字典(建议)
- http_request_duration_seconds{method,route,module,action,status,user_id,ip,scene,channel}
- http_requests_total{method,route,module,action,status,scene,channel}
- http_errors_total{type,route,module,action,scene,channel}
- queue_length{queue_name,scene,channel}
- 实施清单
- 在 ThrottleMiddleware 中记录耗时与状态码。
- 在 front/init/Init 中初始化全局计时与日志运行时。
- 配置 Log 采样率与每分钟同 key 限制。
- 部署导出器/Agent,将日志转为指标写入时序库。
- 在 Grafana 创建仪表盘与告警规则。