You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:14:57