Menggunakan SSE Alih-alih WebSockets Untuk Aliran Data Searah Melalui HTTP/2

Diterbitkan: 2022-03-10
Ringkasan cepat Setiap kali kita mendesain aplikasi web menggunakan data waktu nyata, kita perlu mempertimbangkan bagaimana kita akan mengirimkan data kita dari server ke klien. Jawaban default biasanya adalah "WebSockets." Tetapi apakah ada cara yang lebih baik? Mari kita bandingkan tiga metode berbeda: Long polling, WebSockets, dan Server-Sent Events; untuk memahami keterbatasan dunia nyata mereka. Jawabannya mungkin akan mengejutkan Anda.

Saat membangun aplikasi web, seseorang harus mempertimbangkan mekanisme pengiriman seperti apa yang akan mereka gunakan. Katakanlah kita memiliki aplikasi lintas platform yang bekerja dengan data waktu nyata; aplikasi pasar saham yang menyediakan kemampuan untuk membeli atau menjual saham secara real time. Aplikasi ini terdiri dari widget yang memberikan nilai berbeda bagi pengguna yang berbeda.

Ketika datang ke pengiriman data dari server ke klien, kami terbatas pada dua pendekatan umum: client pull atau server push . Sebagai contoh sederhana dengan aplikasi web apa pun, klien adalah browser web. Saat situs web di browser Anda meminta data dari server, ini disebut tarikan klien . Kebalikannya, ketika server secara proaktif mendorong pembaruan ke situs web Anda, itu disebut server push .

Saat ini, ada beberapa cara untuk menerapkannya:

  • Jajak pendapat panjang/pendek (tarik klien)
  • WebSocket (dorongan server)
  • Peristiwa yang Dikirim Server (push server).

Kami akan melihat secara mendalam tiga alternatif setelah kami menetapkan persyaratan untuk kasus bisnis kami.

Kasus Bisnis

Agar dapat mengirimkan widget baru untuk aplikasi pasar saham kami dengan cepat dan memasangnya tanpa memindahkan seluruh platform, kami membutuhkan widget ini untuk mandiri dan mengelola I/O data mereka sendiri. Widget tidak digabungkan satu sama lain dengan cara apa pun. Dalam kasus yang ideal, mereka semua akan berlangganan beberapa titik akhir API dan mulai mendapatkan data darinya. Selain waktu yang lebih cepat untuk memasarkan fitur-fitur baru, pendekatan ini memberi kami kemampuan untuk mengekspor konten ke situs web pihak ketiga sedangkan widget kami membawa semua yang mereka butuhkan sendiri.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Perangkap utama di sini adalah jumlah koneksi akan bertambah secara linier dengan jumlah widget yang kita miliki dan kita akan mencapai batas browser untuk jumlah permintaan HTTP yang ditangani sekaligus.

Data yang akan diterima widget kami sebagian besar terdiri dari angka dan pembaruan nomornya: Respons awal berisi sepuluh saham dengan beberapa nilai pasar untuk mereka. Ini termasuk pembaruan penambahan/penghapusan saham dan pembaruan nilai pasar dari yang disajikan saat ini. Kami mentransfer sejumlah kecil string JSON untuk setiap pembaruan secepat mungkin.

HTTP/2 menyediakan pelipatgandaan permintaan yang berasal dari domain yang sama, artinya kita hanya bisa mendapatkan satu koneksi untuk beberapa respons. Ini terdengar seperti itu bisa memecahkan masalah kita. Kami mulai dengan menjelajahi berbagai opsi untuk mendapatkan data dan melihat apa yang bisa kami dapatkan darinya.

  • Kami akan menggunakan NGINX untuk penyeimbangan beban dan proxy untuk menyembunyikan semua titik akhir kami di belakang domain yang sama. Ini akan memungkinkan kita untuk menggunakan HTTP/2 multiplexing di luar kotak.
  • Kami ingin menggunakan jaringan dan baterai perangkat seluler secara efisien.

Alternatifnya

Polling Panjang

polling panjang

Tarikan klien adalah implementasi perangkat lunak yang setara dengan anak menyebalkan yang duduk di kursi belakang mobil Anda yang terus-menerus bertanya, "Apakah kita sudah sampai?" Singkatnya, klien meminta server untuk data. Server tidak memiliki data dan menunggu beberapa saat sebelum mengirim respons:

  • Jika sesuatu muncul selama menunggu, server mengirimkannya dan menutup permintaan;
  • Jika tidak ada yang dikirim dan waktu tunggu maksimum tercapai, server mengirimkan respons bahwa tidak ada data;
  • Dalam kedua kasus, klien membuka permintaan data berikutnya;
  • Busa, bilas, ulangi.

