HTTP/2 Üzerinden Tek Yönlü Veri Akışı İçin WebSockets Yerine SSE Kullanımı

Yayınlanan: 2022-03-10
Kısa özet ↬ Gerçek zamanlı verileri kullanan bir web uygulaması tasarladığımızda, verilerimizi sunucudan istemciye nasıl ileteceğimizi düşünmemiz gerekir. Varsayılan yanıt genellikle "WebSockets" şeklindedir. Ama daha iyi bir yol var mı? Üç farklı yöntemi karşılaştıralım: Uzun yoklama, WebSockets ve Sunucudan Gönderilen Olaylar; gerçek dünyadaki sınırlamalarını anlamak için. Cevap sizi şaşırtabilir.

Bir web uygulaması oluştururken, ne tür bir dağıtım mekanizması kullanacakları göz önünde bulundurulmalıdır. Diyelim ki gerçek zamanlı verilerle çalışan bir çapraz platform uygulamamız var; gerçek zamanlı olarak hisse senedi alıp satma olanağı sağlayan bir borsa uygulaması. Bu uygulama, farklı kullanıcılara farklı değerler getiren widget'lardan oluşmaktadır.

Sunucudan istemciye veri teslimi söz konusu olduğunda, iki genel yaklaşımla sınırlıyız: client pull veya server push . Herhangi bir web uygulamasına basit bir örnek olarak, istemci web tarayıcısıdır. Tarayıcınızdaki web sitesi sunucudan veri istediğinde buna client pull denir. Tersine, sunucu proaktif olarak web sitenize güncellemeler gönderiyorsa buna server push denir.

Günümüzde, bunları uygulamanın birkaç yolu vardır:

  • Uzun/kısa yoklama (istemci çekme)
  • WebSockets (sunucu push)
  • Sunucu Tarafından Gönderilen olaylar (sunucu gönderimi).

İş senaryomuz için gereksinimleri belirledikten sonra üç alternatife derinlemesine bakacağız.

İş Örneği

Borsa uygulamamız için yeni widget'ları hızlı bir şekilde sunabilmek ve tüm platformu yeniden konuşlandırmadan tak ve çalıştır bunları kullanabilmek için, bunların bağımsız olmalarına ve kendi veri G/Ç'lerini yönetmelerine ihtiyacımız var. Widget'lar hiçbir şekilde birbiriyle bağlantılı değildir. İdeal durumda, hepsi bir API uç noktasına abone olacak ve ondan veri almaya başlayacak. Yeni özelliklerin pazara daha hızlı sunulmasının yanı sıra, bu yaklaşım bize içeriği üçüncü taraf web sitelerine aktarma yeteneği verirken, widget'larımız ihtiyaç duydukları her şeyi kendi başlarına getiriyor.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Buradaki ana tuzak, sahip olduğumuz widget sayısı ile bağlantı sayısının doğrusal olarak artması ve bir kerede işlenen HTTP isteklerinin sayısı için tarayıcıların sınırına ulaşacağımızdır.

Widget'larımızın alacağı veriler esas olarak rakamlardan ve sayılarına yapılan güncellemelerden oluşur: İlk yanıt, onlar için bazı piyasa değerlerine sahip on hisse senedi içerir. Bu, hisse senedi ekleme/çıkarma güncellemelerini ve halihazırda sunulanların piyasa değerlerinin güncellemelerini içerir. Her güncelleme için küçük miktarlarda JSON dizelerini olabildiğince hızlı aktarıyoruz.

HTTP/2, aynı etki alanından gelen isteklerin çoğullanmasını sağlar, yani birden çok yanıt için yalnızca bir bağlantı alabiliriz. Bu, sorunumuzu çözebilir gibi görünüyor. Verileri almak için farklı seçenekleri keşfederek başlıyoruz ve onlardan ne elde edebileceğimizi görüyoruz.

  • Tüm uç noktalarımızı aynı etki alanının arkasına gizlemek için yük dengeleme ve proxy için NGINX kullanacağız. Bu, kutudan çıktığı gibi HTTP/2 çoğullamayı kullanmamızı sağlayacaktır.
  • Mobil cihazların ağını ve pilini verimli kullanmak istiyoruz.

Alternatifler

uzun yoklama

