关于(SSE: Server-Sent Events)的代码实现

本贴结合分享和问答,由问答贴 Laravel + SSE(Server-Sent Events)消息未连续发送 转化
(原帖由于问题定位不明以及多次编辑,导致逻辑有些混乱)

结论

实现:在纯 PHP 代码和 Laravel 代码都可实现;

运行:builtin-server (php -S) 可行,Nginx 可行,Apache 不正常

实现代码

前端

query();
function query() {
    if (!window.EventSource) {
        alert("浏览器不支持 EventSource");
        return;
    }

    var eventSource = new EventSource("/");

    eventSource.onopen = function (e) {
        console.log("onEventSourceOpen")
    }
    eventSource.onmessage = function (e) {
        console.log("onEventSourceMessage: " + e.data);
    }
    eventSource.onerror = function (e) {
        console.log("onEventSourceError: ", e);
    }
}

后端

后端代码仅在 php -S 和 Nginx 测试通过,Apache 方式暂未测试通过

PHP

header("Cache-Control: no-cache");
header("Content-Type: text/event-stream");
header("Connection: keep-alive");
header("X-Accel-Buffering: no");

while (true) {
    if(connection_aborted()) exit();

    $ts = microtime(true);

    // echo str_repeat("\n", 100000);
    echo "event: message\ndata: $ts\nid: $ts\n\n";

    ob_end_flush(); // 或 ob_flush()
    flush();

    sleep(1);
}

Laravel

// 这里加 return 或者末尾加 ->send() 都可以
return (new \Symfony\Component\HttpFoundation\StreamedResponse(function () {
    while (true) {
        if (connection_aborted()) return;

        $ts = microtime(true);
        echo str_repeat("\n", 50000);
        echo "event: message\ndata: $ts $ts\nid: $ts\n\n";

        // Laravel 中不加以下两个 flush 也能测试成功
        ob_flush();
        flush();

        sleep(1);
    }
}, 200, [
    "Content-Type" => "text/event-stream",
    "Cache-Control" => "no-cache",
    "Connection" => "Keep-Alive",
    "X-Accel-Buffering" => "no",
]))->send();

相关设置

builtin:直接 php -S 运行即可,注意写对 Content-Type,以及输出缓冲的刷新

Nginx:fastcgi 模式的话需要设置 fastcgi_buffering off;,proxy 模式的话需要设置 proxy_buffering off;(未测试);或者 php 代码设置请求头 X-Accel-Buffering: no

Apache:直接访问的话,效果是在超出 PHP 的最大执行时间之后(比如 30 秒)一次性输出很多数据;如想正常输出,可以在正常数据之前,先输出一大段空白内容(但是这个感觉是偏方,目前还没有找到能直接在代码里刷新 apache 输出缓存的方法,如有合适方法还请告知一下

吐槽:没想到测试一个 SSE,最大的障碍是缓冲区

讨论数量: 6

PHP实现会一个连接占用一个FPM进程吧

2周前 评论
zhaojjiang (楼主) 2周前
cevin (作者) 2周前
zhaojjiang (楼主) 2周前
cevin (作者) 2周前
zhaojjiang (楼主) 2周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!