Panggilan AJAX berfungsi pada protokol HTTP yang berarti permintaan ke domain yang sama harus dimultipleks secara default. Namun, kami mengalami banyak masalah dengan mencoba membuatnya berfungsi sesuai kebutuhan. Beberapa perangkap yang kami identifikasi dengan pendekatan widget kami:

  • Header Overhead
    Setiap permintaan dan tanggapan polling adalah pesan HTTP lengkap dan berisi set lengkap header HTTP dalam pembingkaian pesan. Dalam kasus kami di mana kami memiliki pesan kecil yang sering, header sebenarnya mewakili persentase yang lebih besar dari data yang dikirimkan. Muatan berguna yang sebenarnya jauh lebih sedikit daripada total byte yang ditransmisikan (misalnya, 15 KB header untuk 5 KB data).

  • Latensi Maksimal
    Setelah server merespon, tidak dapat lagi mengirim data ke klien sampai klien mengirimkan permintaan berikutnya. Sementara latensi rata-rata untuk polling panjang mendekati satu transit jaringan, latensi maksimum lebih dari tiga transit jaringan: respons, permintaan, respons. Namun, karena kehilangan paket dan transmisi ulang, latensi maksimum untuk protokol TCP/IP apa pun akan lebih dari tiga transit jaringan (dapat dihindari dengan pipelining HTTP). Sementara dalam koneksi LAN langsung ini bukan masalah besar, itu menjadi satu saat seseorang sedang bergerak dan berpindah sel jaringan. Sampai tingkat tertentu, ini diamati dengan SSE dan WebSockets, tetapi efeknya paling besar dengan polling.

  • Pembentukan Koneksi
    Meskipun dapat dihindari dengan menggunakan koneksi HTTP persisten yang dapat digunakan kembali untuk banyak permintaan polling, sulit untuk menyesuaikan waktu semua komponen Anda untuk polling dalam jangka waktu singkat untuk menjaga koneksi tetap hidup. Akhirnya, tergantung pada respons server, polling Anda akan disinkronkan.

  • Penurunan Kinerja
    Klien polling panjang (atau server) yang sedang dimuat memiliki kecenderungan alami untuk menurunkan kinerja dengan mengorbankan latensi pesan. Ketika itu terjadi, peristiwa yang didorong ke klien akan mengantri. Ini sangat tergantung pada implementasinya; dalam kasus kami, kami perlu menggabungkan semua data saat kami mengirim acara tambah/hapus/perbarui ke widget kami.

  • Waktu habis
    Permintaan polling yang panjang harus tetap tertunda sampai server memiliki sesuatu untuk dikirim ke klien. Ini dapat menyebabkan koneksi ditutup oleh server proxy jika tetap menganggur terlalu lama.

  • Multiplexing
    Ini dapat terjadi jika respons terjadi pada saat yang sama melalui koneksi HTTP/2 yang persisten. Ini bisa sulit dilakukan karena respons polling tidak bisa benar-benar sinkron.

Lebih lanjut tentang masalah dunia nyata yang dapat dialami seseorang dengan polling panjang dapat ditemukan di sini .

WebSocket

WebSocket

Sebagai contoh pertama dari metode server push , kita akan melihat WebSockets.

Melalui MDN:

WebSockets adalah teknologi canggih yang memungkinkan untuk membuka sesi komunikasi interaktif antara browser pengguna dan server. Dengan API ini, Anda dapat mengirim pesan ke server dan menerima respons berdasarkan peristiwa tanpa harus melakukan polling ke server untuk mendapatkan balasan.

Ini adalah protokol komunikasi yang menyediakan saluran komunikasi dupleks penuh melalui satu koneksi TCP.

Baik HTTP dan WebSockets terletak di lapisan aplikasi dari model OSI dan dengan demikian bergantung pada TCP di lapisan 4.

  1. Aplikasi
  2. Presentasi
  3. Sidang
  4. Mengangkut
  5. Jaringan
  6. Hubungan data
  7. Fisik

