PHP 长连接与轮询的选择:站内消息提醒的 3 种实现对比

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-16 06:22 ·2 浏览 ·0 回复

结论:对绝大多数 PHP 论坛(包括 Clara BBS 这类无框架轻量系统),站内消息提醒首选用「15-30 秒短轮询 + 未读数缓存」,投入产出比最高;需要更强实时感就用 SSE;长轮询只在用户量小、且你能盯住 php-fpm 进程数时才考虑,WebSocket 对这类「上传即用、无需命令行」的部署形态不划算。

三种方案先摆在一起看

短轮询:前端定时问服务器「有没有新消息」,实现最简单,代价是大量空请求。
长轮询:请求发出去后服务器「挂住」不马上回,有新消息或超时才返回,实时感接近推送,代价是每个挂起连接占一个 PHP 进程。
SSE(Server-Sent Events,服务器单向事件流):一次 HTTP 连接持续推数据给浏览器,浏览器断线自动重连,只支持服务端→客户端单向。
WebSocket:全双工,但 PHP 下必须靠 Swoole/Workerman 之类常驻进程,需要命令行、进程守护与额外端口,与「上传文件→访问 install→完成」的部署模型不兼容。

方案一:短轮询——90% 的场景够用

结论:短轮询不是「落后方案」,在论坛这种消息密度低的场景里它就是最优解。

做法很直白:前端 `setInterval` 每 20-30 秒请求一次未读接口;后端只跑一条 `SELECT COUNT(*) FROM notify WHERE uid=? AND is_read=0`,并给 `(uid, is_read)` 建联合索引。优化点有三条:一是用系统自带的 `Cache::remember` 把未读数缓存 10 秒,多人同页时能砍掉绝大部分重复查询;二是监听 `visibilitychange`,标签页切到后台就停掉定时器,前台化再立即拉一次;三是失败退避,连续失败把间隔拉长到 60 秒,避免服务器抖动时被打爆。

注意点只有一条:别把轮询接口做成「查全量消息列表」,那才是真正拖慢数据库的原因。

方案二:长轮询——实时感好,但吃 PHP-FPM 进程

结论:长轮询的并发上限不是带宽决定的,是 `pm.max_children` 决定的,一个挂起连接就占死一个 worker。

服务端要点:`set_time_limit(0)`;循环里 `sleep(1)` 查一次「自增 ID 大于上次的 ID」,查不到就继续等;总等待时间控制在 25-30 秒后返回空结果,让前端立刻发起下一次;每轮循环必须用 `connection_aborted()` 判断客户端是否已断开,否则用户关页面后进程还要空转半分钟。前端收到响应就马上再发一次请求,形成「伪长连接」。

必须同步调的三处配置:Nginx 的 `fastcgi_read_timeout` 要大于 hold 时间(比如设 60s),否则网关先掐断;数据库查询只允许走主键/索引,禁止 `SELECT *`;`pm.max_children` 要留出余量——假设设 50,最多 50 个用户能同时挂住,第 51 个请求就得排队。所以这套方案适合几百活跃用户、且在线峰值可控的站。

方案三:SSE——比 WebSocket 简单得多的单向推送

结论:只需要「服务器推、浏览器收」,SSE 是比 WebSocket 低一个数量级的成本。

实现骨架:响应头设 `Content-Type: text/event-stream`、`Cache-Control: no-cache`,并加上 `X-Accel-Buffering: no` 防止 Nginx 缓冲;循环输出 `data: {...}\n\n` 后 `flush()`;客户端用 `EventSource` 接收即可,浏览器会自动重连。Nginx 侧需要关掉 `proxy_buffering`/`gzip`,并把读超时调大。

它的坑和长轮询一样——仍然占用 worker,而且 php-fpm 默认的 `request_terminate_timeout` 会掐断长连接,需要单独开一个 fpm 池或调大该值。好处是每个用户只需要一条连接,比长轮询反复建连要省。

落到 Clara BBS 上怎么接

结论:消息的产生和消费是两件事,先用系统现成设施把「产生」做扎实,再选推送方式。

Clara BBS 的公共设施里有 `notify` 通知、`Cron::register` 定时任务(懒触发、零配置)和 `Cache::remember` 缓存,插件放在 `content/plugins` 走运行时钩子加载、保存即生效。所以合理分工是:发帖、回帖 @提及、私信、悬赏被采纳这些事件通过钩子写入 notify 通知表;未读数走缓存;清理过期通知、每日汇总这类活儿交给定时任务;前端提醒方式按前面三选一来定。这样即使以后从短轮询换成 SSE,也只动前端和推送接口,业务侧一行不用改。

一句话收尾:论坛消息密度低、PHP 进程又是稀缺资源,先用短轮询把功能跑起来,量大了再评估 SSE;长轮询和 WebSocket 都是「有明确并发预算和运维手段」之后才该碰的选项。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-393.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~