İçeriğe geç

Canlı Veri — Polling, SSE ve WebSocket

Orta 9 dk Sık karşılaşılır

Önce şunu oku: JavaScript Olay Döngüsü — setTimeout(0) Neden Sonra Yazıyor?

30 saniyede özet

Tarayıcı sunucuya "yeni bir şey var mı?" diye sık sık sorabilir (polling), sunucu açık bir cevaptan olayları akıtabilir (SSE) ya da iki taraf tek bir bağlantıdan konuşabilir (WebSocket). Seçim, verinin yönüne ve sıklığına bağlı.

Kargo bekliyorsun ve her beş dakikada bir firmayı arayıp kargonun gelip gelmediğini sorabilirsin. Ya da kurye kapıya gelince seni arar ve sen hiç uğraşmadan haberi alırsın.

  1. Bayt: Gelen havaleler ekranda canlı görünsün istiyoruz. Her iki saniyede bir sunucuya soralım!

  2. Sen: Bir dakikada otuz istek. Kaçında yeni havale var?

  3. Bayt: Dördünde... gerisi "yeni bir şey yok" dedi.

  4. Bayt: Belki sormak yerine sunucunun haber vermesini sağlamalıyız. Seçeneklere bakalım.

Sormak: polling

pollingTarayıcının belli aralıklarla sunucuya "yeni bir şey var mı?" diye sorması. Kolaydır ama ya boş istek ya da gecikme üretir.Sözlükte gör →, tarayıcının belli aralıklarla sunucuya “yeni bir şey var mı?” diye sormasıdır. Kurması en kolay yoldur; her sunucu ve her proxy bunu destekler.

Ama bir ikilem var. Sık sorarsan isteklerin çoğu boş döner ve sunucu boşuna çalışır. Seyrek sorarsan havale ekrana saniyeler sonra gelir.

Kafam karıştı, daha basit anlat

Polling: düzenli aralıklarla sor. Sık sorarsan israf, seyrek sorarsan gecikme.

Hızlı kontrolBaşlangıç

Polling nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

İki saniyede bir sorulan bir dakikada yalnızca dört yeni olay var. Ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Haber vermek: SSE ve WebSocket

Sunucu her havaleyi olduğu an bildirmek istiyor; tarayıcının sunucuya bir şey göndermesi gerekmiyor. Kaç bağlantı yeter? Cevabı göster

Bir tane. Tarayıcı tek bir istek açar, sunucu cevabı kapatmaz ve her havaleyi bu açık cevabın içine yazar.

Sormak mı, haber almak mı?
Adım adım oku
  1. Sık sık 'geldi mi?' diye aramak.
  2. Aramaların çoğu boşa: 'henüz yok'.
  3. Kurye gelince kendisi haber verir.
  4. Açık bir hat varsa haber gelince hemen duyarsın.

Bu yöntemin adı Server-Sent EventsSunucunun açık tuttuğu tek bir HTTP cevabına olay yazdığı, tek yönlü akış. Tarayıcıda EventSource ile açılır ve kopunca kendisi yeniden bağlanır.Sözlükte gör →. Tarayıcıda EventSource ile açılır, düz HTTP üzerinde çalışır ve bağlantı koparsa tarayıcı kendisi yeniden bağlanır. Ama tek yönlüdür: tarayıcı bir şey göndermek isterse ayrı bir istek atar.

İki taraf da sık sık konuşuyorsa WebSocketTarayıcı ile sunucu arasında açık kalan, iki yönlü tek bağlantı. İki taraf da sık konuşuyorsa uygundur.Sözlükte gör → kullanılır. Tek bir bağlantı açılır ve iki yönde de mesaj gider. Karşılığında yeniden bağlanmayı ve mesaj biçimini sen yönetirsin.

Kafam karıştı, daha basit anlat

SSE: sunucudan tarayıcıya tek yönlü akış. WebSocket: iki yönlü konuşma.

Hızlı kontrolOrta

Server-Sent Events (SSE) nasıl çalışır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Ne zaman WebSocket, SSE'den daha uygun olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

Sunucudan canlı haber almak

Tohum 249012