RFC 6455 menyatakan bahwa WebSocket "dirancang untuk bekerja melalui port HTTP 80 dan 443 serta mendukung proxy dan perantara HTTP" sehingga membuatnya kompatibel dengan protokol HTTP. Untuk mencapai kompatibilitas, jabat tangan WebSocket menggunakan header Peningkatan HTTP untuk mengubah dari protokol HTTP ke protokol WebSocket.

Ada juga artikel yang sangat bagus yang menjelaskan semua yang perlu Anda ketahui tentang WebSockets di Wikipedia. Saya mendorong Anda untuk membacanya.

Setelah menetapkan bahwa soket benar-benar dapat bekerja untuk kami, kami mulai mengeksplorasi kemampuan mereka dalam kasus bisnis kami, dan memukul dinding demi dinding.

  • Server proxy : Secara umum, ada beberapa masalah berbeda dengan WebSockets dan proxy:

    • Yang pertama terkait dengan penyedia layanan Internet dan cara mereka menangani jaringan mereka. Masalah dengan proxy radius memblokir port, dan sebagainya.
    • Jenis masalah kedua terkait dengan cara proxy dikonfigurasikan untuk menangani lalu lintas HTTP yang tidak aman dan koneksi yang berumur panjang (dampaknya berkurang dengan HTTPS).
    • Masalah ketiga “dengan WebSockets, Anda dipaksa untuk menjalankan proxy TCP sebagai lawan dari proxy HTTP. Proksi TCP tidak dapat menyuntikkan header, menulis ulang URL, atau melakukan banyak peran yang secara tradisional kami biarkan proksi HTTP kami urus.”
  • Sejumlah koneksi : Batas koneksi terkenal untuk permintaan HTTP yang berkisar pada angka 6, tidak berlaku untuk WebSockets. 50 soket = 50 koneksi. Sepuluh tab browser dengan 50 soket = 500 koneksi dan seterusnya. Karena WebSocket adalah protokol yang berbeda untuk mengirimkan data, itu tidak secara otomatis dimultipleks melalui koneksi HTTP/2 (tidak benar-benar berjalan di atas HTTP sama sekali). Menerapkan multiplexing kustom baik di server dan klien terlalu rumit untuk membuat soket berguna dalam kasus bisnis tertentu. Selanjutnya, ini memasangkan widget kami ke platform kami karena mereka akan membutuhkan semacam API pada klien untuk berlangganan dan kami tidak dapat mendistribusikannya tanpa itu.

  • Load balancing (tanpa multiplexing) : Jika setiap pengguna membuka n jumlah soket, load balancing yang tepat sangat rumit. Ketika server Anda kelebihan beban, dan Anda perlu membuat instance baru dan menghentikan yang lama tergantung pada implementasi perangkat lunak Anda, tindakan yang diambil pada "sambungkan kembali" dapat memicu rantai penyegaran besar-besaran dan permintaan baru untuk data yang akan membebani sistem Anda . WebSockets perlu dipelihara baik di server maupun di klien. Tidak mungkin untuk memindahkan koneksi soket ke server lain jika yang sekarang mengalami beban tinggi. Mereka harus ditutup dan dibuka kembali.

  • DoS : Ini biasanya ditangani oleh proxy HTTP front-end yang tidak dapat ditangani oleh proxy TCP yang diperlukan untuk WebSockets. Seseorang dapat terhubung ke soket dan mulai membanjiri server Anda dengan data. WebSockets membuat Anda rentan terhadap serangan semacam ini.

  • Menemukan kembali roda : Dengan WebSockets, seseorang harus menangani banyak masalah yang ditangani sendiri di HTTP.

Lebih lanjut tentang masalah dunia nyata dengan WebSockets dapat dibaca di sini.

Beberapa kasus penggunaan yang baik untuk WebSockets adalah obrolan dan permainan multi-pemain yang manfaatnya lebih besar daripada masalah implementasinya. Dengan manfaat utama mereka adalah komunikasi dupleks, dan kami tidak benar-benar membutuhkannya, kami perlu melanjutkan.

Dampak

Kami mendapatkan peningkatan biaya operasional dalam hal pengembangan, pengujian, dan penskalaan; perangkat lunak dan infrastruktur TI dengan keduanya: polling dan WebSockets.

