WebSocketStream: Akışları WebSocket API ile entegre etme

Geri basınç uygulayarak uygulamanızın WebSocket mesajlarına boğulmasını veya WebSocket sunucusunun mesajlarla dolmasını önleyin.

Arka plan

WebSocket API, WebSocket protokolü için bir JavaScript arayüzü sağlar. Bu arayüz, kullanıcının tarayıcısı ile bir sunucu arasında iki yönlü etkileşimli bir iletişim oturumu açmayı mümkün kılar. Bu API ile, sunucuya yanıt için yoklama yapmadan sunucuya mesaj gönderebilir ve etkinliğe dayalı yanıtlar alabilirsiniz.

Streams API

Streams API, JavaScript'in ağ üzerinden alınan veri parçaları akışlarına programatik olarak erişmesini ve bunları istediği şekilde işlemesini sağlar. Akışlar bağlamında önemli bir kavram geri basınçtır. Bu, tek bir akışın veya bir boru zincirinin okuma ya da yazma hızını düzenlediği süreçtir. Akışın kendisi veya ardışık düzen zincirindeki sonraki bir akış hala meşgul olduğunda ve henüz daha fazla parça kabul etmeye hazır olmadığında, zincir boyunca geriye doğru bir sinyal göndererek teslimatı uygun şekilde yavaşlatır.

Mevcut WebSocket API'siyle ilgili sorun

Alınan mesajlara geri basınç uygulamak mümkün değildir.

Mevcut WebSocket API'sinde, bir mesaja tepki verme işlemi WebSocket.onmessage içinde gerçekleşir. Bu, sunucudan bir mesaj alındığında çağrılan bir EventHandler'dir.

Yeni bir mesaj alındığında yoğun veri işleme işlemleri yapması gereken bir uygulamanız olduğunu varsayalım. Akışı büyük olasılıkla aşağıdaki koda benzer şekilde ayarlarsınız ve await process() çağrısının sonucunu aldığınız için sorun olmaz, değil mi?

// A heavy data crunching operation.
const process = async (data) => {
  return new Promise((resolve) => {
    window.setTimeout(() => {
      console.log('WebSocket message processed:', data);
      return resolve('done');
    }, 1000);
  });
};

webSocket.onmessage = async (event) => {
  const data = event.data;
  // Await the result of the processing step in the message handler.
  await process(data);
};

Yanlış! Mevcut WebSocket API'sindeki sorun, geri basınç uygulamanın mümkün olmamasıdır. Mesajlar, process() yönteminin işleyebileceğinden daha hızlı geldiğinde oluşturma işlemi, bu mesajları arabelleğe alarak belleği doldurur, %100 CPU kullanımı nedeniyle yanıt vermez veya her ikisi de gerçekleşir.

Gönderilen mesajlara geri basınç uygulamak ergonomik değildir

Gönderilen iletilere geri basınç uygulamak mümkündür ancak bu işlem, verimsiz ve ergonomik olmayan WebSocket.bufferedAmount özelliğinin yoklanmasını gerektirir. Bu salt okunur özellik, WebSocket.send() çağrıları kullanılarak sıraya alınmış ancak henüz ağa iletilmemiş veri baytı sayısını döndürür. Bu değer, sıraya alınan tüm veriler gönderildikten sonra sıfıra sıfırlanır. Ancak WebSocket.send()'yı çağırmaya devam ederseniz değer artmaya devam eder.

WebSocketStream API nedir?

WebSocketStream API, akışları WebSocket API ile entegre ederek var olmayan veya ergonomik olmayan geri basınç sorununu ele alır. Bu, geri basıncın ek bir maliyet olmadan "ücretsiz" olarak uygulanabileceği anlamına gelir.

WebSocketStream API için önerilen kullanım alanları

Bu API'yi kullanabilen sitelere örnek olarak şunlar verilebilir:

  • Etkileşimi koruması gereken yüksek bant genişliğine sahip WebSocket uygulamaları (özellikle video ve ekran paylaşımı).
  • Benzer şekilde, tarayıcıda çok fazla veri oluşturan ve sunucuya yüklenmesi gereken video çekimi ve diğer uygulamalar. Geri basınç sayesinde istemci, verileri bellekte biriktirmek yerine üretmeyi durdurabilir.

Mevcut durum

Step Durum
1. Açıklayıcı oluşturma Tamamlandı
2. Spesifikasyonun ilk taslağını oluşturma Devam ediyor
3. Geri bildirim toplama ve tasarım üzerinde yineleme yapma Devam ediyor
4. Kaynak denemesi Tamamlandı
5. Başlat Başlatılmadı

WebSocketStream API'yi kullanma

WebSocketStream API, söz tabanlıdır. Bu nedenle, modern JavaScript dünyasında bu API ile çalışmak doğal bir deneyim sunar. Öncelikle yeni bir WebSocketStream oluşturup WebSocket sunucusunun URL'sini ileterek işe başlarsınız. Ardından, bağlantının opened olmasını beklersiniz. Bu işlem sonucunda ReadableStream ve/veya WritableStream elde edilir.

ReadableStream.getReader() yöntemini çağırarak sonunda bir ReadableStreamDefaultReader elde edersiniz. Bu nesneden, akış tamamlanana kadar (yani {value: undefined, done: true} biçiminde bir nesne döndürene kadar) read() verilerini alabilirsiniz.