Sunucuda olan havaleler

Tarayıcının açtığı istekler: 0

Ekrana gelen havaleler

  • ● havale oldu
  • | istek
  • ✓ hemen görüldü
  • ✕ 5 sn ya da daha geç

Oynat ya da adımla: her adım on saniye.

Hız
Adım 0

Şu an ne oldu?

Polling (2 sn)

Bir dakika içinde 5 havale gelecek. Tarayıcı bunu nasıl öğrenecek?

Görevler0/3

  • Yirmiden fazla isteğin boş dönmesini sağlaaçık

    İpucu

    Sık sık sor.

  • Bir havaleyi ekrana en az 5 saniye geç getiraçık

    İpucu

    Seyrek sor.

  • Kullanıcı da yazarken her şeyi tek bağlantıyla halletaçık

    İpucu

    İki yönlü bir bağlantı gerek.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

  1. Varsayılanla oynat. İki saniyede bir sormak: 30 istek, 26’sı boş döndü.
  2. “Polling (10 sn)” seç. Yalnızca 6 istek, ama bir havale ekrana 9 saniye geç geldi.
  3. “Long polling” seç. Sunucu isteği bir şey olana kadar tuttu; gecikme yok, ama her havaleden sonra yeni bir istek açıldı.
  4. “Server-Sent Events” seç. Tek bağlantı, gecikme yok.
  5. “Kullanıcı da mesaj gönderiyor”u aç, SSE ile WebSocket’i karşılaştır. SSE’de her mesaj ayrı bir istek oldu; WebSocket tek bağlantıda kaldı.
Hızlı kontrolOrta

Long polling'de sunucu ne yapar?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

SSE kullanan bir sohbet ekranında kullanıcı da mesaj gönderiyor. Mesajlar nasıl gider?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Tuzaklar

Kopmayı unutmak: mobil ağda bağlantılar sık kopar. SSE yeniden bağlanırken son gördüğü olayın kimliğini (Last-Event-ID) gönderir ve sunucu kaçanları buradan tamamlamalı; WebSocket’te bunu sen yazarsın.

Proxy’lerin sessiz bağlantıyı kesmesi. Uzun süre hiçbir şey akmayan bağlantıları ara katmanlar kapatabilir. Sunucu birkaç saniyede bir küçük bir “hâlâ buradayım” mesajı göndermeli.

Gerekmeyen yerde WebSocket. Yalnızca sunucudan bildirim gerekiyorsa WebSocket ekstra karmaşıklık getirir. Önce SSE’yi düşün; iki yönlü ve sık konuşma varsa WebSocket’e geç.

Kafam karıştı, daha basit anlat

Kopmaya hazırlan, sessiz bağlantıya nabız gönder, gerekmedikçe WebSocket’e geçme.

Hızlı kontrolOrta

Mobil ağda SSE bağlantısı kopup yeniden bağlandı. Kaçan olaylar nasıl tamamlanır?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aşağıdaki örnek bir mobil bankanın gelen havale bildirimlerinden: Spring tarafında SSE, tarayıcıda EventSource, kaçan olayları Last-Event-ID ile tamamlama.

Derinleş · Gelen havaleler: SSE ile canlı bildirim 4 dosya · ~84 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/java/com/bank/live/ TransferStreamController.java Akış: her müşteri için bir SseEmitter; havale olduğunda olay kimliğiyle yazılıyor.