Kami mendapatkan masalah yang sama pada perangkat seluler dan jaringan dengan keduanya. Desain perangkat keras perangkat ini mempertahankan koneksi terbuka dengan menjaga antena dan koneksi ke jaringan seluler tetap hidup. Hal ini menyebabkan berkurangnya masa pakai baterai, panas, dan dalam beberapa kasus, biaya tambahan untuk data.

Tapi Mengapa Kami Masih Memiliki Masalah Dengan Perangkat Seluler?

Mari kita pertimbangkan bagaimana perangkat seluler default terhubung ke Internet:

Perangkat seluler harus melalui beberapa rintangan sebelum dapat terhubung ke Internet.

Penjelasan langsung tentang cara kerja jaringan seluler: Biasanya perangkat seluler memiliki antena berdaya rendah yang dapat menerima data dari sel. Dengan cara ini, setelah perangkat menerima data dari panggilan masuk, antena dupleks penuh akan dinyalakan untuk melakukan panggilan. Antena yang sama digunakan setiap kali Anda ingin melakukan panggilan atau mengakses Internet (jika tidak tersedia WiFi). Antena full-duplex perlu membuat koneksi ke jaringan seluler dan melakukan beberapa otentikasi. Setelah koneksi dibuat, ada beberapa komunikasi antara perangkat Anda dan sel untuk melakukan permintaan jaringan kami. Kami dialihkan ke proxy internal penyedia layanan seluler yang menangani permintaan Internet. Sejak saat itu, prosedurnya sudah diketahui: ia menanyakan DNS di mana www.domainname.ext sebenarnya, menerima URI ke sumber daya, dan akhirnya dialihkan ke sana.

Proses ini, seperti yang dapat Anda bayangkan, menghabiskan cukup banyak daya baterai. Inilah alasan mengapa vendor ponsel memberikan waktu siaga beberapa hari dan waktu bicara hanya dalam beberapa jam.

Tanpa WiFi, WebSockets dan polling memerlukan antena dupleks penuh untuk bekerja hampir secara konstan. Jadi, kami menghadapi peningkatan konsumsi data dan peningkatan konsumsi daya — dan bergantung pada perangkat — juga panas.

Pada saat segalanya tampak suram, sepertinya kita harus mempertimbangkan kembali persyaratan bisnis untuk aplikasi kita. Apakah kita kehilangan sesuatu?

SSE

Acara yang Dikirim oleh Server

Melalui MDN:

“Antarmuka EventSource digunakan untuk menerima Acara yang Dikirim oleh Server. Itu terhubung ke server melalui HTTP dan menerima acara dalam format teks/aliran acara tanpa menutup koneksi.

Perbedaan utama polling adalah bahwa kami hanya mendapatkan satu koneksi dan terus mengalirkan acara melaluinya. Jajak pendapat panjang menciptakan koneksi baru untuk setiap tarikan — ergo header di atas kepala dan masalah lain yang kami hadapi di sana.

Melalui html5doctor.com:

Peristiwa Terkirim Server adalah peristiwa waktu nyata yang dipancarkan oleh server dan diterima oleh browser. Mereka mirip dengan WebSockets karena terjadi secara real time, tetapi mereka merupakan metode komunikasi satu arah dari server.

Kelihatannya agak aneh, tetapi setelah dipertimbangkan — aliran data utama kami adalah dari server ke klien dan dalam kesempatan yang jauh lebih sedikit dari klien ke server.

Sepertinya kami dapat menggunakan ini untuk kasus bisnis utama kami dalam pengiriman data. Kami dapat menyelesaikan pembelian pelanggan dengan mengirimkan permintaan baru karena protokolnya searah dan klien tidak dapat mengirim pesan ke server melaluinya. Ini pada akhirnya akan memiliki waktu tunda antena dupleks penuh untuk boot pada perangkat seluler. Namun, kita dapat hidup dengan itu terjadi dari waktu ke waktu — bagaimanapun juga, penundaan ini diukur dalam milidetik.

Fitur unik

  • Aliran koneksi berasal dari server dan hanya-baca.
  • Mereka menggunakan permintaan HTTP biasa untuk koneksi persisten, bukan protokol khusus. Mendapatkan multiplexing melalui HTTP/2 di luar kotak.
  • Jika koneksi terputus, EventSource memicu peristiwa kesalahan dan secara otomatis mencoba menyambung kembali. Server juga dapat mengontrol batas waktu sebelum klien mencoba menyambung kembali (dijelaskan lebih detail nanti).
  • Klien dapat mengirim ID unik dengan pesan. Saat klien mencoba menyambung kembali setelah koneksi terputus, ia akan mengirimkan ID terakhir yang diketahui. Kemudian server dapat melihat bahwa klien melewatkan n jumlah pesan dan mengirim backlog pesan yang tidak terjawab saat terhubung kembali.

