要让PHP在服务器上持续运行,核心是让脚本脱离Web请求生命周期,以CLI模式常驻内存,并配合Supervisor或systemd进行守护,而不是靠浏览器访问触发。 下面从运行模式、配置方法、守护方案到常见坑,一层层拆开讲。
为什么PHP脚本默认总是“跑一会就退下”
PHP原生设计是“短命进程”,Web模式下,每一次请求进来,PHP执行完就销毁所有变量、释放连接、退出进程,这是为了安全和高并发,但也是很多人在服务器上运行长任务时频繁中断的根源。
具体限制来自几个方面:
max_execution_time,默认30秒,超时强制终止php-fpm配置里的request_terminate_timeout,更严格- Nginx的
fastcgi_read_timeout,代理层也有自己的耐心
这些叠加起来,导致你在浏览器里访问一个循环处理的PHP脚本,几乎不可能撑过几分钟。
PHP脚本怎么在服务器后台持续运行?先分清两种模式
你首先要明白:Web模式跑不了长任务,CLI模式才是持续运行的容器。 CLI模式下PHP默认没有执行时间上限,你的脚本可以一直待在内存里干活。
核心配置:让脚本自己不逃跑
在CLI脚本里,这几行代码是地基:
set_time_limit(0); ignore_user_abort(true);
set_time_limit(0):执行时间设为无限ignore_user_abort(true):用户断开、父进程退出,脚本照常运行
但只有这两行还不够,你还需要一个循环结构:
while (true) {
// 从队列取任务
$job = $queue->pop();
if ($job) {
handle($job);
}
usleep(1000000); // 休息1秒,避免CPU空转
}
这就是一个最小可用的常驻脚本,把它保存为worker.php,然后在服务器上执行:
nohup php /www/wwwroot/yourproject/worker.php > /dev/null 2>&1 &
脚本就能在后台一直跑。nohup保证你关掉SSH后脚本不退出,&直接挂到后台。
为什么你手动启动的进程总在几天后消失
手动nohup太脆弱,服务器重启、脚本抛异常、内存涨爆被系统杀掉,你都察觉不到,更可靠的方式是交给守护进程管理,下面讲。
PHP常驻内存脚本怎么写?用Supervisor守护才稳定
Supervisor是Linux上非常常用的进程管家,它的职责很简单:盯着你的PHP脚本,挂掉就自动拉起来,还能统一看日志。
安装和配置Supervisor
以Ubuntu为例:
apt-get install supervisor
安装后写一个配置文件,放到/etc/supervisor/conf.d/里,比如worker.conf:
[program:php_worker] command=php /www/wwwroot/yourproject/worker.php autostart=true autorestart=true startretries=3 stdout_logfile=/var/log/php_worker.log stderr_logfile=/var/log/php_worker_err.log
然后执行:
supervisorctl reread supervisorctl update supervisorctl start php_worker
这样你的PHP脚本就真正进入“长期在岗”的状态了,即使进程崩溃,Supervisor检测到异常退出后会自动重启,几乎不需要人工干预。
不想装Supervisor?用systemd一样能守护
新版Linux发行版都内置systemd,用它写一个服务单元也能达到同样效果,创建/etc/systemd/system/php-worker.service:
[Unit] Description=PHP Worker Service WantedBy=multi-user.target [Service] ExecStart=/usr/bin/php /www/wwwroot/yourproject/worker.php Restart=always User=www-data Group=www-data [Install] WantedBy=multi-user.target
启动命令:
systemctl enable --now php-worker
systemd的Restart=always会保证进程退出后立即重启,这也是一套非常标准的php服务器长时间运行设置。
定时任务与常驻进程,我的业务该选哪种?
很多场景下你只需要定时触发,没必要搞常驻,选错方案会让运维变得复杂,这里给你一个判断标准:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 每5分钟清理一次缓存 | crontab | 简单,执行完自动退出,没有内存包袱 |
| 每天凌晨发邮件 | crontab | 低频任务,不值得常驻 |
| 队列消息秒级处理 | 常驻进程 | 省去反复启动PHP解释器的开销 |
| WebSocket服务 | 常驻进程(Swoole) | 需要长连接,必须持续监听端口 |
| 数据统计定时跑批 | crontab或常驻 | 看数据量,跑批超过10分钟建议拆任务 |
用crontab -e可以轻松加入定时任务:
/5 /usr/bin/php /www/wwwroot/yourproject/cleanup.php
定时任务每次启动一个全新进程,天然隔离了内存泄漏问题,这是它最大的优势。
内存一直涨怎么办?PHP常驻的生存之道
常驻脚本跑一周,内存从40MB涨到400MB,这是最常见的事故原因,PHP虽然会自动回收局部变量,但循环中积累的全局状态、静态数组、未关闭的数据库连接都会留在内存里。
几个实操建议:
- 每轮循环结束用
unset($large_data)手动释放大变量 - 定时调用
gc_collect_cycles()回收循环引用 - 用
memory_get_usage(true)监控内存占用,超过设定阈值就主动退出,让Supervisor重启,这是最粗暴也最有效的兜底方案
代码片段:
while (true) {
$job = $queue->pop();
if ($job) {
handle($job);
}
unset($job);
if (memory_get_usage(true) > 128 1024 1024) {
exit(0); // 内存超过128MB就退出,靠守护进程拉起来
}
usleep(1000000);
}
不要指望一个PHP进程永生不死,定期重启反而能让系统更稳定。
PHP持续运行的进阶姿势:Swoole常驻服务器
如果你需要高并发、长连接、实时通信,原生PHP的while(true)循环会显得单薄,Swoole扩展让PHP可以直接创建TCP/UDP服务器,进程常驻在内存里,事件驱动处理请求。
一个最简单的Swoole TCP服务器:
$server = new SwooleServer("0.0.0.0", 9501);
$server->on("Receive", function ($server, $fd, $reactor_id, $data) {
$server->send($fd, "收到:" . $data);
});
$server->start();
代码写完直接php server.php,进程就会一直运行,相比原生CLI脚本,Swoole自己管理内存和事件循环,不需要你再写while和usleep。
业内专家指出:Swoole把PHP从“短命请求”变成“常驻内存服务”,性能接近Node.js,但前提是你能接受线程安全、信号处理这些新概念,对于大多数中小业务,原生CLI+Supervisor已经够用,不必急着上Swoole。
排查:我的常驻进程又挂了
脚本无端消失又不写日志,大概率是系统级干预。
看看是不是被OOM Killer杀了
内存耗尽时,Linux内核会主动挑选进程杀掉,检查系统日志:
dmesg | grep -i kill
如果看到你的PHP进程名,说明内存确实超了,解决办法就是主动限制资源使用,或者增加机器内存,避免swap。
php set_time_limit(0)失效原因有哪些
你可能会在代码里写set_time_limit(0),但它依然被终止,常见原因:
- 处于
php-fpm管理下,request_terminate_timeout优先级更高 - Nginx的
fastcgi_read_timeout设置了代理超时,请求时段连接被切断 - 安全扩展或操作系统资源限制,比如
RLIMIT_CPU - 代码中使用了
pcntl_alarm自行设置了定时器,覆盖了全局配置
真正需要长跑的任务,永远不要放在Web入口里,走CLI才是一劳永逸的办法。
如何优雅地停止一个常驻PHP脚本
用Supervisor管理的话,直接:
supervisorctl stop php_worker
如果你希望脚本收到停止信号时先保存进度再退出,可以在脚本里注册信号处理器:
pcntl_async_signals(true);
pcntl_signal(SIGTERM, function () {
// 保存进度、关闭连接
exit(0);
});
这样脚本就不是被强杀,而是“交代好后事”再走。
说到这,核心其实已经清楚了:PHP持续运行的答案是脱离Web生命周期,进入CLI常驻,再交给Supervisor或systemd守护。
关于PHP服务器持续运行时间,你需要知道的3个问题
我的虚拟主机,能不能让PHP持续运行?
共享虚拟主机通常没有SSH登录,也没有Systemd或Supervisor,你能用的最高权限就是crontab,这种情况下,严格意义上的持续运行很难实现,更靠谱的做法是把长任务拆成一个个短任务,在crontab里缩短间隔执行。
常驻PHP脚本会拖垮服务器吗?
只要内存控制得当,常驻进程占用的资源远低于你想象的,一个轻量队列worker常驻内存大约50到100MB,CPU只在有任务时跳动,相比每次请求都重新加载整个框架(可能要300-500MB内存),常驻进程反而更省资源。
一个PHP进程最多能“扛”多久不退出?
在CLI模式下,只要代码不出致命错误、内存没耗尽、服务器不重启,理论上它能无限期运行,业务逻辑越复杂,内存碎片和资源泄漏的风险越高,所以圈子里更流行“跑一段时间自动退出,再由守护进程拉起来”的模式,而不是追求单个进程的永久存活。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/770312.html