Buna göre, WritableStream.getWriter() yöntemini çağırarak sonunda WritableStreamDefaultWriter elde edersiniz. Ardından, write() verilerini kullanabilirsiniz.

  const wss = new WebSocketStream(WSS_URL);
  const {readable, writable} = await wss.opened;
  const reader = readable.getReader();
  const writer = writable.getWriter();

  while (true) {
    const {value, done} = await reader.read();
    if (done) {
      break;
    }
    const result = await process(value);
    await writer.write(result);
  }

Geri basınç

Vaat edilen geri basınç özelliği ne zaman kullanıma sunulacak? Bu özelliği "ücretsiz" olarak kullanabilirsiniz. Başka bir işlem yapmanız gerekmez. process() ek süre alırsa bir sonraki mesaj yalnızca kanal hazır olduğunda tüketilir. Benzer şekilde, WritableStreamDefaultWriter.write() adımı yalnızca güvenliyse devam eder.

Gelişmiş örnekler

WebSocketStream'in ikinci bağımsız değişkeni, gelecekteki genişletmeye olanak tanıyan bir seçenek paketidir. Tek seçenek protocols'dır. Bu seçenek, WebSocket oluşturucusunun ikinci bağımsız değişkeni ile aynı şekilde çalışır:

const chatWSS = new WebSocketStream(CHAT_URL, {protocols: ['chat', 'chatv2']});
const {protocol} = await chatWSS.opened;

Seçilen protocol ve olası extensions, WebSocketStream.opened sözüyle kullanılabilen sözlüğün bir parçasıdır. Bağlantı başarısız olursa bu durum önemli olmadığından canlı bağlantıyla ilgili tüm bilgiler bu söz tarafından sağlanır.

const {readable, writable, protocol, extensions} = await chatWSS.opened;

Kapatılan WebSocketStream bağlantısı hakkında bilgi

WebSocket API'deki WebSocket.onclose ve WebSocket.onerror etkinliklerinden elde edilen bilgiler artık WebSocketStream.closed sözü aracılığıyla kullanılabilir. Temiz olmayan bir kapatma durumunda söz reddedilir, aksi takdirde sunucu tarafından gönderilen kod ve nedene göre çözümlenir.

Olası tüm durum kodları ve anlamları, CloseEvent durum kodları listesinde açıklanmıştır.

const {code, reason} = await chatWSS.closed;

WebSocketStream bağlantısını kapatma

WebSocketStream, AbortController ile kapatılabilir. Bu nedenle, AbortSignal öğesini WebSocketStream oluşturucusuna iletin. AbortController.abort() yalnızca el sıkışmadan önce çalışır, sonrasında çalışmaz.

const controller = new AbortController();
const wss = new WebSocketStream(URL, {signal: controller.signal});
setTimeout(() => controller.abort(), 1000);

Alternatif olarak WebSocketStream.close() yöntemini de kullanabilirsiniz ancak bu yöntemin asıl amacı, sunucuya gönderilen kodu ve nedeni belirtmeye izin vermektir.

wss.close({closeCode: 4000, reason: 'Game over'});

Progresif geliştirme ve birlikte çalışabilirlik

Chrome şu anda WebSocketStream API'yi uygulayan tek tarayıcıdır. Klasik WebSocket API ile birlikte çalışabilirlik için, alınan mesajlara geri basınç uygulamak mümkün değildir. Gönderilen iletilere geri basınç uygulamak mümkündür ancak bu işlem, verimsiz ve ergonomik olmayan WebSocket.bufferedAmount özelliğinin yoklanmasını gerektirir.

Özellik algılama

WebSocketStream API'nin desteklenip desteklenmediğini kontrol etmek için şunu kullanın:

if ('WebSocketStream' in window) {
  // `WebSocketStream` is supported!
}

Demo

Desteklenen tarayıcılarda, yerleştirilmiş iFrame'de veya doğrudan Glitch'te WebSocketStream API'nin nasıl çalıştığını görebilirsiniz.

Geri bildirim

Chrome Ekibi, WebSocketStream API ile ilgili deneyimlerinizi öğrenmek istiyor.

API tasarımı hakkında bilgi verin.

API'nin beklentilerinizi karşılamayan bir özelliği var mı? Yoksa fikrinizi uygulamak için eksik yöntemler veya özellikler mi var? Güvenlik modeliyle ilgili sorunuz veya yorumunuz mu var? İlgili GitHub deposunda bir spesifikasyon sorunu bildirin veya düşüncelerinizi mevcut bir soruna ekleyin.

Uygulamayla ilgili sorun bildirme

Chrome'un uygulamasında bir hata mı buldunuz? Yoksa uygulama, spesifikasyondan farklı mı? new.crbug.com adresinden hata bildirin. Mümkün olduğunca fazla ayrıntı eklediğinizden, hatayı yeniden oluşturmak için basit talimatlar verdiğinizden ve Bileşenler kutusuna Blink>Network>WebSockets girdiğinizden emin olun.

API'ye desteğinizi gösterme

WebSocketStream API'yi kullanmayı planlıyor musunuz? Herkese açık desteğiniz, Chrome ekibinin özelliklere öncelik vermesine yardımcı olur ve diğer tarayıcı satıcılarına bu özellikleri desteklemenin ne kadar önemli olduğunu gösterir.

#WebSocketStream hashtag'ini kullanarak @ChromiumDev'e tweet gönderin ve nerede, nasıl kullandığınızı bize bildirin.

Faydalı bağlantılar

Teşekkür

WebSocketStream API, Adam Rice ve Yutaka Hirano tarafından uygulanmıştır.