Contoh Implementasi Klien

Peristiwa ini mirip dengan peristiwa JavaScript biasa yang terjadi di browser — seperti peristiwa klik — kecuali kita dapat mengontrol nama peristiwa dan data yang terkait dengannya.

Mari kita lihat pratinjau kode sederhana untuk sisi klien:

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

Apa yang kita lihat dari contoh adalah bahwa sisi klien cukup sederhana. Itu terhubung ke sumber kami dan menunggu untuk menerima pesan.

Untuk mengaktifkan server untuk mendorong data ke halaman web melalui HTTP atau menggunakan protokol server-push khusus, spesifikasi memperkenalkan antarmuka `EventSource` pada klien. Menggunakan API ini terdiri dari membuat objek `EventSource` dan mendaftarkan event listener.

Implementasi klien untuk WebSockets terlihat sangat mirip dengan ini. Kompleksitas dengan soket terletak pada infrastruktur TI dan implementasi server.

Sumber Acara

Setiap objek EventSource memiliki anggota berikut:

  • URL: disetel selama konstruksi.
  • Permintaan: awalnya nol.
  • Waktu koneksi ulang: nilai dalam ms (nilai yang ditentukan agen pengguna).
  • ID peristiwa terakhir: awalnya berupa string kosong.
  • Status siap: status koneksi.
    • MENGHUBUNGKAN (0)
    • BUKA (1)
    • TUTUP (2)

Terlepas dari URL, semua diperlakukan seperti pribadi dan tidak dapat diakses dari luar.

Acara bawaan:

  1. Membuka
  2. Pesan
  3. Kesalahan

Menangani Koneksi Terputus

Sambungan secara otomatis dibuat kembali oleh browser jika terputus. Server dapat mengirimkan batas waktu untuk mencoba lagi atau menutup koneksi secara permanen. Dalam kasus seperti itu, browser akan mematuhi baik mencoba menyambung kembali setelah batas waktu atau tidak mencoba sama sekali jika sambungan mendapat pesan pemutusan. Tampaknya cukup sederhana - dan memang begitu.

Contoh Implementasi Server

Nah jika kliennya sesederhana itu, mungkin implementasi servernya rumit?

Nah, handler server untuk SSE mungkin terlihat seperti ini:

 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 }

Kami mendefinisikan fungsi yang akan menangani respons:

  1. Siapkan header
  2. Menulis pesan
  3. Mengirim

Perhatikan bahwa Anda tidak melihat panggilan metode send() atau push() . Ini karena standar mendefinisikan bahwa pesan akan dikirim segera setelah menerima dua \n\n karakter seperti pada contoh: response.write("data: " + data + '\n\n'); . Ini akan segera mendorong pesan ke klien. Harap perhatikan bahwa data harus berupa string yang diloloskan dan tidak memiliki karakter baris baru di akhir.

Konstruksi Pesan

Seperti disebutkan sebelumnya, pesan dapat berisi beberapa properti:

  1. pengenal
    Jika nilai bidang tidak berisi U+0000 NULL, setel buffer ID peristiwa terakhir ke nilai bidang. Jika tidak, abaikan bidang tersebut.
  2. Data
    Tambahkan nilai bidang ke buffer data, lalu tambahkan satu karakter U+000A LINE FEED (LF) ke buffer data.
  3. Peristiwa
    Setel buffer jenis acara ke nilai bidang. Ini mengarah ke event.type mendapatkan nama acara khusus Anda.
  4. Mencoba kembali
    Jika nilai bidang hanya terdiri dari digit ASCII, maka tafsirkan nilai bidang sebagai bilangan bulat dalam basis sepuluh, dan atur waktu penyambungan kembali aliran peristiwa ke bilangan bulat tersebut. Jika tidak, abaikan bidang tersebut.

Apa pun akan diabaikan. Kami tidak bisa memperkenalkan bidang kami sendiri.

Contoh dengan event tambahan:

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

Kemudian pada klien, ini ditangani dengan addEventListener seperti:

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

Anda dapat mengirim beberapa pesan yang dipisahkan oleh baris baru selama Anda memberikan ID yang berbeda.

 ... 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 ...

