php 实现长轮询
PHP 实现长轮询的方法
长轮询(Long Polling)是一种服务器推送技术,客户端发送请求后,服务器保持连接开放直到有新数据或超时。以下是实现步骤:
基础实现代码
创建一个简单的长轮询 PHP 脚本(long_polling.php):
<?php
// 设置脚本执行时间无限制
set_time_limit(0);
// 模拟数据检查(实际应用可能查询数据库或外部API)
function checkForUpdates($lastUpdateTime) {
// 这里用随机数模拟数据更新
if (rand(0, 10) > 7) {
return ['status' => 'updated', 'data' => 'New data at '.date('H:i:s')];
}
return null;
}
$lastUpdate = isset($_GET['lastUpdate']) ? $_GET['lastUpdate'] : 0;
$timeout = 30; // 超时时间(秒)
$startTime = time();
while (true) {
$response = checkForUpdates($lastUpdate);
if ($response) {
echo json_encode($response);
break;
}
// 检查是否超时
if (time() - $startTime > $timeout) {
echo json_encode(['status' => 'timeout']);
break;
}
// 减少CPU占用
usleep(500000); // 0.5秒
flush();
}
?>
客户端 JavaScript 调用
前端通过 AJAX 递归调用长轮询接口:
function longPoll() {
fetch('long_polling.php?lastUpdate=' + lastUpdate)
.then(response => response.json())
.then(data => {
if (data.status === 'updated') {
console.log('Received:', data.data);
lastUpdate = Date.now();
// 处理新数据...
}
// 无论是否超时都立即发起新请求
longPoll();
})
.catch(error => {
console.error('Polling error:', error);
setTimeout(longPoll, 5000); // 错误时延迟重试
});
}
let lastUpdate = 0;
longPoll(); // 开始长轮询
优化建议
-
数据库驱动实现
实际应用通常基于数据库变更检测:// 使用MySQL的UNIX_TIMESTAMP()比较 $query = "SELECT * FROM messages WHERE timestamp > FROM_UNIXTIME($lastUpdate)"; // 或使用专门的轮询表 -
连接管理
添加连接心跳检测防止中间设备断开:
// 客户端定时发送心跳 setInterval(() => { fetch('ping.php'); }, 25000); -
性能优化
- 使用共享内存(APCu)替代数据库查询
- 设置适当的
max_execution_time - 考虑用
session_write_close()释放会话锁
替代方案比较
-
WebSocket
更适合高频双向通信,但需要额外服务端支持(如Ratchet) -
Server-Sent Events (SSE)
标准化的服务器推送技术,但部分旧浏览器不支持
-
定时短轮询
简单但效率低,适用于更新不频繁的场景
注意事项
-
Nginx/Apache 可能有默认超时限制(通常60秒),需调整配置:
location /poll { proxy_read_timeout 300s; } -
避免在长轮询期间进行会话操作,可能阻塞其他请求
-
生产环境建议添加客户端重连机制和错误处理
此实现适用于消息通知、实时状态更新等场景,可根据具体需求调整超时时间和数据检查逻辑。






