引言:为什么您的网站在实时交互时“卡顿”?
\n\n想象一下,您登录一个外汇网站,但美元汇率只有在刷新页面后才会更新。或者您在聊天中发送了一条消息,对方却在 10 秒后才看到。熟悉吗?这是任何试图让应用变得“鲜活”的开发者的痛点。
\n\n在 PHP 的世界里,每个请求的生命周期都很短暂,实现实时功能是一项颇具挑战的任务。有两种主要方法可以解决这个问题:Long polling 和 WebSocket。对于您的 PHP 项目,应该选择哪一种?让我们通过实际的代码示例,直截了当地进行分析。
\n\nPHP 中的 Long polling 是什么?一个带有秘密的长请求
\n\nLong polling 是一种巧妙的技巧,它利用普通的 HTTP 请求来模拟实时连接。客户端向服务器发送一个请求,但服务器不会立即响应。它会保持连接打开,直到有新事件发生(例如,聊天中的新消息)。一旦事件发生,服务器就会发送响应,客户端处理该响应后,立即发起一个新的“长”请求。
\n\n它在 PHP 中如何工作:一个简单的例子
\n\n假设我们有一个简单的聊天应用。客户端发送一个 AJAX 请求到 PHP 脚本,该脚本在一个循环中“挂起”,直到数据库中出现新消息。
\n\n<?php\n// long_polling.php\nheader('Content-Type: application/json');\nheader('Cache-Control: no-cache');\n\n$lastMessageId = $_GET['last_id'] ?? 0;\n$timeout = 30; // 最多等待 30 秒\n$startTime = time();\n\nwhile (time() - $startTime < $timeout) {\n // 模拟数据库检查(实际应用中为 MySQL/PostgreSQL 查询)\n $newMessages = getNewMessagesFromDB($lastMessageId);\n \n if (!empty($newMessages)) {\n echo json_encode([\n 'status' => 'success',\n 'messages' => $newMessages\n ]);\n exit;\n }\n \n // 休眠 1 秒,避免 CPU 过载\n sleep(1);\n}\n\n// 如果 30 秒内没有新消息\necho json_encode(['status' => 'timeout', 'messages' => []]);\nexit;\n?>\n\n在客户端(JavaScript)中,代码如下:
\n\nfunction startLongPolling(lastId) {\n fetch('/long_polling.php?last_id=' + lastId)\n .then(response => response.json())\n .then(data => {\n if (data.status === 'success') {\n data.messages.forEach(msg => addMessageToChat(msg));\n startLongPolling(data.messages[data.messages.length - 1].id);\n } else {\n startLongPolling(lastId); // 超时——只需重新启动\n }\n })\n .catch(() => setTimeout(() => startLongPolling(lastId), 1000));\n}\n\n\nPHP 中 Long polling 的优点
\n- \n
- 实现简单 — 无需额外的服务器或库。一切都可以在普通的 Apache/Nginx + PHP-FPM 上运行。 \n
- 兼容性好 — 任何支持 HTTP 的地方都能工作。无需担心代理或企业防火墙问题。 \n
- 入门门槛低 — 如果您了解 PHP 和 AJAX,您就可以实现 Long polling。 \n
Long polling 的缺点
\n- \n
- 服务器负载高 — 每个“长”请求都会占用一个 PHP-FPM 进程。如果有 1000 个用户,您就需要 1000 个进程。 \n
- 延迟高 — 最小延迟为 1 秒(由于 sleep)。如果减少 sleep 时间,CPU 负载会急剧上升。 \n
- 扩展困难 — Long polling 与负载均衡器配合不佳(需要将用户绑定到特定服务器)。 \n
PHP 中的 WebSocket:真正的实时,无需妥协
\n\nWebSocket 是一个基于 TCP 的全双工通信通道。在通过 HTTP 完成握手后,