Ini sangat menyederhanakan apa yang dapat kita lakukan dengan data kita.

Persyaratan Server Tertentu

Selama POC kami untuk back-end, kami mengidentifikasi memiliki beberapa spesifik yang perlu ditangani untuk memiliki implementasi kerja SSE. Skenario kasus terbaik Anda akan menggunakan server berbasis loop acara seperti NodeJS, Kestrel, atau Twisted. Idenya adalah bahwa dengan solusi berbasis utas Anda akan memiliki utas per koneksi → 1000 koneksi = 1000 utas. Dengan solusi loop acara, Anda akan memiliki satu utas untuk 1000 koneksi.

  1. Anda hanya dapat menerima permintaan EventSource jika permintaan HTTP mengatakan dapat menerima jenis MIME aliran peristiwa;
  2. Anda perlu menyimpan daftar semua pengguna yang terhubung untuk memancarkan acara baru;
  3. Anda harus mendengarkan koneksi yang terputus dan menghapusnya dari daftar pengguna yang terhubung;
  4. Secara opsional, Anda harus menyimpan riwayat pesan sehingga klien yang terhubung kembali dapat mengejar pesan yang terlewat.

Ini bekerja seperti yang diharapkan dan terlihat seperti sihir pada awalnya. Kami mendapatkan semua yang kami inginkan agar aplikasi kami bekerja dengan cara yang efisien. Seperti semua hal yang terlihat terlalu bagus untuk menjadi kenyataan, terkadang kita menghadapi beberapa masalah yang perlu ditangani. Namun, mereka tidak rumit untuk diterapkan atau digunakan:

  • Server proxy lawas diketahui, dalam kasus tertentu, memutuskan koneksi HTTP setelah batas waktu yang singkat. Untuk melindungi dari server proxy tersebut, penulis dapat menyertakan baris komentar (yang dimulai dengan karakter ':') setiap 15 detik atau lebih.

  • Penulis yang ingin menghubungkan koneksi sumber peristiwa satu sama lain atau ke dokumen tertentu yang disajikan sebelumnya mungkin menemukan bahwa mengandalkan alamat IP tidak berfungsi, karena klien individu dapat memiliki beberapa alamat IP (karena memiliki beberapa server proxy) dan alamat IP individu dapat memiliki beberapa klien (karena berbagi server proxy). Lebih baik menyertakan pengidentifikasi unik dalam dokumen saat disajikan dan kemudian meneruskan pengidentifikasi itu sebagai bagian dari URL saat koneksi dibuat.

  • Penulis juga diperingatkan bahwa chunking HTTP dapat memiliki efek negatif yang tidak terduga pada keandalan protokol ini, khususnya, jika chunking dilakukan oleh lapisan yang berbeda yang tidak mengetahui persyaratan waktu. Jika ini menjadi masalah, chunking dapat dinonaktifkan untuk menyajikan aliran acara.

  • Klien yang mendukung batasan koneksi per server HTTP mungkin mengalami masalah saat membuka beberapa halaman dari situs jika setiap halaman memiliki EventSource ke domain yang sama. Penulis dapat menghindari hal ini menggunakan mekanisme yang relatif kompleks menggunakan nama domain unik per koneksi, atau dengan mengizinkan pengguna untuk mengaktifkan atau menonaktifkan fungsionalitas EventSource pada basis per halaman, atau dengan berbagi satu objek EventSource menggunakan pekerja bersama.

  • Dukungan browser dan Polyfill: Edge tertinggal dari implementasi ini, tetapi tersedia polyfill yang dapat menyelamatkan Anda. Namun, kasus terpenting untuk SSE dibuat untuk perangkat seluler di mana IE/Edge tidak memiliki pangsa pasar yang layak.

Beberapa polyfill yang tersedia:

  • yafle
  • amvtek
  • remy

Dorongan Tanpa Koneksi Dan Fitur Lainnya

Agen pengguna yang berjalan di lingkungan yang terkendali, misalnya, browser pada handset seluler yang terkait dengan operator tertentu, dapat mengalihkan pengelolaan koneksi ke proxy di jaringan. Dalam situasi seperti itu, agen pengguna untuk tujuan kesesuaian dianggap menyertakan perangkat lunak handset dan proxy jaringan.

