本文目录导读:

PHP项目实现公告推送功能,通常有以下几种主流方案,具体选择取决于你的实时性要求、用户规模以及技术栈:
核心需求分析
在开始编码前,先明确你的场景:
- 被动获取:用户打开页面或刷新时看到公告(简单,适合非紧急通知)。
- 主动推送:用户无需刷新,服务器主动将公告推送到浏览器(实时性强,适合紧急通知、系统维护)。
方案详解
方案1:轮询(Polling)—— 最简单,适合小项目(<1000用户)
原理:前端定时(如每5秒)向服务器发起AJAX请求,查询是否有新公告。
优点:实现简单,兼容所有浏览器。 缺点:浪费带宽,有一定延迟;高并发时服务器压力大。
实现步骤:
- 数据库表
announcements:id(INT, PK, AUTO_INCREMENT)title(VARCHAR)content(TEXT)created_at(DATETIME, 索引)status(TINYINT, 1=发布)
- PHP API(
get_latest_announcement.php):<?php header('Content-Type: application/json'); $lastId = $_GET['last_id'] ?? 0; $conn = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'); $stmt = $conn->prepare("SELECT * FROM announcements WHERE id > ? AND status=1 ORDER BY id DESC LIMIT 1"); $stmt->execute([$lastId]); $announcement = $stmt->fetch(PDO::FETCH_ASSOC); echo json_encode($announcement ?: ['new' => false]); - 前端JavaScript:
let lastId = 0; setInterval(() => { fetch(`get_latest_announcement.php?last_id=${lastId}`) .then(res => res.json()) .then(data => { if (data.new !== false) { showAnnouncement(data); lastId = data.id; } }); }, 5000); // 5秒一次
方案2:长轮询(Long Polling)—— 折中方案
原理:前端发起请求后,PHP服务器保持连接,直到有新公告才返回响应,之后前端立即发起新请求。
优点:比普通轮询实时性更好,消息到达即推送。 缺点:服务器需要保持大量连接(消耗内存/进程),PHP本身不适合长时间挂起。
实现(简化版,需搭配 sleep+数据库检查):
<?php
set_time_limit(30); // 允许脚本运行30秒
$lastId = $_GET['last_id'] ?? 0;
$timeout = 25; // 最长等待25秒
$start = time();
while (time() - $start < $timeout) {
// 查询数据库
$stmt = $pdo->prepare("SELECT * FROM announcements WHERE id > ?");
$stmt->execute([$lastId]);
$data = $stmt->fetch();
if ($data) {
echo json_encode($data);
exit;
}
sleep(2); // 每2秒检查一次,避免疯狂查库
}
echo json_encode(['timeout' => true]); // 超时,客户端重连
注意:PHP长轮询在高并发下性能较差,建议用Swoole/Workerman或Node.js替代。
方案3:WebSocket(推荐用于高实时性需求)
原理:建立持久TCP连接,服务器可以主动推送数据,这是真正的“推送”。
优点:真正的实时推送,延迟极低(毫秒级),节省带宽。 缺点:需要额外的WebSocket服务器(通常不是Apache/Nginx),实现稍复杂。
推荐工具:
- Swoole(PHP扩展) —— 直接在PHP生态内实现WebSocket服务器。
- Workerman —— 纯PHP的socket框架,简单易用。
- Node.js + Socket.IO —— 如果团队熟悉Node,这是最成熟的选择。
以Workerman为例:
-
安装:
composer require workerman/workerman -
服务器端(
server.php):<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; use Workerman\Lib\Timer; $ws_worker = new Worker('websocket://0.0.0.0:2346'); $ws_worker->count = 4; // 进程数 // 存储所有连接的客户端 $ws_worker->onConnect = function($connection) { // 可以选择将连接加入群组,或标记用户 }; $ws_worker->onMessage = function($connection, $data) { // 通常客户端只认证,不发送消息 }; // 定时器:每10秒检查数据库是否有新公告 $ws_worker->onWorkerStart = function($worker) { Timer::add(10, function() use ($worker) { // 查询数据库最新公告(需带上次推送的ID) // 如果有新公告,遍历$worker->connections并推送 foreach ($worker->connections as $conn) { $conn->send(json_encode(['title' => '新公告', 'content' => '...'])); } }); }; Worker::runAll(); -
客户端JavaScript:
let ws = new WebSocket('ws://yourdomain.com:2346'); ws.onmessage = function(event) { let data = JSON.parse(event.data); showAnnouncement(data); };
方案4:Server-Sent Events (SSE) —— 服务器单向推送
原理:HTML5标准,客户端通过EventSource对象订阅服务器事件流,服务器返回text/event-stream格式的数据。
优点:比WebSocket简单(不需要额外协议),原生支持,自动重连;非常适合“公告”这种服务器单向推送场景。 缺点:不支持IE(支持Edge),只能是文本消息,不适合双向通信。
实现:
-
PHP端点(
sse.php):<?php header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('Connection: keep-alive'); $lastId = $_GET['last_id'] ?? 0; while (true) { $data = checkDatabaseForNewAnnouncement($lastId); // 你的查询逻辑 if ($data) { echo "id: {$data['id']}\n"; echo "event: announcement\n"; echo "data: " . json_encode($data) . "\n\n"; ob_flush(); flush(); } sleep(5); // 每5秒检查一次 if (connection_aborted()) break; } -
前端:
var source = new EventSource('sse.php'); source.addEventListener('announcement', function(e) { var data = JSON.parse(e.data); showAnnouncement(data); });
技术选型建议
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 企业内部系统、管理后台 | 轮询 或 SSE | 实现简单,用户数少,实时性要求不高 |
| 社区、论坛、新闻门户 | SSE 或 WebSocket (Workerman) | 中等实时性,支持自动重连,兼容性好 |
| 即时聊天、直播互动、游戏 | WebSocket (Swoole/Node) | 毫秒级延迟,双向通信,高并发 |
| 系统维护通知、紧急公告 | WebSocket + 消息队列 | 低延迟,可配合Redis发布订阅实现跨进程推送 |
| 不希望引入额外服务器/进程 | 轮询 | 最兼容,无需额外维护,适合传统LAMP架构 |
升级与优化
- 消息队列(Redis Pub/Sub, RabbitMQ):当公告发布时,后台将消息推送到队列,WebSocket服务器从队列中消费并推送给客户端,解耦发布和推送,避免每次轮询数据库。
- 用户分组推送:例如只向在线用户或特定用户组(VIP)推送公告,可以在WebSocket连接握手时,解析用户身份并分组。
- 断线重连:WebSocket客户端(使用socket.io或原生)应实现自动重连逻辑,并带上
last_message_id,重连后拉取离线期间错过的公告。 - 安全与鉴权:对于SSE/WebSocket,应在连接建立时校验用户token(例如通过URL参数或WebSocket握手头),防止任意客户端接入。
最终推荐(懒人版)
- 快速集成:如果项目已使用Laravel,推荐 Laravel WebSockets 或 Laravel Reverb(Laravel 11官方推荐),结合其广播系统。
- 小项目自建:使用 SSE,PHP原生支持,代码量最少,适合公告推送。
- 大型项目:单独起一个 Node.js 或 Go 服务做WebSocket推送,PHP只负责发布消息到Redis/消息队列。