Komunikacja w czasie rzeczywistym w przeglądarce
Klasyczny model HTTP to żądanie-odpowiedź: klient pyta, serwer odpowiada. Ale co, gdy serwer chce poinformować klienta o czymś bez pytania? Powiadomienia, czat na żywo, aktualizacje statusu, dane giełdowe — wszystkie te scenariusze wymagają komunikacji w czasie rzeczywistym.
Dwa główne podejścia to WebSockets i Server-Sent Events (SSE). Oba rozwiązują problem "serwer chce wysłać dane do klienta", ale robią to w fundamentalnie różny sposób. Wybór między nimi ma konkretne konsekwencje dla architektury, wydajności i złożoności aplikacji.
WebSockets — pełny duplex
WebSocket to protokół ustanawiający stałe, dwukierunkowe połączenie TCP między klientem a serwerem. Po początkowym handshake'u (upgrade z HTTP) obie strony mogą wysyłać dane w dowolnym momencie, bez narzutu nagłówków HTTP.
Klient w JavaScript:
const ws = new WebSocket('wss://example.com/ws');
ws.onopen = () => ws.send(JSON.stringify({ type: 'subscribe', channel: 'updates' }));
ws.onmessage = (event) => console.log(JSON.parse(event.data));
ws.onclose = () => console.log('Rozłączono');
Serwer w Go (z pakietem gorilla/websocket lub standardowej biblioteki od Go 1.22):
func handleWS(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer conn.Close()
for {
_, msg, err := conn.ReadMessage()
if err != nil {
break
}
// przetwórz wiadomość i odpowiedz
conn.WriteMessage(websocket.TextMessage, response)
}
}
Zalety WebSockets:
- Dwukierunkowa komunikacja — klient i serwer wysyłają dane swobodnie
- Niski narzut — po handshake'u ramki danych mają minimalny overhead (2-14 bajtów)
- Binarny i tekstowy — obsługa obu typów danych
- Niskie opóźnienia — brak narzutu HTTP na każdą wiadomość
Wady:
- Złożoność — zarządzanie połączeniami, heartbeat, reconnect, stany błędów
- Problemy z proxy — starsze proxy HTTP i firewalle mogą blokować WebSockety
- Skalowalność — każde połączenie to stały koszt pamięci na serwerze
- Brak automatycznego reconnectu — trzeba implementować samodzielnie
Server-Sent Events — prostota jednokierunkowa
SSE to mechanizm wbudowany w standard HTTP, pozwalający serwerowi wysyłać strumień zdarzeń do klienta. Komunikacja jest jednokierunkowa — tylko serwer do klienta. Klient komunikuje się z serwerem normalnym HTTP (POST, PUT, etc.).
Klient w JavaScript:
const source = new EventSource('/events');
source.onmessage = (event) => console.log(event.data);
source.addEventListener('notification', (event) => {
console.log('Powiadomienie:', JSON.parse(event.data));
});
Serwer w Go:
func handleSSE(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")
flusher, _ := w.(http.Flusher)
for {
select {
case <-r.Context().Done():
return
case msg := <-messages:
fmt.Fprintf(w, "event: notification\ndata: %s\n\n", msg)
flusher.Flush()
}
}
}
Zalety SSE:
- Prostota — działa na zwykłym HTTP, nie wymaga specjalnego protokołu
- Automatyczny reconnect — przeglądarka sama wznawia połączenie po zerwaniu
- Event ID — wbudowany mechanizm śledzenia ostatniego odebranego zdarzenia, pozwalający wznowić strumień od miejsca przerwania
- Kompatybilność — działa przez każdy proxy HTTP, firewall, load balancer
- Standardowa biblioteka Go — nie potrzeba zewnętrznych pakietów
Wady:
- Jednokierunkowość — tylko serwer do klienta
- Tylko tekst — brak wsparcia dla danych binarnych (trzeba kodować w Base64)
- Limit połączeń — przeglądarki ograniczają liczbę jednoczesnych połączeń SSE per domena (zwykle 6 w HTTP/1.1, bez limitu w HTTP/2)
- Brak w IE — Internet Explorer nie obsługuje SSE (ale to już mało istotne w 2025 roku)
Kiedy wybrać WebSockets
WebSockets to właściwy wybór, gdy potrzebujesz dwukierunkowej komunikacji w czasie rzeczywistym:
- Czat — użytkownicy wysyłają i odbierają wiadomości
- Gry multiplayer — ciągła wymiana stanu gry
- Współedycja dokumentów — np. Google Docs, gdzie wielu użytkowników edytuje jednocześnie
- Terminale webowe — interaktywna sesja z serwerem
- Streaming binarny — przesyłanie audio/video
Reguła kciuka: jeśli klient musi wysyłać dane do serwera z tą samą częstotliwością co serwer do klienta — wybierz WebSockets.
Kiedy wybrać SSE
SSE jest lepszym wyborem, gdy komunikacja jest głównie jednokierunkowa (serwer do klienta):
- Powiadomienia — nowe wiadomości, alerty, statusy
- Dashboardy — aktualizacje metryk w czasie rzeczywistym
- Feedy aktywności — nowe posty, komentarze, logi
- Postęp operacji — pasek postępu długo trwającego zadania
- Aktualizacje cenowe — kursy walut, ceny akcji (jednokierunkowy strumień)
Jeśli klient rzadko wysyła dane (i może to robić zwykłym POST/PUT) — SSE jest prostszym i bardziej niezawodnym rozwiązaniem.
Porównanie praktyczne
| Cecha | WebSockets | SSE | |-------|-----------|-----| | Kierunek | Dwukierunkowy | Serwer → klient | | Protokół | ws:// / wss:// | HTTP | | Reconnect | Ręczny | Automatyczny | | Dane binarne | Tak | Nie (Base64) | | Proxy/firewall | Problematyczne | Bez problemów | | Złożoność serwera | Wyższa | Niższa | | Zależności w Go | Zewnętrzny pakiet* | Standardowa biblioteka |
*Od Go 1.22 pakiet net/http zawiera eksperymentalne wsparcie dla WebSockets, ale w produkcji nadal częściej stosuje się gorilla/websocket lub nhooyr/websocket.
Wzorzec hybrydowy
W praktyce wiele aplikacji łączy oba podejścia. SSE do powiadomień push (serwer informuje klienta o zmianach), zwykłe HTTP POST/PUT do akcji użytkownika. Ten wzorzec jest prostszy w implementacji i debugowaniu niż pełny WebSocket, a wystarcza dla większości przypadków.
Przykład: system zarządzania zadaniami. Użytkownik tworzy zadanie (POST), zmienia status (PUT), a SSE informuje wszystkich o aktualizacjach w czasie rzeczywistym. Brak potrzeby WebSocketów — komunikacja klient-serwer jest sporadyczna i nieinteraktywna.
Podsumowanie
WebSockets i SSE to nie konkurencyjne technologie, lecz narzędzia do różnych zastosowań. SSE powinno być domyślnym wyborem — jest prostsze, niezawodniejsze i nie wymaga zewnętrznych bibliotek w Go. Po WebSockets sięgaj, gdy naprawdę potrzebujesz dwukierunkowej komunikacji z niskim opóźnieniem.
Planujesz aplikację z komunikacją w czasie rzeczywistym? Skontaktuj się z nami — pomożemy wybrać odpowiednie podejście.