Ответ
Да, я знаком с Server-Sent Events (SSE). Это технология, которая позволяет серверу отправлять обновления клиенту в одностороннем порядке через одно HTTP-соединение. В отличие от WebSockets, SSE проще и работает поверх стандартного HTTP, но не поддерживает двунаправленную связь.
Ключевые особенности SSE:
- Однонаправленная связь: Только сервер может отправлять данные клиенту (server -> client).
- Простота: Легко реализуется как на сервере, так и на клиенте (стандартный
EventSourceAPI в браузерах). - Автоматическое переподключение: Браузерный клиент автоматически пытается восстановить соединение в случае обрыва.
Сценарии применения: SSE идеально подходит для задач, где не требуется ответ от клиента, например:
- Лента новостей или обновлений в реальном времени.
- Системы уведомлений.
- Отображение статуса выполнения долгой задачи (например, обработка видео).
- Live-дэшборды с котировками или метриками.
Пример реализации на Go
Сервер должен установить специальные заголовки и отправлять события в формате data: <сообщение>nn.
func sseHandler(w http.ResponseWriter, r *http.Request) {
// Устанавливаем необходимые заголовки для SSE
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// http.Flusher необходим для отправки данных клиенту по частям,
// а не после завершения всего ответа.
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
// Бесконечный цикл для отправки событий
for {
select {
// Если клиент закрыл соединение, r.Context().Done() получит сигнал
case <-r.Context().Done():
fmt.Println("Client disconnected")
return
// Отправляем событие каждую секунду
default:
// Формат события: "data: {message}nn"
fmt.Fprintf(w, "data: Текущее время: %snn", time.Now().Format(time.RFC1123))
flusher.Flush() // Отправляем буферизированные данные клиенту
time.Sleep(1 * time.Second)
}
}
}
Клиентская часть (JavaScript)
// Создаем подключение к нашему SSE-эндпоинту
const eventSource = new EventSource('/sse');
// Обработчик для всех сообщений без указанного event
eventSource.onmessage = (event) => {
console.log('Новое сообщение:', event.data);
};
// Обработчик ошибок
eventSource.onerror = (err) => {
console.error("EventSource failed:", err);
}; Ответ 18+ 🔞
О, а вот это, блядь, Server-Sent Events, SSE! Ну, технология, которая позволяет серверу, сука, писать клиенту в ухо, как радио, в одностороннем порядке. Одно HTTP-соединение, и всё, пиздец, понеслась. Не то что эти ваши WebSockets с их двусторонней болтовнёй — тут всё проще, на обычном HTTP, но клиент, понятное дело, ответить не может, только слушать, как идиот.
Что за зверь такой, этот SSE:
- Однорукий бандит: Данные летят только в одну сторону — от сервера к клиенту. Сервер -> клиент, и всё, блядь. Обратно — ни хуя.
- Проще пареной репы: Реализовать — не вставая с дивана. На сервере — заголовки поправить, на клиенте — стандартный
EventSourceв браузере. - Самовосстанавливающийся: Если соединение отвалилось, браузерный клиент сам, сука, попытается переподключиться, как назойливая муха. Удобно, блядь.
Куда это воткнуть: Идеально, ёпта, для ситуаций, когда клиенту нужно только получать пинки, а не отвечать. Например:
- Лента новостей, которая сама обновляется, пока ты кофе пьёшь.
- Уведомления, которые выскакивают, как чёртики из табакерки.
- Прогресс-бар для долгой задачи, типа "рендерим видео, сиди, жди, не дыши".
- Дашборды с биржевыми котировками, где цифры пляшут, как угорелые.
Как это выглядит на Go, ёпта
Серверу нужно надеть правильную шапку и слать события в строгом формате: data: <сообщение>nn. Два переноса строки, блядь, не один, а то клиент обосрётся и ничего не поймёт.
func sseHandler(w http.ResponseWriter, r *http.Request) {
// Надеваем на ответ правильные заголовки, как парадную форму
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
// http.Flusher — это такая штука, чтобы данные улетали к клиенту сразу, а не копились до конца.
// Без него — нихуя не работает, клиент будет ждать вечность.
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported!", http.StatusInternalServerError)
return
}
// Бесконечный цикл, чтобы долбить клиента сообщениями, пока он не сбежит
for {
select {
// Если клиент нажал крестик или связь порвалась, r.Context().Done() даст знать
case <-r.Context().Done():
fmt.Println("Client disconnected")
return
// А так — работаем, пашем, шлём данные
default:
// Вот этот самый магический формат, блядь: "data: {message}nn"
fmt.Fprintf(w, "data: Текущее время: %snn", time.Now().Format(time.RFC1123))
flusher.Flush() // Выталкиваем буферизированные данные наружу, к клиенту
time.Sleep(1 * time.Second) // Ждём секунду, чтобы не заспамить насмерть
}
}
}
Клиентская часть (JavaScript), там вообще красота
// Подключаемся к нашему эндпоинту, как к пожарной сирене
const eventSource = new EventSource('/sse');
// Этот обработчик ловит все сообщения, у которых не указан специальный 'event'
eventSource.onmessage = (event) => {
console.log('Новое сообщение:', event.data); // И вуаля, данные прилетели
};
// А тут ловим всякие косяки — обрыв связи и прочую хуйню
eventSource.onerror = (err) => {
console.error("EventSource failed:", err);
};
Вот и вся магия, блядь. Клиент сидит, слушает, сервер его поливает данными, как из брандспойта. Красота, в рот меня чих-пых!