PHP 怎么PHP 服务事件

wen PHP项目 1

本文目录导读:

PHP 怎么PHP 服务事件

  1. 基本实现原理
  2. 高级功能实现
  3. 重要注意事项
  4. 完整示例:实时消息通知系统

PHP 服务端事件(Server-Sent Events, SSE)是一种允许服务器主动向客户端推送数据的技术,在 PHP 中实现 SSE 相对简单,主要通过设置响应头并不断输出特定格式的数据来完成。

基本实现原理

服务器端 PHP 脚本

<?php
// 设置 SSE 需要的响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// 禁用输出缓冲
@ob_end_flush();
ob_implicit_flush(true);
// 无限循环发送事件
while (true) {
    // 获取当前时间
    $time = date('Y-m-d H:i:s');
    // 发送事件
    echo "data: 当前时间: {$time}\n\n";
    flush();
    // 每 5 秒发送一次
    sleep(5);
    // 检测客户端是否断开连接
    if (connection_aborted()) {
        break;
    }
}
?>

客户端 JavaScript 代码

<!DOCTYPE html>
<html>
<head>SSE Demo</title>
</head>
<body>
    <div id="events"></div>
    <script>
        // 创建 EventSource 连接
        const eventSource = new EventSource('sse.php');
        // 监听消息事件
        eventSource.onmessage = function(event) {
            const eventsDiv = document.getElementById('events');
            const p = document.createElement('p');
            p.textContent = `收到: ${event.data}`;
            eventsDiv.appendChild(p);
        };
        // 监听错误
        eventSource.onerror = function(event) {
            console.error('SSE 连接错误:', event);
        };
        // 关闭连接的函数
        function closeConnection() {
            eventSource.close();
            console.log('连接已关闭');
        }
    </script>
</body>
</html>

高级功能实现

带事件类型的 SSE

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// 发送不同事件类型
echo "event: userUpdate\n";
echo "data: {\"username\": \"john\", \"action\": \"login\"}\n\n";
flush();
echo "event: notification\n";
echo "id: 123\n";  // 可选的事件ID
echo "data: 你有新的消息\n\n";
flush();

客户端监听特定事件:

const eventSource = new EventSource('events.php');
eventSource.addEventListener('userUpdate', function(e) {
    console.log('用户更新:', JSON.parse(e.data));
});
eventSource.addEventListener('notification', function(e) {
    console.log('通知:', e.data);
});

带重连机制的 SSE

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
// 设置重连时间(毫秒)
echo "retry: 3000\n\n";
$counter = 0;
while (true) {
    $counter++;
    // 发送带 ID 的事件,确保客户端能跟踪
    echo "id: {$counter}\n";
    echo "data: 消息 #{$counter}\n\n";
    flush();
    if (connection_aborted()) {
        break;
    }
    sleep(2);
}
?>

从数据库推送事件

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
// 数据库连接
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password');
$lastCheck = time();
while (true) {
    // 检查是否有新数据
    $stmt = $pdo->prepare("SELECT * FROM notifications WHERE created_at > :last_time");
    $stmt->execute([':last_time' => date('Y-m-d H:i:s', $lastCheck)]);
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        echo "event: {$row['type']}\n";
        echo "data: " . json_encode($row) . "\n\n";
        flush();
    }
    $lastCheck = time();
    if (connection_aborted()) {
        break;
    }
    sleep(2); // 每 2 秒检查一次
}
?>

重要注意事项

PHP 配置要求

; 需要在 php.ini 中设置
max_execution_time = 0    ; 取消执行时间限制
output_buffering = Off    ; 关闭输出缓冲

服务器配置

Apache:

# 启用 mod_headers 模块
Header set Cache-Control "no-cache"

Nginx:

# 禁用代理缓冲
proxy_buffering off;
proxy_cache off;
fastcgi_buffering off;

性能优化建议

<?php
// 使用数据库连接池或持久连接
// 实现心跳机制防止超时
while (true) {
    // 发送心跳
    echo ": heartbeat\n\n";
    flush();
    // 实际业务逻辑
    // ...
    // 控制频率
    usleep(500000); // 0.5秒
}
?>

完整示例:实时消息通知系统

服务器端 (notifications.php)

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
// 身份验证(示例)
$user_id = $_GET['user_id'] ?? die('User ID required');
// 建立与 Redis 的连接
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 订阅用户的通知频道
$channel = "user:{$user_id}:notifications";
while (true) {
    // 从 Redis 获取新消息
    $message = $redis->blPop([$channel], 2);
    if ($message) {
        $data = json_decode($message[1], true);
        echo "event: {$data['type']}\n";
        echo "data: " . json_encode([
            'title' => $data['title'],
            'message' => $data['message'],
            'timestamp' => time()
        ]) . "\n\n";
        flush();
    }
    // 发送心跳
    echo ": keepalive\n\n";
    flush();
    if (connection_aborted()) {
        break;
    }
}
?>

客户端 (index.html)

<!DOCTYPE html>
<html>
<head>实时通知</title>
</head>
<body>
    <div id="notifications"></div>
    <script>
        const userId = 1; // 当前用户ID
        const eventSource = new EventSource(`notifications.php?user_id=${userId}`);
        eventSource.addEventListener('message', function(e) {
            const notification = JSON.parse(e.data);
            showNotification(notification);
        });
        eventSource.addEventListener('alert', function(e) {
            const alert = JSON.parse(e.data);
            showAlert(alert);
        });
        function showNotification(data) {
            const div = document.getElementById('notifications');
            const p = document.createElement('p');
            p.textContent = `[${data.title}] ${data.message}`;
            div.appendChild(p);
        }
        function showAlert(data) {
            alert(`紧急通知: ${data.message}`);
        }
        eventSource.onerror = function() {
            console.log('尝试重新连接...');
        };
    </script>
</body>
</html>

SSE 比 WebSocket 更适合简单的服务器推送场景,实现更简单,且原生支持 HTTP 协议,但请注意它只支持单向通信(服务器→客户端),且连接数较多时对服务器资源消耗较大。

抱歉,评论功能暂时关闭!