必须用 microtime(true) 包裹整个请求生命周期记录真实耗时,因其精度达微秒且涵盖 DNS、SSL、重试等全部用户感知环节;CURLINFO_TOTAL_TIME 仅统计 cURL 内部网络阶段,偏差 5–20ms,不可替代。

用 microtime(true) 记录真实请求耗时
AI 接口(比如调用 curl_exec() 或 file_get_contents())的响应时间不能靠前端打点或日志时间差估算,必须在 PHP 请求发起前后用高精度时间戳采样。PHP 的 microtime(true) 返回浮点秒数,精度到微秒,是唯一可靠选择。
常见错误是只记开始不记结束,或把 microtime()(无参数)当浮点用——它返回字符串,直接相减会出错。
- 务必用
microtime(true),不是microtime() - 记录点要包住实际网络 I/O:从
curl_exec()调用前,到它返回后立即记录 - 别把 DNS 解析、SSL 握手、重试逻辑漏在计时外——它们都算用户感知的“响应时间”
$start = microtime(true);
$ch = curl_init('https://api.example.ai/v1/chat');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch); // 网络耗时发生在这里
$end = microtime(true);
$duration = $end - $start; // 单位:秒,保留小数点后 6 位足够区分 CURLINFO_TOTAL_TIME 和自测耗时
有人用 curl_getinfo($ch, CURLINFO_TOTAL_TIME) 替代手动计时,但它只统计 cURL 库内部的网络阶段,不包含 PHP 层开销(如 JSON 编码、header 构造、异常处理),也不含连接复用判断、DNS 缓存查找等隐式行为。实测中,CURLINFO_TOTAL_TIME 比 microtime 差值常低 5–20ms,尤其在高并发下偏差更明显。
- 监控目标是“用户等待总时间”,必须用
microtime(true)包裹整个请求生命周期 -
CURLINFO_TOTAL_TIME可作为辅助指标,用于排查是否卡在网络栈,但不能替代主耗时字段 - 若启用了
CURLOPT_CONNECTTIMEOUT_MS或重试逻辑,这些时间也必须计入主耗时
聚合统计不能只存原始值,得实时计算分位数
存一堆 $duration 到数据库或 Redis,后续查 P95/P99 会非常慢。尤其 AI 接口响应波动大(可能 100ms 也可能 3s),平均值完全失真。必须在写入时就做轻量级流式统计。
立即学习“PHP免费学习笔记(深入)”;
- 推荐用
tdigest类库(如 jeremeamia/tdigest)做内存内分位估算,误差 - 简单场景可用滑动窗口:用 Redis 的
LPUSH + LTRIM存最近 1000 次耗时,定时用SORT取 P95 —— 但注意避免阻塞主线程 - 别用 MySQL
AVG()或PERCENT_RANK()实时算,QPS 高时 IO 和 CPU 都扛不住
注意 OpenAI 类接口的流式响应(text/event-stream)计时陷阱
当用 stream=true 请求 ChatGPT 风格接口时,curl_exec() 会等到流结束才返回,但用户其实从第一个 chunk 就开始看到内容。此时“首字节时间(TTFB)”比“总耗时”更有业务意义。
- 需改用
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback),在回调里检测首次写入并打点first_byte_time - 总耗时仍用
microtime包裹curl_exec(),但必须和 TTFB 分开存储、分开告警 - 流式场景下,超时设置要拆开:
CURLOPT_TIMEOUT_MS控制整体,CURLOPT_LOW_SPEED_LIMIT防止卡死
AI 接口性能监控真正的难点不在采集,而在定义“慢”的上下文——同一个 800ms 响应,对摘要任务可能是异常,对长文本生成却算正常。指标必须绑定业务标签(如 model=gpt-4-turbo, input_tokens=1200)才能有效归因。



















