PHP 网站如何异步处理延迟 IP 封禁?
结论
PHP 可以启动后台进程,但你贴出的 cURL 调用是同步的:curl_exec() 会等远程 absolve.php 返回,当前请求也会一直被占用。要实现“登录失败后等待 5 分钟,期间登录成功就取消,否则封禁 IP”,通常不需要让 PHP 进程 sleep(300)。可以把待处理状态和到期时间存起来,再由后续请求或定时任务处理。
用待处理记录实现 5 分钟窗口
下面以数据库为例。登录失败时,为该 IP 写入到期时间;访客在 5 分钟内登录成功时,删除对应的待处理记录。记录到期后仍存在,就将 IP 加入封禁表。这里假设使用 MySQL,字段和连接代码需要根据你的项目调整。
CREATE TABLE pending_ip_blocks (
ip VARCHAR(45) PRIMARY KEY,
expires_at DATETIME NOT NULL
);
CREATE TABLE blocked_ips (
ip VARCHAR(45) PRIMARY KEY,
blocked_at DATETIME NOT NULL
);
登录失败时记录到期时间:
INSERT INTO pending_ip_blocks (ip, expires_at)
VALUES (?, UTC_TIMESTAMP() + INTERVAL 5 MINUTE)
ON DUPLICATE KEY UPDATE expires_at = VALUES(expires_at);
登录成功时取消待处理封禁:
DELETE FROM pending_ip_blocks WHERE ip = ?;
处理器查找已到期的记录,将 IP 写入封禁表,再清理待处理记录。封禁表以 IP 为主键,可以避免重复写入:
INSERT IGNORE INTO blocked_ips (ip, blocked_at)
SELECT ip, UTC_TIMESTAMP()
FROM pending_ip_blocks
WHERE expires_at <= UTC_TIMESTAMP();
DELETE FROM pending_ip_blocks
WHERE expires_at <= UTC_TIMESTAMP();
你可以用服务器的 cron 定期运行这个处理器。若每分钟运行一次,实际封禁时间最多会比到期时间晚约一分钟。若要在到期后尽快处理,可以使用运行频率更高的队列或常驻 worker。也可以在每次登录请求开始时,先检查并处理该 IP 的到期记录;但如果访客到期后没有再次发起请求,封禁表就不会立刻更新。
为什么现有尝试会等待
-
sleep(300)会占用正在执行的 PHP 进程。若activator.php直接调用absolve.php,当前请求就会一直等待。 -
curl_exec()默认会等请求完成后才返回。它会请求另一个 URL,但不会自动把调用变成后台任务。 -
exec("... &")在允许启动 shell 子进程的环境中可以用于后台执行。不过,共享主机可能禁用了exec;命令路径、PHP CLI 路径或进程管理策略也可能导致启动失败。重定向标准错误也有助于避免命令输出管道让调用继续等待,例如:exec('nohup /usr/bin/php /path/to/absolve.php >/dev/null 2>&1 &');/usr/bin/php和脚本路径只是示例,需要替换成服务器上的实际路径。不要用chmod 777解决问题。这样会放宽文件权限,也无法绕过主机对exec的限制。
注意事项
blocked.txt 可以用于简单存储,但多个 PHP 请求同时追加时,需要处理文件锁和读写错误。数据库或队列更适合保存待处理任务。认证流程也应检查 IP 是否已封禁,并确定在代理环境下如何可信地获取访客 IP,避免直接信任客户端可以伪造的请求头。
备注:内容仅供参考。
