PHP 怎么PHP内存常驻

wen PHP项目 2

本文目录导读:

PHP 怎么PHP内存常驻

  1. 使用 while(true) 循环(原生 CLI 方式)
  2. 使用 pcntl_fork() 多进程(CLI)
  3. 使用 Swoole / Workerman(生产环境首选)
  4. 关键注意事项(避坑指南)
  5. 总结建议

在 PHP 中实现“内存常驻”(即进程持续运行,不释放内存,常驻内存)通常有几种方法,最核心的思想是让 PHP 进程不结束,从而让变量、连接和缓存数据一直保留在内存中。

根据你的需求(是写脚本、做队列消费,还是开发高性能服务),主要有以下三种主流方案:

使用 while(true) 循环(原生 CLI 方式)

这是最基础、最直接的方法,PHP 执行完代码后,如果进程不退出,内存自然就常驻了。

适用场景: 简单的定时任务、单进程队列消费、简单的长连接脚本。

核心代码示例(一个简单的消费者):

<?php
// 在 CLI 模式下运行: php consumer.php
// 关闭 PHP 默认的执行时间限制(否则脚本会自动退出)
set_time_limit(0);
// 关闭输出缓冲,让日志实时输出
ob_implicit_flush(true);
// 模拟一个全局缓存或数据库连接
$cache = [];
echo "Worker 已启动,内存常驻中...\n";
while (true) {
    // 模拟从队列(如 Redis/DB)拉取任务
    $task = getTaskFromQueue(); // 假设这个函数会阻塞或者等待
    if ($task !== null) {
        echo "处理任务: " . $task . "\n";
        // 将结果存入内存,实现“记忆”
        $cache[$task] = time();
    }
    // **重要:防止 CPU 空转 100%**
    usleep(100000); // 休眠 0.1 秒再继续
}
// 下面的代码永远不会执行到

关键点:

  • while 循环内,变量 $cache 一直存在且可读写。
  • 必须要在循环里加 usleep()sleep(),否则 CPU 会飙升。

缺点: 代码结构简单,但如果循环内发生未捕获的异常,进程会直接退出,需要很严谨的异常处理来保活。


使用 pcntl_fork() 多进程(CLI)

如果你需要同时处理多个任务,且希望主进程常驻挂载子进程,可以用进程控制函数。

适用场景: 复杂的并发队列消费(如多开的 Worker)。

核心思路:

<?php
// 主进程(Supervisor)
set_time_limit(0);
// 创建 3 个子进程
for ($i = 0; $i < 3; $i++) {
    $pid = pcntl_fork();
    if ($pid == -1) {
        die('无法创建子进程');
    } elseif ($pid) {
        // 这是父进程
        echo "已启动子进程 PID: $pid\n";
    } else {
        // 这是子进程
        while (true) {
            echo "子进程 " . getmypid() . " 处理中...\n";
            sleep(1);
            // 这里可以放具体的业务逻辑
        }
        exit(); // 子进程退出,不会影响父进程
    }
}
// 父进程进入监控状态,等待信号回收子进程
while (true) {
    $status = 0;
    $pid = pcntl_waitpid(-1, $status, WNOHANG); // 非阻塞等待子进程退出
    if ($pid > 0) {
        echo "子进程 $pid 退出了,主进程继续运行。\n";
        // 这里可以重新 fork 新进程补充
    }
    sleep(1);
}

使用 Swoole / Workerman(生产环境首选)

如果你是需要高性能 HTTP 服务、WebSocket 或复杂的 TCP 服务,强烈推荐使用 Swoole 扩展或 Workerman 框架,它们本质上是 C 语言写的事件循环 + PHP 业务逻辑,内存管理极其高效,且提供了完善的进程管理机制。

适用场景: 高并发 API 服务、长连接推送、微服务。

Swoole 核心示例(内存常驻 + 热更新):

<?php
// 文件: server.php
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
// 创建 HTTP 服务,监听 9501 端口
$server = new Server("0.0.0.0", 9501);
// 设置参数
$server->set([
    'worker_num' => 4, // 开启 4 个 Worker 进程
    'daemonize' => false, // 前台运行 (方便调试)
    'max_request' => 10000, // 每个 Worker 处理 1 万请求后重启,防止内存溢出
]);
// 这段代码在 Worker 进程启动时只执行一次,内存常驻
$server->on('WorkerStart', function ($server, $workerId) {
    // 在这里初始化全局数据库连接或共享数据(仅限该 Worker 内)
    global $db;
    $db = new PDO('mysql:host=...', 'user', 'pass');
    echo "Worker $workerId 启动,内存常驻!\n";
});
// 回调函数
$server->on('request', function (Request $request, Response $response) {
    // 这里面的局部变量 $request 每次请求都会重新创建
    // WorkerStart 中的 $db 连接是常驻的
    $response->header("Content-Type", "text/plain");
    $response->end("Hello World - 这是常驻进程!\n");
});
echo "服务器启动,PID: " . $server->master_pid . "\n";
$server->start();

关键注意事项(避坑指南)

  1. 内存泄漏(Memory Leak)

    • 虽然建议常驻,但 PHP 本质是脚本语言,没有不引用的对象自动 GC,如果你在循环里一直 new 对象而不释放,内存会缓慢增加。
    • 解决方法:使用 unset() 释放大变量;如果是 Swoole,设置 max_request 让 Worker 定期自动重启,是防止内存泄漏的标配操作。
  2. set_time_limit(0)

    • 在 CLI 模式下,默认 max_execution_time 为 0(无限),但如果是在某些框架中,可能需要手动设置 ini_set('max_execution_time', '0')
  3. 异常与崩溃恢复

    • 常驻进程最怕 Fatal Error(如内存耗尽),用 register_shutdown_functiontry...catch 捕获,或者用 pcntl_signal 监听退出信号做平滑退出。
  4. 代码修改后如何生效

    • 常驻进程启动后,修改 PHP 文件不会生效(因为代码已经加载进内存了)。
    • 必须重启进程,如果是 Swoole,可以用 SIGUSR1 信号实现平滑重载(Reload)。

总结建议

  • 如果只是临时测试:用 while(true) + usleep(0.1) 最方便。
  • 如果是做正式项目/服务强烈建议直接用 Swoole 或 Workerman,它们帮你处理好了进程管理、信号、内存回收等底层复杂逻辑,你只需要专注写业务代码即可。

如果你在 Windows 下开发,Swoole 不支持 Windows,可以考虑使用 Workerman(纯 PHP 实现)或者用 Docker/Linux 环境。

抱歉,评论功能暂时关闭!