Gymterview
middle

Как реализовать Server-Sent Events (SSE) с помощью WebFlux?

Server-Sent Events (SSE) — стандарт HTML5 для однонаправленной потоковой передачи данных от сервера к клиенту через HTTP. В отличие от WebSocket, SSE работает поверх обычного HTTP и поддерживает автоматическое переподключение браузером.

Простейший SSE-эндпоинт

Пример
@RestController
public class SseController {

    @GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamEvents() {
        return Flux.interval(Duration.ofSeconds(1))
            .map(i -> "Event #" + i);
    }
}

SSE с типизированными событиями

Пример
@GetMapping(value = "/notifications", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<NotificationDto>> streamNotifications() {
    return notificationService.getNotifications()
        .map(notification -> ServerSentEvent.<NotificationDto>builder()
            .id(notification.getId().toString())
            .event(notification.getType())
            .data(notification)
            .retry(Duration.ofSeconds(5))
            .build());
}
Реальный пример — лента обновлений с Sinks + SSE
@Service
public class UpdateService {
    private final Sinks.Many<Update> sink =
        Sinks.many().multicast().onBackpressureBuffer(100);

    public void publishUpdate(Update update) {
        sink.tryEmitNext(update);
    }

    public Flux<Update> getUpdates() {
        return sink.asFlux();
    }
}

@RestController
public class UpdateController {

    private final UpdateService updateService;

    @GetMapping(value = "/updates/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Update>> stream() {
        return updateService.getUpdates()
            .map(update -> ServerSentEvent.builder(update)
                .id(UUID.randomUUID().toString())
                .event("update")
                .build());
    }
}

Клиент на JavaScript

Пример
const eventSource = new EventSource('/updates/stream');

eventSource.addEventListener('update', (event) => {
    const data = JSON.parse(event.data);
    console.log('Получено обновление:', data);
});

eventSource.onerror = (error) => {
    console.error('SSE ошибка:', error);
    // Браузер автоматически переподключится
};

Частые ошибки

  • Не указать produces = TEXT_EVENT_STREAM_VALUE — клиент получит JSON-массив вместо потока
  • Отправлять слишком большие объекты — SSE предназначен для лёгких уведомлений
  • Забывать про CORS — SSE-запросы подчиняются политике CORS

Как используется в 2026

  • SSE — стандартный подход для push-уведомлений, дашбордов, live-лент
  • Для двунаправленного обмена — WebSocket, для однонаправленного — SSE
  • SSE хорошо работает с CDN и прокси (в отличие от WebSocket, требующего Upgrade)
  • В микросервисной архитектуре SSE используется совместно с Kafka

На собеседовании: покажите знание практической реализации: MediaType.TEXT_EVENT_STREAM_VALUE + Flux. Частая ошибка — путать SSE с WebSocket. SSE — однонаправленный (сервер -> клиент), работает поверх HTTP, браузер переподключается автоматически. WebSocket — двунаправленный, требует Upgrade.