如何让PHP文件在Apache2中持续运行并可被其他PHP请求访问?
让PHP脚本持续运行并共享资源的几种实用方案
没问题,我完全懂你的需求——你想让一个PHP脚本一直驻留在服务器内存里,保持像$conn这样的长连接或者变量不被销毁,还能让其他PHP请求(比如你的index.php)调用它的资源对吧?下面给你几个生产环境里靠谱的方案,一步步来:
1. CLI常驻进程 + 进程间通信(最直接的原生方案)
PHP的CLI模式(命令行运行)不会像web请求那样执行完就销毁进程,所以我们可以把alwaysrunning.php做成一个常驻CLI脚本,再通过Unix套接字实现和web端PHP的通信。
第一步:编写常驻脚本
修改你的alwaysrunning.php,让它保持运行并监听套接字请求:
// alwaysrunning.php (必须用CLI模式运行) $serverip = '目标服务器IP'; $serverport = 目标端口; $conn = fsockopen($serverip, $serverport, $errno, $errstr, 30); if (!$conn) { die("初始连接失败: $errstr ($errno)\n"); } // 创建Unix套接字用于和web端通信 $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); $socketPath = '/tmp/php_always_running.sock'; // 清理旧的套接字文件(如果存在) if (file_exists($socketPath)) { unlink($socketPath); } socket_bind($socket, $socketPath); socket_listen($socket); // 设置非阻塞模式,避免进程卡住 socket_set_nonblock($socket); // 无限循环保持进程运行 while (true) { // 检查是否有web端的新请求 $clientSocket = socket_accept($socket); if ($clientSocket !== false) { // 读取web端发来的命令 $command = socket_read($clientSocket, 1024); if ($command !== false && !empty(trim($command))) { // 用持久化的$conn发送命令 fwrite($conn, trim($command) . "\n"); // 可选:给web端返回确认信息 socket_write($clientSocket, "命令已通过长连接发送\n"); } socket_close($clientSocket); } // 维护长连接:如果连接断开则重新连接 if (!is_resource($conn)) { echo "连接断开,尝试重连...\n"; $conn = fsockopen($serverip, $serverport, $errno, $errstr, 30); } // 休眠1秒,避免占用过多CPU资源 sleep(1); }
第二步:启动常驻进程
在服务器终端执行以下命令,让脚本后台持续运行:
nohup php /path/to/alwaysrunning.php > /dev/null 2>&1 &
nohup:保证你退出终端后进程依然运行> /dev/null 2>&1:把日志输出重定向到空,避免生成大量日志文件&:让进程在后台运行
第三步:web端调用(index.php)
修改你的index.php,通过套接字和常驻进程通信:
// index.php $socketPath = '/tmp/php_always_running.sock'; $socket = socket_create(AF_UNIX, SOCK_STREAM, 0); $connectResult = socket_connect($socket, $socketPath); if ($connectResult) { $command = '你要发送的命令内容'; socket_write($socket, $command . "\n"); // 读取常驻进程的回复 $response = socket_read($socket, 1024); echo $response; socket_close($socket); } else { echo "无法连接到常驻进程,请检查进程是否运行"; }
⚠️ 注意:要确保web服务器的用户(比如www-data)有读写/tmp/php_always_running.sock的权限。
2. 消息队列中间层(更灵活的方案)
如果觉得套接字配置麻烦,可以用Redis这样的消息队列做中间层,常驻脚本订阅消息频道,web端发送消息,常驻脚本收到后用长连接执行操作。
常驻脚本(alwaysrunning.php)
$serverip = '目标服务器IP'; $serverport = 目标端口; $conn = fsockopen($serverip, $serverport, $errno, $errstr, 30); if (!$conn) { die("初始连接失败: $errstr ($errno)\n"); } // 连接Redis(确保服务器已安装Redis扩展) $redis = new Redis(); $redis->connect('127.0.0.1', 6379); // 订阅命令频道,收到消息后执行发送逻辑 $redis->subscribe(['php_command_channel'], function ($redis, $channel, $message) use ($conn) { if (is_resource($conn)) { fwrite($conn, trim($message) . "\n"); // 可选:发送执行确认到回复频道 $redis->publish('php_response_channel', "命令已发送成功"); } });
web端调用(index.php)
$redis = new Redis(); $redis->connect('127.0.0.1', 6379); $command = '你要发送的命令内容'; $redis->publish('php_command_channel', $command); // 可选:订阅回复频道获取执行结果 $redis->subscribe(['php_response_channel'], function ($redis, $channel, $message) { echo $message; // 只获取一次回复就退出订阅 $redis->unsubscribe(); });
3. 进程守护(必加的稳定性保障)
不管用哪种方案,常驻进程都可能因为意外(比如内存溢出、连接断开)退出,所以建议用supervisord或者systemd做进程守护,自动重启进程。
以supervisord为例,添加一个配置文件:
[program:php_always_running] command=php /path/to/alwaysrunning.php user=www-data # 和web服务器用户一致,避免权限问题 autostart=true # 服务器启动时自动启动进程 autorestart=true # 进程意外退出时自动重启 stdout_logfile=/var/log/php_always_running.log # 日志文件路径 stderr_logfile=/var/log/php_always_running.err.log
额外注意事项
- 内存泄漏:PHP CLI模式不会自动释放内存,循环里如果有大量变量生成,要手动清理(比如
unset()) - 连接维护:定期检查
$conn是否有效,避免用无效连接发送数据 - 权限控制:确保常驻进程和web服务器的用户权限一致,避免无法访问共享资源
内容的提问来源于stack exchange,提问作者Nurio Fernandez
相关产品推荐
相关产品推荐

