Отправка Server-Sent Events (SSE) в Laravel

Server-Sent Events (SSE) - это стандартный способ отправки односторонних событий от сервера к браузеру через HTTP. По своей идее SSE похож на WebSocket, но работает только в направлении сервер → клиент.

Это хорошо подходит, если:

  • нужно просто отправлять обновления с сервера в реальном времени;

  • нет необходимости в двусторонней связи (клиент → сервер);

  • не хочется разворачивать и поддерживать отдельный WebSocket-сервер.

При этом браузер получает события автоматически, без опроса сервера (polling).


Как это работает

В SSE всегда участвуют две стороны:

  • Клиентская - создаёт объект EventSource в JavaScript и открывает соединение.

  • Серверная (Laravel) - возвращает длительный потоковый HTTP-ответ (stream) и отправляет события по мере необходимости.

Соединение может жить долго, от нескольких минут до часов.


Настройка потокового ответа в Laravel

В Laravel поток легко создаётся с помощью метода response()->stream(). Для корректной работы нужно:

  • указать заголовок Content-Type: text/event-stream;

  • отключить буферизацию (важно для nginx);

  • отправлять данные в формате SSE.

Простой пример

return response()->stream(function () {
    // Здесь будем отправлять события
}, 200, [
    'Content-Type' => 'text/event-stream',
    'Cache-Control' => 'no-cache',
    'Connection' => 'keep-alive',
    'X-Accel-Buffering' => 'no',
    'Access-Control-Allow-Origin' => '*',
]);

Ключевой момент здесь text/event-stream заголовок, так браузер поймет, что ответ будет длительным и не закроет соединение.


Отправка события

Чтобы отправить событие клиенту, внутри stream достаточно вывести данные в нужном формате и принудительно сбросить буфер.

return response()->stream(function () {
    while (ob_get_level()) {
        ob_end_flush();
    }

    @ini_set('output_buffering', 'off');
    @ini_set('zlib.output_compression', '0');
    set_time_limit(0);

    // Время между попытками переподключения (мс)
    echo "retry: 2000\n\n";
    @ob_flush(); flush();

    // Отправляем данные
    echo "data: " . json_encode([
        'timestamp' => date('Y-m-d H:i:s'),
        'data' => 'Your Data'
    ]) . "\n\n";

    @ob_flush(); flush();
});

Важно помнить:

  • каждое событие обязательно начинается с data:;

  • событие заканчивается пустой строкой (\n\n);

  • данные могут быть как простой строкой, так и JSON.


Что делать при долгих процессах

Если сервер долго ничего не отправляет (например, выполняется тяжёлый процесс), соединение может быть закрыто браузером или прокси.

Чтобы этого избежать, отправляют heartbeat-события - служебные сообщения, которые просто поддерживают соединение активным.

$lastHeartbeat = time();

// … ваш долгий процесс
while ($process->isRunning()) {

    // каждые 30 секунд отправляем heartbeat
    if (time() - $lastHeartbeat >= 30) {
        echo "data: " . json_encode([
            'timestamp' => date('Y-m-d H:i:s'),
            'type' => 'heartbeat',
            'output' => 'Connection alive',
        ]) . "\n\n";

        @ob_flush(); flush();
        $lastHeartbeat = time();
    }
}

Сервер отправляет такие данные, браузер их получает, но на клиенте их можно просто игнорировать, они нужны только для поддержания соединения.


Клиентская часть(подписка на SSE)

На стороне клиента Server-Sent Events работают из коробки, без библиотек и сложной настройки. Всё, что нужно, это обьект EventSource.

Базовый пример на JavaScript

<script>
    const source = new EventSource('/sse');

    source.onmessage = function (event) {
        const data = JSON.parse(event.data);
        console.log('Новое событие:', data);
    };

    source.onerror = function (error) {
        console.error('Ошибка SSE:', error);
    };
</script>

Что здесь происходит:

  • браузер открывает постоянное HTTP-соединение с /sse;

  • сервер может отправлять события сколько угодно долго;

  • каждая строка data: на сервере превращается в event.data в браузере.


Когда использовать SSE

SSE стоит использовать, если:

  • нужна простая, односторонняя связь от сервера к клиенту;

  • приложение небольшое и не требует сложной масштабируемости;

  • WebSocket-сервер избыточен для текущей задачи.

SSE не подойдёт, если:

  • требуется двусторонняя связь;

  • ожидается большое количество одновременных подключений;

  • нужна высокая масштабируемость (в таких случаях лучше WebSocket, Laravel Reverb или аналогичные решения).

Комментарии (0)

Войдите, чтобы оставить комментарий

Похожие статьи

PHP Generators

Статья рассказывает о PHP генераторах - что это, как они работают, зачем нужны и как использовать их в реальных проектах. Примеры включают чтение больших CSV и потоковую обработку данных.

10 0 2 мин

Как создать AI-агента на Python с OpenAI для автоматического парсинга счетов

Подробный гайд, как разработать полноценного AI-агента на Python и OpenAI API, который автоматически парсит счета (PDF, фото), вытягивает данные в структурированный JSON, валидирует их и сохраняет в CSV или базу. Плюс интеграция с Telegram-ботом для загрузки документов.

13 0 3 мин

Как установить Docker и Docker Compose на Ubuntu и RedHat системы (2025)

Подробная инструкция по установке Docker и Docker Compose на Debian-based и RedHat-based системы. Разбор всех команд по шагам, настройка GPG-ключей, добавление репозиториев, запуск сервиса, проверка и устранение типичных ошибок.

17 0 2 мин