本文目录导读:

- 场景一:CLI模式下的脚本(如消费队列)
- 场景二:FPM模式下(不推荐,但可实现)
- 场景三:使用异步处理框架(如Swoole/Workerman)
- 场景四:使用进程锁或信号量
- 场景五:使用队列系统(最佳实践)
- 关键建议
- 完整示例(CLI模式)
在PHP项目中,处理“存量请求等待处理完毕再退出”通常有几种常见场景和解决方案,下面我会分场景说明:
CLI模式下的脚本(如消费队列)
<?php
// 优雅退出示例
declare(ticks = 1);
// 定义退出信号处理
pcntl_signal(SIGTERM, function ($signal) {
echo "收到退出信号,等待当前请求处理完毕...\n";
// 设置标志位
global $shouldExit;
$shouldExit = true;
});
$shouldExit = false;
while (true) {
// 检查是否需要退出
if ($shouldExit) {
echo "正在停止接收新任务...\n";
break;
}
// 处理业务逻辑
$request = getNextRequest(); // 获取下一个请求
if ($request) {
processRequest($request); // 处理请求
} else {
sleep(1); // 没有请求时等待
}
}
// 清理资源
cleanupResources();
echo "程序已安全退出\n";
FPM模式下(不推荐,但可实现)
<?php
// 使用register_shutdown_function
register_shutdown_function(function () {
// 确保所有输出已经发送
if (ob_get_level() > 0) {
ob_flush();
flush();
}
// 等待某些异步操作完成
while (hasPendingOperations()) {
usleep(100000); // 100ms
processPendingOperations();
}
});
// 正常业务逻辑
$response = handleRequest();
echo $response;
// 强制等待(不推荐)
sleep(5); // 等待5秒,确保所有请求处理完毕
使用异步处理框架(如Swoole/Workerman)
<?php
// 使用Swoole的优雅退出
$server = new Swoole\Http\Server("0.0.0.0", 9501);
$server->on('request', function ($request, $response) {
// 处理请求
$response->end("Hello World");
});
// 监听进程退出信号
$server->on('shutdown', function ($server) {
echo "正在等待所有请求处理完毕...\n";
// 等待事件循环中的任务完成
while ($server->stats()['task_ing_num'] > 0) {
sleep(1);
}
echo "所有请求已处理完毕\n";
});
$server->start();
使用进程锁或信号量
<?php
// 使用文件锁实现
$lockFile = '/tmp/my_process.lock';
$lockHandle = fopen($lockFile, 'w+');
if (flock($lockHandle, LOCK_EX | LOCK_NB)) {
// 获取到锁,处理请求
// 注册退出函数
register_shutdown_function(function () use ($lockHandle) {
// 释放锁
flock($lockHandle, LOCK_UN);
fclose($lockHandle);
});
// 处理请求
sleep(10); // 模拟长时间处理
echo "请求处理完成\n";
} else {
echo "另一个进程正在处理请求\n";
exit(1);
}
使用队列系统(最佳实践)
<?php
// Redis队列消费示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 退出标志
$shouldExit = false;
// 注册信号处理
pcntl_signal(SIGTERM, function () use (&$shouldExit) {
$shouldExit = true;
});
while (true) {
// 检查退出标志
if ($shouldExit && $redis->llen('task_queue') == 0) {
echo "队列已空,安全退出\n";
break;
}
// 从队列获取任务
$task = $redis->brpop('task_queue', 1);
if ($task) {
processTask($task[1]); // 处理任务
}
}
$redis->close();
关键建议
- 使用信号处理:在CLI脚本中使用
pcntl_signal捕获退出信号 - 设置标志位:收到退出信号后设置标志,循环检查
- 等待队列清空:在处理完所有存量请求后才真正退出
- 超时机制:设置最大等待时间,避免无限等待
- 日志记录:记录开始等待和实际退出的时间
完整示例(CLI模式)
<?php
/**
* 优雅退出的工作进程
*/
class GracefulWorker
{
private $shouldExit = false;
private $queue = [];
private $processing = [];
public function __construct()
{
// 注册信号处理
pcntl_signal(SIGTERM, [$this, 'handleSignal']);
pcntl_signal(SIGINT, [$this, 'handleSignal']);
// 注册退出函数
register_shutdown_function([$this, 'onShutdown']);
}
public function handleSignal($signal)
{
echo "\n收到信号 $signal,准备优雅退出...\n";
$this->shouldExit = true;
}
public function onShutdown()
{
// 清理资源
$this->cleanup();
echo "程序已安全退出\n";
}
public function run()
{
while (true) {
// 分发信号
pcntl_signal_dispatch();
// 检查是否需要退出
if ($this->shouldExit && empty($this->processing)) {
echo "所有请求已处理完毕,退出...\n";
break;
}
// 获取新请求
if (!$this->shouldExit) {
$request = $this->getNextRequest();
if ($request) {
$this->processRequest($request);
}
}
// 处理已完成的请求
$this->handleCompletedRequests();
// 短暂休眠
usleep(100000); // 100ms
}
}
private function getNextRequest()
{
// 模拟获取请求
static $count = 0;
if ($count < 10) {
$count++;
return [
'id' => $count,
'data' => "Request #$count"
];
}
return null;
}
private function processRequest($request)
{
echo "处理请求: {$request['id']}\n";
$this->processing[$request['id']] = $request;
// 模拟处理时间
sleep(rand(1, 3));
unset($this->processing[$request['id']]);
echo "请求 {$request['id']} 处理完成\n";
}
private function handleCompletedRequests()
{
// 模拟检查已完成的请求
}
private function cleanup()
{
echo "清理资源...\n";
}
}
// 运行工作进程
$worker = new GracefulWorker();
$worker->run();
这个示例实现了:
- 信号捕获(SIGTERM/SIGINT)
- 优雅退出(等待所有请求处理完毕)
- 资源清理
- 状态检查
根据你的具体使用场景(CLI脚本、Web请求、长连接服务等),选择最适合的实现方式。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播