本文目录导读:

- 拉取模式(Polling)- 最简单
- 长轮询(Long Polling)- 折中方案
- WebSocket - 推荐方案
- Server-Sent Events (SSE) - 适合单向推送
- 完整企业级方案(推荐)
- 推送平台/二要素认证
- 实际项目推荐选择
- 性能优化建议
在PHP中实现消息通知系统,有多种方案,从简单到复杂,我为你整理了几种主流实现方式:
拉取模式(Polling)- 最简单
原理
前端定时向后端发送请求,检查是否有新消息。
实现示例
// client.js
setInterval(async () => {
const response = await fetch('/api/notifications/check');
const data = await response.json();
if (data.hasNew) {
// 处理新消息
showNotification(data.messages);
}
}, 3000); // 每3秒检查一次
// server.php
<?php
// /api/notifications/check
$lastId = $_GET['last_id'] ?? 0;
$notifications = getNotificationsSince($lastId);
echo json_encode(['hasNew' => !empty($notifications), 'messages' => $notifications]);
✅ 优点:实现简单,兼容性好
❌ 缺点:实时性差,请求频繁浪费资源
长轮询(Long Polling)- 折中方案
原理
客户端发出请求后,服务器保持连接直到有新消息或超时才返回。
<?php
// long-polling.php
set_time_limit(30); // 设置30秒超时
$lastId = $_GET['last_id'] ?? 0;
$startTime = time();
while (time() - $startTime < 25) { // 25秒内循环检查
$newMessages = getMessagesSince($lastId);
if (!empty($newMessages)) {
echo json_encode(['messages' => $newMessages, 'last_id' => $newMessages[count($newMessages)-1]['id']]);
exit;
}
usleep(500000); // 休息0.5秒再查
}
echo json_encode(['messages' => [], 'last_id' => $lastId]);
✅ 优点:实时性较好,比轮询节省资源
❌ 缺点:服务器资源占用较高,不适合海量连接
WebSocket - 推荐方案
原理
全双工通信,客户端和服务器都可以随时发送消息。
使用 Ratchet(PHP WebSocket库)
// composer.json
{
"require": {
"cboden/ratchet": "^0.4.4"
}
}
// WebSocket服务器
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class NotificationServer implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "新连接: {$conn->resourceId}\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
// 接收用户ID进行绑定
$data = json_decode($msg, true);
$from->userId = $data['user_id'] ?? null;
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
// 发送通知给特定用户
public function sendNotification($userId, $message) {
foreach ($this->clients as $client) {
if ($client->userId === $userId) {
$client->send(json_encode(['type' => 'notification', 'message' => $message]));
}
}
}
}
// 启动服务器
$server = \Ratchet\Server\IoServer::factory(
new \Ratchet\Http\HttpServer(
new \Ratchet\WebSocket\WsServer(
new NotificationServer()
)
),
8080
);
$server->run();
客户端代码
// client.js
const ws = new WebSocket('ws://localhost:8080');
ws.onopen = () => {
// 发送用户ID进行绑定
ws.send(JSON.stringify({ user_id: user_id }));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'notification') {
alert(data.message.message);
updateNotificationCount();
}
};
✅ 优点:实时性最好,双向通信
❌ 缺点:需要保持长连接,服务器配置相对复杂
Server-Sent Events (SSE) - 适合单向推送
原理
服务器向浏览器单向推送数据,基于HTTP。
<?php
// sse.php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// 必须设置
@ini_set('implicit_flush', 1);
@ob_end_clean();
$lastEventId = $_SERVER['HTTP_LAST_EVENT_ID'] ?? 0;
while (true) {
$messages = getMessagesSince($lastEventId);
if (!empty($messages)) {
foreach ($messages as $msg) {
echo "id: {$msg['id']}\n";
echo "data: " . json_encode($msg) . "\n\n";
}
ob_flush();
flush();
}
// 保持连接,发送心跳
echo ": heartbeat\n\n";
// flush();
sleep(1);
}
客户端
// client.js
var source = new EventSource('/sse.php');
source.onmessage = function(event) {
var data = JSON.parse(event.data);
console.log('收到通知:', data);
// 处理通知...
};
✅ 优点:实时性好,自动重连,使用简单
❌ 缺点:只能服务端向客户端单向推送
完整企业级方案(推荐)
结合数据库、Redis队列和WebSocket:
[业务触发] → [写入DB] → [发布到Redis] → [WebSocket服务器消费] → [推送客户端]
<?php
// 1. 业务端:发送通知
function sendNotification($userId, $type, $content) {
// 保存到数据库
$notification = [
'user_id' => $userId,
'type' => $type,
'content' => $content,
'created_at' => date('Y-m-d H:i:s')
];
$db->insert('notifications', $notification);
// 发布到Redis队列
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lPush('notification_queue', json_encode($notification));
// 通知WebSocket服务器有新消息
$redis->publish('notification_channel', json_encode($notification));
}
// 2. WebSocket服务器
// 在Ratchet中订阅Redis并推送到客户端
public function onOpen(ConnectionInterface $conn) {
// 连接Redis并订阅通知
$this->redis = new \Redis();
$this->redis->connect('127.0.0.1', 6379);
$this->redis->subscribe(['notification_channel'], [$this, 'handleRedisMessage']);
}
public function handleRedisMessage($redis, $channel, $message) {
$data = json_decode($message, true);
$this->sendNotification($data['user_id'], $data);
}
推送平台/二要素认证
服务商集成
- 极光推送(JPush) - 支持Web/App推送
- 个推(Getui) - 偏移动端
- 阿里云推送 - 支持Uni/Push
// 示例:使用极光推送
// composer require jpush/jpush
use JPush\Client as JPushClient;
function sendPush($userId, $content) {
$client = new \JPush\Client('your_app_key', 'your_master_secret');
$push = $client->push()
->setPlatform('all')
->addRegistrationId($userId)
->setNotificationAlert($content);
try {
return $push->send();
} catch (\Exception $e) {
error_log('推送失败: ' . $e->getMessage());
}
}
实际项目推荐选择
| 场景 | 推荐方案 |
|---|---|
| 简单后台管理 | 轮询,5秒一次 |
| 中小型应用 | 长轮询 或 SSE |
| 实时聊天/多人协作 | WebSocket (Ratchet) |
| 移动端推送 | 极光/个推/阿里推送 |
| 大型应用 | Redis + WebSocket + 消息队列 |
性能优化建议
// 1. 使用Redis缓存已读状态 // 2. 消息只存有变更的,减少数据库查询 // 3. 错误处理要考虑网络异常 // 4. 使用Pulsar/Kafka等MQ异步处理大批量消息 // 5. 监控WebSocket连接数,设置最大连接数限制
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播