本文目录导读:

- 方案一:轮询(Polling)—— 简单但低效
- 方案二:长轮询(Long Polling)—— 进阶但仍有缺陷
- 方案三:WebSocket —— 现代、高效的首选方案
- 方案四:服务器发送事件(SSE,Server-Sent Events)—— 简单且单向的实时方案
- 总结与选型建议
在PHP项目中实现实时通知,核心思路是让服务器能主动向浏览器推送数据,传统的HTTP请求是浏览器主动请求,服务器被动响应,无法做到实时推送。
要实现这个目标,通常有以下几种主流方案,每种方案的适用场景和复杂度不同。
轮询(Polling)—— 简单但低效
这是最原始的方法,浏览器通过 setInterval 或 setTimeout 每隔几秒向服务器发一次请求,询问是否有新通知。
- 实现方式:PHP接口返回是否有新通知(
{“new”: true, “count”: 5})。 - 优点:实现最简单,兼容所有浏览器,无需额外服务。
- 缺点:
- 高延迟:不是真正的实时,延迟取决于轮询间隔。
- 浪费资源:大量无效请求占用服务器带宽和PHP进程(哪怕没有新数据也要查询数据库)。
- 适用场景:对实时性要求不高(如几分钟内的延迟)、用户量小、开发周期极短的项目。
长轮询(Long Polling)—— 进阶但仍有缺陷
这是对轮询的改进,浏览器发起请求后,PHP服务端不立即返回,而是“挂起”连接(保持连接打开),直到有新通知产生或超时,才返回数据,客户端收到响应后,立即再次建立连接。
- 实现方式:
- PHP代码中,使用
sleep或Swoole的协程来挂起。 - 数据库或Redis中有一个“通知队列”或“版本号”供查询。
- PHP代码中,使用
- 流程:
- 浏览器请求
server.php - PHP进入循环,查询数据库/Redis。
- 无新数据,
sleep(1秒),继续循环。 - 有新数据,立即返回JSON。
- 浏览器收到回应,立刻发起下一次请求。
- 浏览器请求
- 优点:相比轮询,无效请求大幅减少,延迟降低。
- 缺点:
- 资源占用高:每个PHP-FPM进程会长时间占用一个连接(传统的PHP-FPM是同步阻塞的),并发能力很差(几千个用户可能就需要几千个进程/线程)。
- 复杂度增加:需要处理超时、重连、状态管理等逻辑。
- 适用场景:小规模应用(< 500并发用户),对实时性有一定要求。
WebSocket —— 现代、高效的首选方案
WebSocket是一种在单个TCP连接上进行全双工通信的协议,浏览器和服务器一旦握手成功,双方可以随时互发消息。
-
核心问题:传统的PHP-FPM无法实现WebSocket服务器,因为PHP-FPM是“短生命周期”的,处理完一个请求就销毁了,而WebSocket需要持久的、长连接的服务端进程。
-
解决方案:使用Swoole或Workerman,这两个是PHP扩展/框架,让PHP拥有了常驻内存、异步IO、多进程/协程的能力,可以轻松实现WebSocket服务器。
-
实现方式(以Workerman为例):
- 写一个PHP脚本作为WebSocket服务端(
server.php)。 - 在服务端维护一个用户ID到连接ID的映射表(通常用Redis)。
- 当后台系统(某个PHP后台操作)触发新通知时,调用
server.php提供的API,将消息推送到对应的WebSocket连接上。 - 前端JS使用原生
WebSocket对象连接。
- 写一个PHP脚本作为WebSocket服务端(
-
优点:
- 真正的实时(毫秒级延迟)。
- 极高的效率:单个PHP进程(如Workerman)可以轻松维持数万甚至数十万的并发连接(基于事件驱动),相比长轮询节省大量资源。
- 双向通信:服务器可以主动推送,客户端也可以主动发送消息。
-
缺点:
- 需要安装扩展:需要
php-cli模式运行,不能直接运行在Apache/Nginx的mod_php下。 - 开发复杂度较高:需要理解常驻内存、进程管理、并发编程模型(尤其是Swoole的协程)。
- 调试不便:错误日志和调试不如传统Web请求直观。
- 需要安装扩展:需要
-
适用场景:几乎所有需要实时功能的现代Web应用,如聊天室、协同编辑、在线游戏、股票行情、系统报警。
服务器发送事件(SSE,Server-Sent Events)—— 简单且单向的实时方案
SSE是一种HTTP协议规范,允许服务器向浏览器单向推送事件,它基于HTTP长连接,浏览器只需建立一个普通连接,服务器就能持续发送数据流。
- 原理:PHP输出
Content-Type: text/event-stream,并持续输出特定格式的文本(data:...),浏览器用EventSourceAPI接收。 - 优点:
- 非常简单:纯PHP + Nginx即可,无需额外服务端框架或扩展。
- 自动重连:浏览器内置了重连机制。
- 基于HTTP:没有跨域问题(相比WebSocket)。
- 效率高于长轮询:只建立一个连接,持续接收,不会频繁断开重连。
- 缺点:
- 单向:服务器 -> 浏览器,浏览器不能通过这个连接向服务器发消息(如果需要,可以同时用普通AJAX)。
- 连接数限制:HTTP/1.1下,一个浏览器同域名最多只支持6-8个SSE连接。
- PHP-FPM的瓶颈:同长轮询,传统的PHP-FPM进程会长时间被一个SSE连接占用,并发能力很差。
- 解决方案:同样可以使用Swoole/Workerman来持久化处理SSE连接,或者使用Nginx的
ngx_http_push_module(较少用)。
总结与选型建议
| 方案 | 实时性 | 资源消耗(服务器压力) | 实现复杂度 | 并发能力 | 浏览器兼容性 | 推荐场景 |
|---|---|---|---|---|---|---|
| 轮询 | 差(秒级延迟) | 极高 | 极低 | 极低 | 最好 | 小项目、非实时需求、快速原型 |
| 长轮询 | 较好(秒级) | 较高 | 中 | 低 | 最好 | 小规模、对实时性有要求但不想引入新技术栈 |
| SSE(原生PHP) | 好(毫秒级) | 高(PHP-FPM占用) | 低 | 低 | 好(IE不支持) | 极简单单向通知(如股票行情无前端交互) |
| WebSocket(Swoole/Workerman) | 最好(毫秒级) | 极低 | 高 | 极高 | 好(IE10+,需Polyfill) | 大多数专业项目首选,实时聊天/通知/推送 |
| SSE(Swoole/Workerman) | 最好(毫秒级) | 低 | 中 | 高 | 好(IE不支持) | 单向推送,但需要高并发的场景 |
给出的结论性建议:
- 如果你是初学者或在做小型内部工具:可以使用轮询或长轮询快速完成任务。
- 如果你在开发一个正式的、面向用户的Web应用(尤其是移动端):
- 首选 WebSocket + Swoole/Workerman,这是最主流、最专业的PHP实时方案。
- 如果推送是纯单向的(不需要浏览器向服务器实时发指令),SSE + Swoole/Workerman 是更好的选择(因为实现更简单,没有WebSocket的协议升级开销和防火墙穿透问题)。
- 注意:尽量避免在传统Apache/Nginx +
mod_php下使用SSE或长轮询,因为很快会把服务器进程占满(每个连接消耗一个进程),导致网站崩溃。
最佳实践路径(以WebSocket为例):
- 技术选型:选择 Workerman(推荐新手,文档友好)或 Swoole(功能强大,适合高并发)。
- 架构设计:前端JS -> Nginx(转发WebSocket握手) -> Workerman/Swoole WebSocket Server <-> Redis(存储用户/通知队列)。
- 触发通知:你的普通PHP后台代码(创建订单、发表评论等) -> 通过Redis发布/订阅(Pub/Sub)或向共享内存写入 -> Workerman/Swoole 读取并推送到对应WebSocket客户端。
- 前端实现:用JS监听
onmessage事件,收到JSON后更新UI。
PHP社区推荐使用 GatewayWorker(基于Workerman的WebSocket框架),它封装了客户端管理、心跳、分布式部署等复杂逻辑,让开发一个实时通知系统变得非常简单和标准。