本文目录导读:

- 命令行脚本(CLI)模式
- 使用 Supervisor 管理进程
- 使用 ReactPHP 异步框架
- 使用 Swoole 高性能框架
- 使用 Amp 异步并发框架
- 使用 Workerman
- 使用消息队列
- 进程守护脚本
- 最佳实践建议
- 注意事项
在 PHP 中实现长期运行(Long-running)的进程或流程,主要有以下几种方案,每种都有不同的适用场景:
命令行脚本(CLI)模式
最基本的方式,使用 PHP CLI 运行脚本。
<?php
// long_running.php
set_time_limit(0); // 取消时间限制
ini_set('memory_limit', '512M');
while (true) {
// 处理任务
echo "Processing... " . date('Y-m-d H:i:s') . "\n";
sleep(5); // 等待5秒
// 检查退出条件
if (shouldExit()) {
break;
}
}
// 运行: php long_running.php
使用 Supervisor 管理进程
推荐的生产环境方案,使用 Supervisor 来监控和管理 PHP 进程。
# /etc/supervisor/conf.d/php-worker.conf [program:php-worker] command=php /path/to/worker.php autostart=true autorestart=true startretries=3 stderr_logfile=/var/log/php-worker.err.log stdout_logfile=/var/log/php-worker.out.log
使用 ReactPHP 异步框架
<?php
// 使用 Composer 安装: composer require react/event-loop
require 'vendor/autoload.php';
$loop = React\EventLoop\Factory::create();
// 定时任务
$loop->addPeriodicTimer(2, function () {
echo "每秒执行的任务\n";
});
// 处理流
$stream = fopen('php://stdin', 'r');
$loop->addReadStream($stream, function ($stream) use ($loop) {
$data = fread($stream, 1024);
if ($data) {
echo "收到输入: " . trim($data) . "\n";
}
});
$loop->run();
使用 Swoole 高性能框架
<?php
// 安装: pecl install swoole
// HTTP 服务器
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->on("start", function ($server) {
echo "Swoole HTTP server is started at http://127.0.0.1:9501\n";
});
$http->on("request", function ($request, $response) {
$response->header("Content-Type", "text/plain");
$response->end("Hello Swoole\n");
});
// 定时器
$http->tick(1000, function () {
echo "每1秒执行\n";
});
$http->start();
// 运行: php server.php
使用 Amp 异步并发框架
<?php
// composer require amphp/amp
require 'vendor/autoload.php';
use Amp\Loop;
function processQueue() {
return new Amp\Coroutine(function () {
while (true) {
yield new Amp\Delayed(1000); // 延迟1秒
echo "处理队列任务...\n";
}
});
}
Loop::run(function () {
yield processQueue();
});
使用 Workerman
<?php
// composer require workerman/workerman
require 'vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:2345');
$worker->onConnect = function ($connection) {
echo "新连接\n";
};
$worker->onMessage = function ($connection, $data) {
$connection->send('Hello ' . $data);
};
// 定时器
Worker::getEventLoop()->add(5, function () {
echo "每5秒执行\n";
});
Worker::runAll();
使用消息队列
结合 Redis、RabbitMQ 等消息队列组件。
<?php
// 使用 Redis 队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
while (true) {
// 阻塞获取队列任务
$task = $redis->brpop('task_queue', 5);
if ($task) {
echo "处理任务: " . $task[1] . "\n";
// 执行任务处理逻辑
processTask($task[1]);
} else {
// 队列为空,等待
usleep(100000);
}
}
进程守护脚本
一个完整的守护进程示例:
<?php
class DaemonProcess {
private $pid;
private $isRunning = false;
public function start() {
if ($this->isRunning) {
throw new Exception("进程已经在运行");
}
// 防止僵尸进程
pcntl_signal(SIGCHLD, SIG_IGN);
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
pcntl_signal(SIGHUP, [$this, 'handleSignal']);
$this->isRunning = true;
$this->run();
}
private function run() {
while ($this->isRunning) {
// 在这里执行你的业务逻辑
$this->processTasks();
// 处理信号
pcntl_signal_dispatch();
sleep(5);
}
}
private function processTasks() {
echo "处理任务: " . date('Y-m-d H:i:s') . "\n";
// 具体任务逻辑
}
public function handleSignal($signo) {
switch ($signo) {
case SIGTERM:
echo "收到终止信号\n";
$this->isRunning = false;
break;
case SIGHUP:
echo "收到挂起信号,重新加载配置\n";
// 重新加载配置
break;
}
}
public function stop() {
$this->isRunning = false;
}
}
// 使用
$daemon = new DaemonProcess();
$daemon->start();
最佳实践建议
- 使用 Supervisor 管理所有长期运行的 PHP 进程
- 做好错误处理 和日志记录
- 使用
register_shutdown_function处理意外退出 - 定期重启 避免内存泄漏
- 设置合理的内存限制
- 使用消息队列 解耦任务
注意事项
- 避免在循环中创建大量对象导致内存泄漏
- 使用
gc_collect_cycles()手动触发垃圾回收 - 定期检查进程状态
- 对于 CPU 密集型任务,考虑使用 PHP 扩展(如 Swoole)
根据你的具体需求(是否处理 HTTP 请求、是否需要并发、性能要求等),选择合适的方案,如果只是简单的后台服务,CLI + Supervisor 就足够了。