Misalnya, browser di perangkat seluler, setelah membuat koneksi, mungkin mendeteksi bahwa itu ada di jaringan pendukung dan meminta server proxy di jaringan mengambil alih pengelolaan koneksi. Garis waktu untuk situasi seperti itu mungkin sebagai berikut:

  1. Browser terhubung ke server HTTP jarak jauh dan meminta sumber daya yang ditentukan oleh penulis di konstruktor EventSource.
  2. Server mengirim pesan sesekali.
  3. Di antara dua pesan, browser mendeteksi bahwa itu menganggur kecuali untuk aktivitas jaringan yang terlibat dalam menjaga koneksi TCP tetap hidup, dan memutuskan untuk beralih ke mode tidur untuk menghemat daya.
  4. Peramban terputus dari server.
  5. Peramban menghubungi layanan di jaringan dan meminta layanan tersebut, "proxy push", sebagai gantinya mempertahankan koneksi.
  6. Layanan "push proxy" menghubungi server HTTP jarak jauh dan meminta sumber daya yang ditentukan oleh penulis di konstruktor EventSource (mungkin termasuk header HTTP Last-Event-ID , dll.).
  7. Browser memungkinkan perangkat seluler untuk tidur.
  8. Server mengirim pesan lain.
  9. Layanan "push proxy" menggunakan teknologi seperti push OMA untuk menyampaikan acara ke perangkat seluler, yang hanya cukup bangun untuk memproses acara dan kemudian kembali tidur.

Hal ini dapat mengurangi penggunaan data total, dan oleh karena itu, dapat menghasilkan penghematan daya yang cukup besar.

Selain mengimplementasikan API dan format kawat aliran teks/peristiwa yang ada seperti yang ditentukan oleh spesifikasi dan dengan cara yang lebih terdistribusi (seperti yang dijelaskan di atas), format pembingkaian peristiwa yang ditentukan oleh spesifikasi lain yang berlaku dapat didukung.

Ringkasan

Setelah POC yang panjang dan lengkap termasuk implementasi server dan klien, sepertinya SSE adalah jawaban untuk masalah kami dengan pengiriman data. Ada beberapa jebakan dengan itu juga, tetapi mereka terbukti sepele untuk diperbaiki.

Beginilah tampilan pengaturan produksi kami pada akhirnya:

Ikhtisar arsitektur
Ikhtisar arsitektur akhir. Semua titik akhir API berada di belakang nginx sehingga klien mendapatkan respons multipleks.

Kami mendapatkan yang berikut dari NGINX:

  • Proksi ke titik akhir API di tempat yang berbeda;
  • HTTP/2 dan semua manfaatnya seperti multiplexing untuk koneksi;
  • Penyeimbang beban;
  • SSL.

Dengan cara ini kami mengelola pengiriman data dan sertifikat kami di satu tempat alih-alih melakukannya di setiap titik akhir secara terpisah.

Manfaat utama yang kami dapatkan dari pendekatan ini adalah:

  • Data efisien;
  • Implementasi yang lebih sederhana;
  • Ini secara otomatis dimultipleks melalui HTTP/2;
  • Membatasi jumlah koneksi untuk data pada klien menjadi satu;
  • Menyediakan mekanisme untuk menghemat baterai dengan melepas koneksi ke proxy.

SSE bukan hanya alternatif yang layak untuk metode lain untuk memberikan pembaruan cepat; sepertinya ada di liganya sendiri dalam hal pengoptimalan untuk perangkat seluler. Tidak ada overhead dalam pelaksanaannya dibandingkan dengan alternatif. Dalam hal implementasi sisi server, tidak jauh berbeda dengan polling. Di klien, ini jauh lebih sederhana daripada polling karena memerlukan langganan awal dan menetapkan event handler — mirip dengan cara WebSockets dikelola.

Periksa demo kode jika Anda ingin mendapatkan implementasi client-server sederhana.

Sumber daya

  • “Masalah Umum dan Praktik Terbaik untuk Penggunaan Polling dan Streaming Panjang dalam HTTP Dua Arah,” IETF (PDF)
  • Rekomendasi W3C, W3C
  • “Akankah WebSocket bertahan dari HTTP/2?,” Allan Denis, InfoQ
  • “Streaming Pembaruan dengan Acara yang Dikirim oleh Server,” Eric Bidelman, HTML5 Rocks
  • “Aplikasi Dorong Data dengan HTML5 SSE,” Darren Cook, O'Reilly Media