src/main/java/com/bank/live/TransferStreamController.java
@RestController
class TransferStreamController {
private final TransferFeed feed;
TransferStreamController(TransferFeed feed) {
this.feed = feed;
}
@GetMapping(path = "/accounts/{id}/transfers/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
SseEmitter stream(@PathVariable String id,
@RequestHeader(name = "Last-Event-ID", required = false) String lastSeen) {
SseEmitter emitter = new SseEmitter(Duration.ofMinutes(30).toMillis());
feed.missedSince(id, lastSeen).forEach(transfer -> feed.send(emitter, transfer)); // fill the gap first
feed.subscribe(id, emitter);
return emitter;
}
}

src/main/java/com/bank/live/ TransferFeed.java Yeniden bağlanma: Last-Event-ID gelirse kaçan havaleler önce gönderiliyor.

src/main/java/com/bank/live/TransferFeed.java
@Component
class TransferFeed {
private final Map<String, Set<SseEmitter>> listeners = new ConcurrentHashMap<>();
private final TransferRepository transfers;
TransferFeed(TransferRepository transfers) {
this.transfers = transfers;
}
void subscribe(String accountId, SseEmitter emitter) {
Set<SseEmitter> set = listeners.computeIfAbsent(accountId, key -> ConcurrentHashMap.newKeySet());
set.add(emitter);
emitter.onCompletion(() -> set.remove(emitter));
emitter.onTimeout(() -> set.remove(emitter));
emitter.onError(error -> set.remove(emitter));
}
List<IncomingTransfer> missedSince(String accountId, String lastSeen) {
return lastSeen == null ? List.of() : transfers.findAfter(accountId, Long.parseLong(lastSeen));
}
@EventListener
void on(IncomingTransfer transfer) {
listeners.getOrDefault(transfer.accountId(), Set.of()).forEach(emitter -> send(emitter, transfer));
}
void forEachEmitter(Consumer<SseEmitter> action) {
listeners.values().forEach(set -> set.forEach(action));
}
void send(SseEmitter emitter, IncomingTransfer transfer) {
try {
emitter.send(SseEmitter.event().id(String.valueOf(transfer.sequence())).name("transfer").data(transfer));
} catch (IOException gone) {
emitter.completeWithError(gone); // the browser left; it will reconnect if it wants to
}
}
}

src/main/java/com/bank/live/ Heartbeat.java Nabız: 15 saniyede bir yorum satırı; proxy'ler bağlantıyı sessiz sanıp kesmiyor.

src/main/java/com/bank/live/Heartbeat.java
@Component
class Heartbeat {
private final TransferFeed feed;
Heartbeat(TransferFeed feed) {
this.feed = feed;
}
/** A comment line every 15 seconds keeps proxies from closing a quiet stream. */
@Scheduled(fixedRate = 15_000)
void beat() {
feed.forEachEmitter(emitter -> {
try {
emitter.send(SseEmitter.event().comment("ping"));
} catch (IOException gone) {
emitter.completeWithError(gone);
}
});
}
}

src/app/transfers/ liveTransfers.ts Tarayıcı: EventSource yeniden bağlanmayı kendisi yapıyor; ekran yalnızca olayı çiziyor.

src/app/transfers/liveTransfers.ts
export function watchTransfers(accountId: string, onTransfer: (t: IncomingTransfer) => void): () => void {
// EventSource reconnects on its own and sends Last-Event-ID for us.
const source = new EventSource(`/accounts/${accountId}/transfers/stream`, { withCredentials: true });
source.addEventListener('transfer', (event) => onTransfer(JSON.parse(event.data) as IncomingTransfer));
return () => source.close();
}

Kendini sına

Şimşek turu1/4

Sık polling hem hızlı hem ucuzdur.

Soru 1/3İleri

Uzun süre olay gelmeyen SSE bağlantıları ara katmanda kesiliyor. Çözüm nedir?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Aklında kalacak üç şey

  1. 1 Polling basittir ama ya geç kalır ya da boşa sorar. Sık sorarsan isteklerin çoğu "yeni bir şey yok" ile döner, seyrek sorarsan haberler geç gelir.
  2. 2 Server-Sent Events tek yönlüdür: sunucu, açık tuttuğu tek bir HTTP cevabına olayları yazar. Tarayıcı bağlantı koparsa kendisi yeniden bağlanır.
  3. 3 WebSocket iki yönlüdür ve iki taraf da sık sık konuşuyorsa doğru seçimdir. Yalnızca sunucudan haber gerekiyorsa SSE daha sadedir.
Sonraki kapı Senin bilgisayarında sayfa bir anda açılıyor. Müşterinin telefonunda neden üç ayrı şikâyet var? Core Web Vitals — Sayfa Ne Zaman Gerçekten Hazır? · 9 dk

4 kart sonraki derste seni bekliyor

0/4 kart bu dersten toplandı