Uzun oylama

İstemci çekme, arabanızın arka koltuğunda oturan sinir bozucu çocuğun sürekli “Daha gelmedik mi?” Diye sormasının yazılım uygulaması eşdeğeridir. Kısacası, istemci sunucudan veri ister. Sunucuda veri yok ve yanıtı göndermeden önce bir süre bekler:

  • Bekleme sırasında bir şey çıkarsa, sunucu bunu gönderir ve isteği kapatır;
  • Gönderilecek bir şey yoksa ve maksimum bekleme süresine ulaşılırsa, sunucu veri yok şeklinde bir yanıt gönderir;
  • Her iki durumda da istemci bir sonraki veri talebini açar;
  • Köpürtün, durulayın, tekrarlayın.

AJAX çağrıları HTTP protokolü üzerinde çalışır, yani aynı etki alanına yapılan istekler varsayılan olarak çoğullanmalıdır. Ancak, bunun gerektiği gibi çalışmasını sağlamaya çalışırken birden fazla sorunla karşılaştık. Widget yaklaşımımızla belirlediğimiz bazı tuzaklar:

  • Başlıklar
    Her anket isteği ve yanıtı eksiksiz bir HTTP mesajıdır ve mesaj çerçevesinde tam bir HTTP başlığı seti içerir. Küçük sık mesajlarımızın olduğu durumda, başlıklar aslında iletilen verilerin daha büyük yüzdesini temsil eder. Gerçek faydalı yük, iletilen toplam bayttan çok daha azdır (örneğin, 5 KB veri için 15 KB başlık).

  • Maksimum Gecikme
    Sunucu yanıt verdikten sonra, istemci bir sonraki isteği gönderene kadar istemciye veri gönderemez. Uzun yoklama için ortalama gecikme süresi bir ağ geçişine yakınken, maksimum gecikme süresi üç ağ geçişinden fazladır: yanıt, istek, yanıt. Bununla birlikte, paket kaybı ve yeniden iletim nedeniyle, herhangi bir TCP/IP protokolü için maksimum gecikme süresi üçten fazla ağ geçişi olacaktır (HTTP boru hattı ile önlenebilir). Doğrudan LAN bağlantısındayken bu büyük bir sorun değil, hareket halindeyken ve ağ hücrelerini değiştirirken sorun oluyor. Belli bir dereceye kadar, bu SSE ve WebSockets ile gözlemlenir, ancak etki en çok yoklama ile olur.

  • Bağlantı Kuruluşu
    Birçok yoklama isteği için yeniden kullanılabilir kalıcı HTTP bağlantısı kullanılarak bundan kaçınılabilse de, bağlantıyı canlı tutmak için tüm bileşenlerinizin kısa süreli sürelerde yoklama yapmasını buna göre ayarlamak zor olabilir. Sonunda, sunucu yanıtlarına bağlı olarak, anketlerinizin senkronizasyonu bozulacaktır.

  • Performans Düşüşü
    Yük altında olan uzun bir yoklama istemcisi (veya sunucusu), mesaj gecikmesi pahasına performansta düşmeye doğal bir eğilim gösterir. Bu olduğunda, istemciye gönderilen olaylar sıraya girer. Bu gerçekten uygulamaya bağlıdır; bizim durumumuzda, widget'larımıza ekleme/kaldırma/güncelleme etkinlikleri gönderirken tüm verileri toplamamız gerekiyor.

  • zaman aşımları
    Uzun yoklama isteklerinin, sunucunun istemciye gönderecek bir şeyi olana kadar beklemede kalması gerekir. Bu, çok uzun süre boşta kalırsa bağlantının proxy sunucusu tarafından kapanmasına neden olabilir.

  • çoğullama
    Bu, yanıtlar aynı anda kalıcı bir HTTP/2 bağlantısı üzerinden gerçekleşirse gerçekleşebilir. Yoklama yanıtları gerçekten senkronize olamayacağından bunu yapmak zor olabilir.

Uzun oylamalarda yaşanabilecek gerçek dünya sorunları hakkında daha fazla bilgiyi burada bulabilirsiniz .

WebSockets

WebSockets

Sunucu gönderme yönteminin ilk örneği olarak WebSockets'e bakacağız.

MDN aracılığıyla:

