php 实现长轮询
PHP 实现长轮询的方法
长轮询(Long Polling)是一种服务器推送技术,客户端向服务器发送请求后,服务器保持连接开放,直到有数据可发送或超时。以下是实现长轮询的步骤和代码示例。
服务器端代码
创建一个 PHP 脚本(如 long_polling.php)处理长轮询请求。该脚本会检查数据更新,并在有更新或超时时返回响应。
<?php
// 设置脚本执行时间不受限制
set_time_limit(0);
// 模拟数据存储(实际应用中可能使用数据库或缓存)
$lastUpdate = isset($_GET['lastUpdate']) ? (int)$_GET['lastUpdate'] : 0;
// 长轮询超时时间(秒)
$timeout = 30;
$startTime = time();
// 检查数据是否更新
while (true) {
// 模拟数据更新(实际应用中根据业务逻辑判断)
$currentUpdate = filemtime('data.txt'); // 假设通过文件修改时间判断更新
if ($currentUpdate > $lastUpdate) {
// 返回新数据
$response = [
'status' => 'success',
'data' => file_get_contents('data.txt'),
'lastUpdate' => $currentUpdate
];
echo json_encode($response);
break;
}
// 检查是否超时
if (time() - $startTime >= $timeout) {
$response = ['status' => 'timeout'];
echo json_encode($response);
break;
}
// 减少 CPU 占用
usleep(100000); // 0.1 秒
}
?>
客户端代码
使用 JavaScript 发起长轮询请求,并在收到响应后处理数据或重新发起请求。
function longPolling(lastUpdate = 0) {
fetch(`long_polling.php?lastUpdate=${lastUpdate}`)
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
console.log('New data:', data.data);
// 处理新数据后继续轮询
longPolling(data.lastUpdate);
} else if (data.status === 'timeout') {
// 超时后重新发起轮询
longPolling(lastUpdate);
}
})
.catch(error => {
console.error('Polling error:', error);
// 错误后重新发起轮询
setTimeout(() => longPolling(lastUpdate), 1000);
});
}
// 启动长轮询
longPolling();
实际应用优化
- 数据存储:替换文件操作为数据库或缓存(如 Redis),通过查询判断数据更新。
- 性能优化:避免频繁查询数据库,可以使用缓存或消息队列。
- 错误处理:增加客户端重试机制和服务器端日志记录。
- 安全性:验证客户端请求,防止恶意攻击。
示例:数据库实现
假设使用 MySQL 存储数据,通过检查 last_modified 字段判断更新:
<?php
set_time_limit(0);
$lastUpdate = isset($_GET['lastUpdate']) ? (int)$_GET['lastUpdate'] : 0;
$timeout = 30;
$startTime = time();
// 数据库连接
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
while (true) {
$stmt = $db->prepare("SELECT * FROM messages WHERE last_modified > ? LIMIT 1");
$stmt->execute([$lastUpdate]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if ($data) {
echo json_encode([
'status' => 'success',
'data' => $data['content'],
'lastUpdate' => $data['last_modified']
]);
break;
}
if (time() - $startTime >= $timeout) {
echo json_encode(['status' => 'timeout']);
break;
}
usleep(100000);
}
?>
通过以上方法,可以实现高效的长轮询机制,适用于实时性要求较高的应用场景。







