PAYATHON 2026

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,避免直接信任客户端可以伪造的请求头。

备注:内容仅供参考。