WebSockets, kullanıcının tarayıcısı ile bir sunucu arasında etkileşimli bir iletişim oturumu açmayı mümkün kılan gelişmiş bir teknolojidir. Bu API ile, bir sunucuya mesaj gönderebilir ve bir yanıt için sunucuyu yoklamak zorunda kalmadan olaya dayalı yanıtlar alabilirsiniz.

Bu, tek bir TCP bağlantısı üzerinden tam çift yönlü iletişim kanalları sağlayan bir iletişim protokolüdür.

Hem HTTP hem de WebSockets, OSI modelindeki uygulama katmanında bulunur ve bu nedenle katman 4'teki TCP'ye bağlıdır.

  1. Başvuru
  2. Sunum
  3. Oturum, toplantı, celse
  4. Ulaşım
  5. Veri bağlantısı
  6. Fiziksel

RFC 6455, WebSocket'in "80 ve 443 numaralı HTTP bağlantı noktaları üzerinde çalışacak ve HTTP proxy'lerini ve aracılarını destekleyecek şekilde tasarlandığını" ve böylece HTTP protokolüyle uyumlu hale geldiğini belirtir. Uyumluluğu sağlamak için WebSocket anlaşması, HTTP protokolünden WebSocket protokolüne geçmek için HTTP Yükseltme başlığını kullanır.

Ayrıca Wikipedia'da WebSockets hakkında bilmeniz gereken her şeyi açıklayan çok iyi bir makale var. Okumanızı tavsiye ederim.

