本文目录导读:

- 使用 pcntl_signal(信号处理)
- 使用 register_shutdown_function
- 使用 tick 函数
- Docker/Kubernetes 中的 preStop 实现
- 使用第三方库
- 完整的 preStop Hook 示例
- 使用建议
在 PHP 中并没有像 Kubernetes 那样的原生 preStop Hook 机制,但我们可以通过多种方式实现类似的功能,以下是几种常见的实现方案:
使用 pcntl_signal(信号处理)
<?php
// 注册信号处理器
pcntl_async_signals(true); // 开启异步信号处理
// 定义清理函数
function cleanup() {
echo "执行清理操作...\n";
// 关闭数据库连接
// 保存状态
// 清理临时文件等
file_put_contents('/tmp/app.log', "应用正在停止...\n", FILE_APPEND);
}
// 注册 SIGTERM 信号处理器
pcntl_signal(SIGTERM, function($signo) {
cleanup();
exit(0);
});
// 注册 SIGINT (Ctrl+C) 信号处理器
pcntl_signal(SIGINT, function($signo) {
cleanup();
exit(0);
});
// 主业务逻辑
echo "应用启动...\n";
while (true) {
echo "运行中... " . date('Y-m-d H:i:s') . "\n";
sleep(1);
}
?>
使用 register_shutdown_function
<?php
// 注册关机函数
register_shutdown_function(function() {
echo "执行关机清理...\n";
// 清理资源
file_put_contents('/tmp/app.log', "应用关闭清理\n", FILE_APPEND);
});
// 注册信号处理
pcntl_signal(SIGTERM, function($signo) {
echo "收到停止信号\n";
exit(0);
});
pcntl_signal(SIGINT, function($signo) {
echo "收到中断信号\n";
exit(0);
});
echo "应用启动...\n";
while (true) {
sleep(1);
}
?>
使用 tick 函数
<?php
// 使用 tick 来定期检查
declare(ticks=1);
$running = true;
function checkSignal() {
global $running;
// 检查是否有停止信号
if (file_exists('/tmp/stop.signal')) {
$running = false;
echo "检测到停止信号\n";
// 执行清理
cleanup();
}
}
function cleanup() {
echo "执行清理代码...\n";
// 清理临时文件
@unlink('/tmp/app.tmp');
@unlink('/tmp/stop.signal');
}
while ($running) {
// 业务逻辑
echo "运行中...\n";
sleep(1);
checkSignal();
}
?>
Docker/Kubernetes 中的 preStop 实现
Dockerfile 方式
FROM php:8.1-fpm # 复制启动脚本 COPY start.sh /usr/local/bin/start.sh RUN chmod +x /usr/local/bin/start.sh # 复制停止脚本 COPY preStop.sh /usr/local/bin/preStop.sh RUN chmod +x /usr/local/bin/preStop.sh # 设置入口点 ENTRYPOINT ["/usr/local/bin/start.sh"]
preStop.sh
#!/bin/bash echo "执行 preStop 清理..." # 发送信号给 PHP-FPM 进程 kill -TERM $(pgrep -f "php-fpm: master") # 等待进程结束 wait $(pgrep -f "php-fpm: master") 2>/dev/null echo "清理完成..."
Kubernetes 配置方式
apiVersion: v1
kind: Pod
metadata:
name: php-app
spec:
containers:
- name: php-app
image: my-php-app
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- |
echo "executing preStop hook"
curl -X POST http://localhost:8080/api/cleanup || true
ls -la /tmp
# 等待业务完成
sleep 5
使用第三方库
Spatie/Async
<?php
use Spatie\Async\Pool;
$pool = Pool::create();
// 添加多个异步任务
foreach ($tasks as $task) {
$pool[] = async(function() use ($task) {
// 业务逻辑
return $task->execute();
})->then(function($output) {
// 成功处理
})->catch(function($exception) {
// 错误处理
});
}
// 注册关机钩子
register_shutdown_function(function() use ($pool) {
echo "停止异步任务...\n";
$pool->stop();
echo "异步任务已停止\n";
});
// 等待所有任务完成
$pool->wait();
?>
完整的 preStop Hook 示例
<?php
class PreStopHook {
private $config;
private $isStopped = false;
public function __construct($config = []) {
$this->config = $config;
$this->registerSignalHandlers();
$this->registerShutdownHandler();
}
private function registerSignalHandlers() {
// 异步信号处理(PHP 7.1+)
pcntl_async_signals(true);
// 处理 SIGTERM
pcntl_signal(SIGTERM, [$this, 'handleStop']);
// 处理 SIGINT
pcntl_signal(SIGINT, [$this, 'handleStop']);
// 处理 SIGQUIT
pcntl_signal(SIGQUIT, [$this, 'handleStop']);
}
private function registerShutdownHandler() {
register_shutdown_function([$this, 'cleanup']);
}
public function handleStop($signo) {
if ($this->isStopped) {
return;
}
$this->isStopped = true;
$signalName = $this->getSignalName($signo);
echo "收到信号: {$signalName}\n";
// 执行停止前的操作
$this->preStopTasks();
// 退出
exit(0);
}
private function preStopTasks() {
echo "执行 preStop 清理任务...\n";
// 1. 保存状态
$this->saveState();
// 2. 清理缓存
$this->clearCache();
// 3. 关闭连接
$this->closeConnections();
// 4. 删除临时文件
$this->cleanTempFiles();
echo "preStop 清理完成\n";
}
private function saveState() {
echo "保存应用状态...\n";
file_put_contents('/tmp/app-state.json', json_encode([
'time' => date('Y-m-d H:i:s'),
'memory' => memory_get_usage(),
'pid' => getmypid()
]));
}
private function clearCache() {
echo "清理缓存...\n";
// 清理 APCu 缓存
if (function_exists('apcu_clear_cache')) {
apcu_clear_cache();
}
// 清理 OpCache
if (function_exists('opcache_reset')) {
opcache_reset();
}
}
private function closeConnections() {
echo "关闭数据库连接...\n";
// 假设使用 PDO
// $db = null;
}
private function cleanTempFiles() {
echo "清理临时文件...\n";
array_map('unlink', glob('/tmp/app-*.tmp'));
}
private function getSignalName($signo) {
$signals = [
SIGTERM => 'SIGTERM',
SIGINT => 'SIGINT',
SIGQUIT => 'SIGQUIT'
];
return isset($signals[$signo]) ? $signals[$signo] : "Unknown({$signo})";
}
public function run() {
echo "应用运行中...\n";
while (!$this->isStopped) {
// 业务逻辑
echo "处理业务..." . date('H:i:s') . "\n";
sleep(1);
}
}
}
// 使用示例
$app = new PreStopHook();
$app->run();
?>
使用建议
- 选择合适的方式:根据你的部署环境选择最适合的方案
- 设置超时:在清理操作中设置超时,避免无限等待
- 记录日志:记录清理操作的日志
- 错误处理:清理操作中的错误不应该影响正常退出
- 测试:充分测试各种停止场景
这些方案都能实现类似 preStop Hook 的功能,选择哪种取决于你的具体需求和使用环境。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播