接口请求成功率应定义为:收到服务端HTTP响应且状态码≥400,或捕获到可归因于服务端的HttpException/RequestException(需hasResponse()为true)的失败请求数,占有效响应请求总数的比例;需排除客户端取消、网络超时、DNS失败等非服务端因素。

接口请求成功率怎么定义才合理
成功率不是简单用「成功请求数 / 总请求数」,得排除客户端主动取消、网络超时重试、DNS失败等非业务侧可控因素。ThinkPHP 项目里,think\facade\Http 或第三方 SDK(如 Guzzle)发起的请求,真正能归因到后端接口质量的,是那些收到 HTTP 响应但状态码非 2xx/3xx 的情况,以及明确抛出 HttpException、RequestException 等可捕获异常的调用。
建议只统计以下两类:
- HTTP 响应状态码 ≥ 400 且响应体含有效 JSON(说明服务端已处理,只是失败)
- 捕获到
think\exception\HttpException或GuzzleHttp\Exception\RequestException(不含 ConnectionException、TimeoutException)
在控制器中埋点统计的实操方式
不要在每个接口里手写计数器,容易漏、难维护。推荐统一拦截 + 中间件记录。但若只是快速验证或小范围统计,可在关键接口方法内加轻量埋点:
// 示例:在 UserController::login() 中统计调用第三方验签接口的成功率
try {
$resp = \think\facade\Http::post('https://api.example.com/verify', $data);
if ($resp->getStatusCode() >= 400) {
// 记录失败:状态码非 2xx/3xx,且响应可解析
\think\facade\Log::info('thirdparty_verify_fail', [
'status' => $resp->getStatusCode(),
'body' => $resp->getBody()->getContents(),
]);
$success = false;
} else {
$success = true;
}
} catch (\GuzzleHttp\Exception\RequestException $e) {
// 只记录服务端返回了响应但被判定为异常的情况
if ($e->hasResponse()) {
\think\facade\Log::info('thirdparty_verify_exception', [
'status' => $e->getResponse()->getStatusCode(),
'msg' => $e->getMessage(),
]);
$success = false;
} else {
// 连接失败、超时等不计入成功率分母
throw $e;
}
}注意:$e->hasResponse() 是关键判断——没响应的错误不算进成功率计算。
立即学习“PHP免费学习笔记(深入)”;
用 Redis 做实时成功率聚合的要点
每秒可能有几百次调用,直接写数据库扛不住,用 Redis 的 HINCRBY 按分钟维度聚合最实用:
- Key 设计:
api:success_rate:20240520:14:35(年月日+小时+分钟) - Hash field:
total和success两个字段分别自增 - 写入前用
EXPIRE设置 7 天过期,避免堆积
示例代码:
$key = 'api:success_rate:' . date('Ymd:H:i');
$redis = \think\facade\Cache::store('redis')->handler();
$redis->hIncrBy($key, 'total', 1);
if ($success) {
$redis->hIncrBy($key, 'success', 1);
}
$redis->expire($key, 86400 * 7);别用 INCR 单 key 存两个值——并发下读-改-写会丢数据;HINCRBY 是原子操作,安全。
查不到成功率?检查这三处硬伤
上线后发现 Redis 里没数据或成功率恒为 0,大概率卡在这几个地方:
- 中间件或控制器里没真正触发
hIncrBy—— 检查是否在 try/catch 外层就 return 了,或者异常被上层全局异常处理器吞掉没走到埋点 - Redis 配置用了
default缓存驱动而非redis,导致Cache::store('redis')初始化失败,静默降级成 File 缓存 - 时间 key 用
date('Y-m-d H:i')这种带空格/冒号的格式,Redis key 不允许,必须用Ymd:H:i或其他无特殊字符格式
最常被忽略的是缓存驱动名写错——ThinkPHP 的 store('redis') 要求配置文件里存在名为 redis 的独立缓存连接,不是随便起个名字就能用。



















