接口请求统计应在ThinkPHP中间件中埋点,因其能统一拦截所有请求、安全获取鉴权后的用户ID、避免业务侵入;需用Redis缓存或批量写入提升性能,数据库表须建复合索引以支撑高效聚合查询。

接口请求统计需要在哪个位置埋点
ThinkPHP 的接口请求统计不能只靠前端上报,必须在服务端统一拦截。最稳妥的位置是中间件(Middleware)或控制器基类的 initialize() 方法——中间件更干净,不侵入业务逻辑。
推荐用中间件,因为:
• 所有 HTTP 请求都会经过(包括未登录用户)
• 可以提前拿到 $request->param('user_id') 或从 token 解析出用户 ID
• 避免每个控制器重复写统计逻辑
如何安全获取用户 ID 并避免空值/伪造
直接读 input('user_id') 极其危险:参数可被任意篡改,且未登录时可能为空。真实项目中必须依赖鉴权后已解析的用户上下文。
常见可靠来源:
- 使用 JWT 或 session 登录后,把用户 ID 存在
$this->auth->id(若用 think-auth)或session('user.id') - 若用 API Token,在中间件中调用
TokenService::parse($token)获取 payload 中的uid - 绝不信任
input('user_id')、param('uid')等原始请求参数
示例中间件片段:
public function handle($request, \Closure $next)
{
$userId = $this->getCurrentUserId($request);
if ($userId) {
Db::name('api_log')->insert([
'user_id' => $userId,
'url' => $request->url(true),
'method' => $request->method(),
'create_time' => time()
]);
}
return $next($request);
}
<p>protected function getCurrentUserId($request)
{
// 假设已通过 token 或 session 设置了 user_id 到 request 属性
return $request->has('user_id') ? $request->user_id : null;
}
</p>
数据库表设计和写入性能怎么扛住高并发
高频接口下,每请求都 INSERT 会拖慢响应,甚至压垮数据库。必须做缓冲或异步处理。
两个轻量级方案:
- 用 Redis 缓存计数:
incr api:count:20240521:123(按日+用户 ID 分桶),定时脚本汇总入库 - 用 ThinkPHP 的
Db::execute()批量写入:把多个日志暂存在内存数组,每 50 条或每 2 秒 flush 一次 - 避免在中间件里做耗时操作(如查询用户昵称、写文件),只记录必要字段
表结构建议最小化:
CREATE TABLE `api_log` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `user_id` int unsigned NOT NULL DEFAULT '0', `url` varchar(255) NOT NULL, `method` char(6) NOT NULL, `create_time` int unsigned NOT NULL, PRIMARY KEY (`id`), KEY `idx_user_time` (`user_id`,`create_time`) ) ENGINE=InnoDB;
按用户 ID 统计的 SQL 查询怎么写才高效
统计不是查全量日志,重点是聚合。别用 SELECT * 加 PHP 循环计数,直接交给数据库。
常用统计场景对应写法:
- 今日每个用户请求次数:
SELECT user_id, COUNT(*) FROM api_log WHERE create_time >= 1716537600 GROUP BY user_id - 某用户最近 7 天每日请求数:
SELECT FROM_UNIXTIME(create_time, '%Y-%m-%d') AS day, COUNT(*) FROM api_log WHERE user_id = 123 AND create_time >= UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 7 DAY)) GROUP BY day - 请求 Top 10 用户:
SELECT user_id, COUNT(*) AS cnt FROM api_log WHERE create_time >= UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY) GROUP BY user_id ORDER BY cnt DESC LIMIT 10
注意:create_time 字段必须建索引,否则 WHERE + GROUP BY 会全表扫描。
真正难的不是写 SQL,而是决定“统计口径”:是否过滤 401/404 请求?是否合并重试请求?这些逻辑得在埋点阶段就对齐,后期很难修正。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