Soketlerin bizim için gerçekten işe yarayabileceğini belirledikten sonra, iş durumumuzda yeteneklerini keşfetmeye başladık ve duvardan duvara duvara çarptık.

  • Proxy sunucuları : Genel olarak, WebSockets ve proxy'lerle ilgili birkaç farklı sorun vardır:

    • Birincisi, İnternet servis sağlayıcıları ve ağlarını yönetme biçimleriyle ilgilidir. Yarıçap proxy'leriyle ilgili sorunlar, engellenen bağlantı noktaları vb.
    • İkinci tür sorunlar, proxy'nin güvenli olmayan HTTP trafiğini ve uzun ömürlü bağlantıları işlemek için yapılandırılma biçimiyle ilgilidir (HTTPS ile etki azaltılır).
    • Üçüncü konu “WebSockets ile HTTP proxy'lerinin aksine TCP proxy'lerini çalıştırmanız gerekiyor. TCP proxy'leri, üstbilgileri enjekte edemez, URL'leri yeniden yazamaz veya geleneksel olarak HTTP proxy'lerimizin halletmesine izin verdiğimiz birçok rolü gerçekleştiremez."
  • Bağlantı sayısı : HTTP istekleri için 6 sayısı etrafında dönen ünlü bağlantı sınırı WebSockets için geçerli değildir. 50 soket = 50 bağlantı. 50 soketli on tarayıcı sekmesi = 500 bağlantı vb. WebSocket, veri iletmek için farklı bir protokol olduğundan, HTTP/2 bağlantıları üzerinden otomatik olarak çoğullanmaz (gerçekten HTTP'nin üzerinde çalışmaz). Hem sunucuda hem de istemcide özel çoğullama uygulamak, belirtilen iş senaryosunda soketleri kullanışlı hale getirmek için çok karmaşıktır. Ayrıca, abone olmak için istemcide bir tür API'ye ihtiyaç duyacakları ve bunları onsuz dağıtamayacağımız için bu, widget'larımızı platformumuzla birleştirir.

  • Yük dengeleme (çoklama olmadan) : Her bir kullanıcı n sayıda soket açarsa, uygun yük dengeleme çok karmaşıktır. Sunucularınız aşırı yüklendiğinde ve yazılımınızın uygulanmasına bağlı olarak yeni örnekler oluşturmanız ve eskilerini sonlandırmanız gerektiğinde, “yeniden bağlanma” sırasında gerçekleştirilen eylemler, sisteminizi aşırı yükleyecek büyük bir yenileme zincirini ve yeni veri isteklerini tetikleyebilir. . WebSockets'in hem sunucuda hem de istemcide bakımının yapılması gerekir. Mevcut sunucuda yüksek yük varsa, soket bağlantılarını farklı bir sunucuya taşımak mümkün değildir. Kapatılıp tekrar açılmaları gerekir.

  • DoS : Bu genellikle WebSockets için gerekli olan TCP proxy'leri tarafından işlenemeyen ön uç HTTP proxy'leri tarafından gerçekleştirilir. Sokete bağlanabilir ve sunucularınızı veri ile doldurmaya başlar. WebSockets sizi bu tür saldırılara karşı savunmasız bırakır.

  • Tekerleği yeniden icat etmek : WebSockets ile, HTTP'de halledilen birçok sorunu kendi başına halletmeniz gerekir.

WebSockets ile ilgili gerçek dünya sorunları hakkında daha fazla bilgiyi buradan okuyabilirsiniz.

WebSockets için bazı iyi kullanım örnekleri, faydaların uygulama sorunlarından daha ağır bastığı sohbetler ve çok oyunculu oyunlardır. Başlıca faydaları çift yönlü iletişim olduğu ve bizim buna gerçekten ihtiyacımız olmadığı için devam etmemiz gerekiyor.

Etki

Geliştirme, test etme ve ölçeklendirme açısından artan operasyonel ek yük elde ediyoruz; hem yazılım hem de BT altyapısı: yoklama ve WebSockets.

Aynı sorunu mobil cihazlar ve ağlar üzerinden her ikisiyle de alıyoruz. Bu cihazların donanım tasarımı, anteni ve hücresel ağa olan bağlantıyı canlı tutarak açık bir bağlantı sağlar. Bu, pil ömrünün, ısının ve bazı durumlarda veri için ekstra ücretin azalmasına neden olur.

Peki Neden Hala Mobil Cihazlarda Sorun Yaşıyoruz?

Varsayılan mobil cihazın İnternet'e nasıl bağlandığını düşünelim:

Mobil cihazların internete bağlanabilmeleri için birkaç döngüden geçmeleri gerekir.

Mobil ağın nasıl çalıştığına dair basit bir açıklama: Tipik olarak mobil cihazların, bir hücreden veri alabilen düşük güçlü bir anteni vardır. Bu şekilde, cihaz gelen bir aramadan veri aldığında, aramayı kurmak için tam çift yönlü anteni başlatır. Arama yapmak veya İnternet'e erişmek istediğinizde (WiFi yoksa) aynı anten kullanılır. Tam çift yönlü antenin hücresel ağa bağlantı kurması ve bazı kimlik doğrulaması yapması gerekir. Bağlantı kurulduktan sonra, şebeke talebimizi gerçekleştirmek için cihazınız ile hücre arasında bir miktar iletişim kurulur. İnternet isteklerini işleyen mobil servis sağlayıcının dahili proxy'sine yönlendiriliyoruz. O andan itibaren, prosedür zaten bilinmektedir: www.domainname.ext gerçekte nerede olduğunu bir DNS sorar, URI'yi kaynağa alır ve sonunda ona yönlendirilir.

Bu işlem, tahmin edebileceğiniz gibi, oldukça fazla pil gücü çekiyor. Cep telefonu satıcılarının birkaç günlük bekleme süresi ve sadece birkaç saat içinde konuşma süresi vermesinin nedeni budur.

WiFi olmadan, hem WebSockets hem de yoklama, tam çift yönlü antenin neredeyse sürekli çalışmasını gerektirir. Bu nedenle, artan veri tüketimi ve artan güç tüketimi - ve cihaza bağlı olarak - ısı ile de karşı karşıyayız.

İşler kasvetli göründüğünde, uygulamamız için iş gereksinimlerini yeniden gözden geçirmemiz gerekecek gibi görünüyor. Bir şey mi kaçırıyoruz?

SSE

Sunucu Tarafından Gönderilen Olaylar

MDN aracılığıyla:

“EventSource arayüzü, Sunucu Tarafından Gönderilen Olayları almak için kullanılır. HTTP üzerinden bir sunucuya bağlanır ve bağlantıyı kapatmadan olayları metin/olay akışı biçiminde alır.

Yoklamanın temel farkı, yalnızca bir bağlantı elde etmemiz ve bunun üzerinden bir olay akışını sürdürmemizdir. Uzun yoklama, her çekiş için yeni bir bağlantı oluşturur - bu nedenle, başlıklar ve orada karşılaştığımız diğer sorunlar gibi.

html5doctor.com aracılığıyla:

Sunucu Tarafından Gönderilen Olaylar, sunucu tarafından yayılan ve tarayıcı tarafından alınan gerçek zamanlı olaylardır. Gerçek zamanlı olmaları bakımından WebSockets'e benzerler, ancak sunucudan çok tek yönlü bir iletişim yöntemidir.

Biraz garip görünüyor, ancak düşündükten sonra - ana veri akışımız sunucudan istemciye ve çok daha az durumda istemciden sunucuya.

Görünüşe göre bunu, veri iletmeyle ilgili ana iş durumumuz için kullanabiliriz. Protokol tek yönlü olduğundan ve istemci sunucuya bunun üzerinden mesaj gönderemediğinden, müşteri satın almalarını yeni bir istek göndererek çözebiliriz. Bu, sonunda tam çift yönlü antenin mobil cihazlarda açılması için zaman gecikmesine sahip olacak. Ancak, bunun zaman zaman gerçekleşmesiyle yaşayabiliriz - sonuçta bu gecikme milisaniyelerle ölçülür.

Benzersiz özellikler

  • Bağlantı akışı sunucudan geliyor ve salt okunur.
  • Kalıcı bağlantı için özel bir protokol değil, normal HTTP istekleri kullanırlar. Kutudan HTTP/2 üzerinden çoğullama alınıyor.
  • Bağlantı kesilirse EventSource bir hata olayı başlatır ve otomatik olarak yeniden bağlanmayı dener. Sunucu, istemci yeniden bağlanmaya çalışmadan önce zaman aşımını da kontrol edebilir (daha sonra ayrıntılı olarak açıklanacaktır).
  • İstemciler, mesajlarla benzersiz bir kimlik gönderebilir. Bir istemci, bağlantı kesildikten sonra yeniden bağlanmaya çalıştığında, bilinen son kimliği gönderir. Ardından sunucu, istemcinin n sayıda mesajı kaçırdığını görebilir ve yeniden bağlanma sırasında cevapsız mesajların biriktirme listesini gönderebilir.

Örnek İstemci Uygulaması

Bu olaylar, olayın adını ve onunla ilişkili verileri kontrol edebilmemiz dışında, tarayıcıda gerçekleşen tıklama olayları gibi sıradan JavaScript olaylarına benzer.

İstemci tarafı için basit kod önizlemesini görelim:

 // subscribe for messages var source = new EventSource('URL'); // handle messages source.onmessage = function(event) { // Do something with the data: event.data; };

Örnekten gördüğümüz şey, istemci tarafının oldukça basit olmasıdır. Kaynağımıza bağlanır ve mesaj almayı bekler.

Sunucuların verileri HTTP üzerinden web sayfalarına iletmesini veya tahsis edilmiş sunucu-itme protokollerini kullanmasını sağlamak için spesifikasyon, istemcide 'EventSource' arayüzünü sunar. Bu API'yi kullanmak, bir "EventSource" nesnesi oluşturmaktan ve bir olay dinleyicisini kaydetmekten oluşur.

WebSockets için istemci uygulaması buna çok benziyor. Soketlerle ilgili karmaşıklık, BT altyapısında ve sunucu uygulamasında yatmaktadır.

OlayKaynağı

Her EventSource nesnesi aşağıdaki üyelere sahiptir:

  • URL: inşaat sırasında ayarlanır.
  • İstek: başlangıçta boş.
  • Yeniden bağlantı süresi: ms cinsinden değer (kullanıcı aracısı tanımlı değer).
  • Son olay kimliği: başlangıçta boş bir dize.
  • Hazır durumu: bağlantının durumu.
    • BAĞLANIYOR (0)
    • AÇIK (1)
    • KAPALI (2)

URL dışında, tümü özel olarak kabul edilir ve dışarıdan erişilemez.

Yerleşik olaylar:

  1. Açık
  2. İleti
  3. Hata

Bağlantı Düşüşlerini İşleme

Bağlantı kesilirse tarayıcı tarafından otomatik olarak yeniden kurulur. Sunucu, bağlantıyı kalıcı olarak yeniden denemek veya kapatmak için zaman aşımı gönderebilir. Böyle bir durumda, tarayıcı zaman aşımından sonra tekrar bağlanmaya çalışmak veya bağlantı sonlandır mesajı alırsa hiç denememekle uyumlu olacaktır. Oldukça basit görünüyor - ve aslında öyle.

Örnek Sunucu Uygulaması

Peki, istemci bu kadar basitse, sunucu uygulaması karmaşık olabilir mi?

SSE için sunucu işleyicisi şöyle görünebilir:

 function handler(response) { // setup headers for the response in order to get the persistent HTTP connection response.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); // compose the message response.write('id: UniqueID\n'); response.write("data: " + data + '\n\n'); // whenever you send two new line characters the message is sent automatically }

Yanıtı işleyecek bir işlev tanımlıyoruz:

  1. Kurulum başlıkları
  2. Mesaj oluşturmak
  3. Göndermek

Bir send() veya push() yöntemi çağrısı görmediğinizi unutmayın. Bunun nedeni, standardın, örnekte olduğu gibi mesajın iki \n\n karakter alır almaz gönderileceğini tanımlamasıdır: response.write("data: " + data + '\n\n'); . Bu, mesajı hemen müşteriye iletecektir. Lütfen data çıkış karakterli bir dize olması gerektiğini ve sonunda yeni satır karakterleri içermediğini unutmayın.

Mesaj Yapısı

Daha önce belirtildiği gibi, mesaj birkaç özellik içerebilir:

  1. İD
    Alan değeri U+0000 NULL içermiyorsa, son olay kimliği arabelleğini alan değerine ayarlayın. Aksi takdirde, alanı görmezden gelin.
  2. Veri
    Alan değerini veri arabelleğine ekleyin, ardından veri arabelleğine tek bir U+000A SATIR BESLEME (LF) karakteri ekleyin.
  3. Etkinlik
    Olay türü arabelleğini alan değerine ayarlayın. Bu, event.type özel olay adınızı almasına yol açar.
  4. yeniden dene
    Alan değeri yalnızca ASCII basamaklarından oluşuyorsa, alan değerini on tabanında bir tam sayı olarak yorumlayın ve olay akışının yeniden bağlantı süresini bu tam sayıya ayarlayın. Aksi takdirde, alanı görmezden gelin.

Diğer her şey göz ardı edilecektir. Kendi alanlarımızı tanıtamıyoruz.

Eklenen event ile örnek:

 response.write('id: UniqueID\n'); response.write('event: add\n'); response.write('retry: 10000\n'); response.write("data: " + data + '\n\n');

Daha sonra istemcide bu, addEventListener ile şu şekilde işlenir:

 source.addEventListener("add", function(event) { // do stuff with data event.data; });

Farklı kimlikler sağladığınız sürece, yeni bir satırla ayrılmış birden fazla mesaj gönderebilirsiniz.

 ... id: 54 event: add data: "[{SOME JSON DATA}]" id: 55 event: remove data: JSON.stringify(some_data) id: 56 event: remove data: { data: "msg" : "JSON data"\n data: "field": "value"\n data: "field2": "value2"\n data: }\n\n ...

Bu, verilerimizle yapabileceklerimizi büyük ölçüde basitleştirir.

Özel Sunucu Gereksinimleri

Arka uç için POC'miz sırasında, çalışan bir SSE uygulamasına sahip olmak için ele alınması gereken bazı özellikleri olduğunu belirledik. NodeJS, Kestrel veya Twisted gibi olay döngüsü tabanlı sunucuyu kullanacağınız en iyi durum senaryosu. Fikir, iş parçacığı tabanlı çözümle bağlantı başına bir iş parçacığına sahip olacağınızdır → 1000 bağlantı = 1000 iş parçacığı. Olay döngüsü çözümü ile 1000 bağlantı için bir iş parçacığına sahip olacaksınız.

  1. EventSource isteklerini yalnızca HTTP isteği, olay akışı MIME türünü kabul edebileceğini söylüyorsa kabul edebilirsiniz;
  2. Yeni olaylar yaymak için tüm bağlı kullanıcıların bir listesini tutmanız gerekir;
  3. Bırakılan bağlantıları dinlemeli ve bağlı kullanıcılar listesinden çıkarmalısınız;
  4. İsteğe bağlı olarak, yeniden bağlanan istemcilerin kaçırılan mesajları yakalayabilmesi için bir mesaj geçmişi tutmalısınız.

Beklendiği gibi çalışıyor ve ilk başta sihir gibi görünüyor. Uygulamamızın verimli bir şekilde çalışması için istediğimiz her şeyi elde ediyoruz. Gerçek olamayacak kadar iyi görünen her şeyde olduğu gibi, bazen ele alınması gereken bazı sorunlarla karşılaşıyoruz. Ancak, uygulanması veya dolaşması karmaşık değildir:

  • Eski proxy sunucularının, belirli durumlarda kısa bir zaman aşımından sonra HTTP bağlantılarını bıraktığı bilinmektedir. Bu tür proxy sunucularına karşı koruma sağlamak için, yazarlar her 15 saniyede bir (bir ':' karakteriyle başlayan) bir yorum satırı ekleyebilir.

  • Olay kaynağı bağlantılarını birbirleriyle veya önceden sunulan belirli belgelerle ilişkilendirmek isteyen yazarlar, bireysel istemcilerin birden çok IP adresine (birden çok proxy sunucusuna sahip olmaları nedeniyle) ve ayrı IP adreslerine sahip olabileceğinden, IP adreslerine güvenmenin işe yaramadığını görebilir. birden fazla istemci (bir proxy sunucusunun paylaşılması nedeniyle). Belge sunulduğunda belgeye benzersiz bir tanımlayıcı eklemek ve ardından bağlantı kurulduğunda bu tanımlayıcıyı URL'nin bir parçası olarak iletmek daha iyidir.

  • Yazarlar ayrıca, özellikle yığınlamanın zamanlama gereksinimlerinden habersiz farklı bir katman tarafından yapılması durumunda, HTTP yığınlamanın bu protokolün güvenilirliği üzerinde beklenmedik olumsuz etkileri olabileceği konusunda uyarılır. Bu bir sorunsa, olay akışlarını sunmak için parçalama devre dışı bırakılabilir.

  • HTTP'nin sunucu başına bağlantı sınırlamasını destekleyen istemciler, her sayfada aynı etki alanına bir EventSource varsa, bir siteden birden çok sayfa açarken sorun yaşayabilir. Yazarlar, bağlantı başına benzersiz etki alanı adları kullanmanın nispeten karmaşık mekanizmasını kullanarak veya kullanıcının EventSource işlevini sayfa bazında etkinleştirmesine veya devre dışı bırakmasına izin vererek veya paylaşılan bir çalışan kullanarak tek bir EventSource nesnesini paylaşarak bundan kaçınabilir.

  • Tarayıcı desteği ve Çoklu Dolgular: Edge bu uygulamanın gerisinde kalıyor, ancak sizi kurtarabilecek bir çoklu dolgu mevcut. Ancak, SSE için en önemli durum, IE/Edge'in geçerli bir pazar payına sahip olmadığı mobil cihazlar için yapılmıştır.

Mevcut çoklu dolgulardan bazıları:

  • yaffle
  • amvtek
  • ilaç

Bağlantısız İtme ve Diğer Özellikler

Kontrollü ortamlarda çalışan kullanıcı aracıları, örneğin belirli taşıyıcılara bağlı mobil telefonlardaki tarayıcılar, bağlantının yönetimini ağdaki bir proxy'ye devredebilir. Böyle bir durumda, uygunluk amacıyla kullanıcı aracısının hem el cihazı yazılımını hem de ağ proxy'sini içerdiği kabul edilir.

Örneğin, bir mobil cihazdaki bir tarayıcı, bağlantı kurduktan sonra, cihazın destekleyici bir ağda olduğunu algılayabilir ve ağdaki bir proxy sunucusunun bağlantının yönetimini üstlenmesini isteyebilir. Böyle bir durum için zaman çizelgesi aşağıdaki gibi olabilir:

  1. Tarayıcı uzak bir HTTP sunucusuna bağlanır ve yazar tarafından EventSource yapıcısında belirtilen kaynağı ister.
  2. Sunucu ara sıra mesajlar gönderir.
  3. Tarayıcı, iki mesaj arasında, TCP bağlantısını canlı tutmakla ilgili ağ etkinliği dışında boşta olduğunu algılar ve güç tasarrufu için uyku moduna geçmeye karar verir.
  4. Tarayıcı sunucuyla bağlantısını keser.
  5. Tarayıcı, ağdaki bir hizmetle bağlantı kurar ve bunun yerine hizmetin, bir "push proxy" bağlantısını sürdürmesini ister.
  6. "Push proxy" hizmeti, uzak HTTP sunucusuyla bağlantı kurar ve yazar tarafından EventSource yapıcısında belirtilen kaynağı ister (muhtemelen bir Last-Event-ID HTTP üstbilgisi vb. dahil).
  7. Tarayıcı, mobil cihazın uyku moduna geçmesine izin verir.
  8. Sunucu başka bir mesaj gönderir.
  9. "Push proxy" hizmeti, olayı mobil cihaza iletmek için OMA push gibi bir teknoloji kullanır; bu teknoloji yalnızca olayı işlemeye yetecek kadar uyanır ve ardından uyku moduna geri döner.

Bu, toplam veri kullanımını azaltabilir ve dolayısıyla önemli ölçüde güç tasarrufu sağlayabilir.

Spesifikasyon tarafından tanımlandığı gibi ve daha dağıtılmış yollarla (yukarıda açıklandığı gibi) mevcut API ve metin/olay akışı kablo formatının uygulanmasının yanı sıra, diğer uygulanabilir spesifikasyonlar tarafından tanımlanan olay çerçeveleme formatları desteklenebilir.

Özet

Sunucu ve istemci uygulamalarını içeren uzun ve kapsamlı POC'lerden sonra, veri teslimi ile ilgili sorunlarımızın cevabı SSE gibi görünüyor. Bununla ilgili bazı tuzaklar da var, ancak düzeltilmesi önemsiz olduğunu kanıtladı.

Üretim kurulumumuz sonunda böyle görünüyor:

Mimariye genel bakış
Son mimariye genel bakış. Tüm API uç noktaları nginx'in arkasındadır, böylece istemciler çoklanmış yanıt alır.

NGINX'ten şunları alıyoruz:

  • Farklı yerlerdeki API uç noktalarına proxy;
  • HTTP/2 ve bağlantılar için çoğullama gibi tüm faydaları;
  • Yük dengeleme;
  • SSL.

Bu şekilde, veri teslimimizi ve sertifikalarımızı her uç noktada ayrı ayrı yapmak yerine tek bir yerde yönetiyoruz.

Bu yaklaşımdan elde ettiğimiz başlıca faydalar şunlardır:

  • Veri verimli;
  • Daha basit uygulama;
  • HTTP/2 üzerinden otomatik olarak çoğullanır;
  • İstemcideki veriler için bağlantı sayısını bir ile sınırlar;
  • Bağlantıyı bir proxy'ye boşaltarak pilden tasarruf etmek için bir mekanizma sağlar.

SSE, yalnızca hızlı güncellemeler sağlamak için diğer yöntemlere uygun bir alternatif değildir; mobil cihazlar için optimizasyonlar söz konusu olduğunda kendi liginde gibi görünüyor. Alternatiflere kıyasla uygulanmasında ek yük yoktur. Sunucu tarafı uygulama açısından, yoklamadan çok farklı değildir. İstemcide, WebSockets'in nasıl yönetildiğine çok benzer şekilde, bir ilk abonelik ve olay işleyicileri atama gerektirdiğinden yoklamadan çok daha basittir.

Basit bir istemci-sunucu uygulaması elde etmek istiyorsanız kod demosunu kontrol edin.

Kaynaklar

  • "İki Yönlü HTTP'de Uzun Yoklama ve Akış Kullanımına İlişkin Bilinen Sorunlar ve En İyi Uygulamalar", IETF (PDF)
  • W3C Önerisi, W3C
  • Allan Denis, InfoQ "WebSocket HTTP/2'den kurtulacak mı?"
  • “Sunucu Tarafından Gönderilen Olaylarla Güncellemeleri Akış”, Eric Bidelman, HTML5 Rocks
  • Darren Cook, O'Reilly Media "HTML5 SSE ile Veri Aktarma Uygulamaları"