Peristiwa yang dikirim server (SSE) mengirim pembaruan otomatis ke klien dari server, dengan koneksi jarak jauh. Setelah koneksi dibuat, server dapat memulai data transmisi otomatis.
Anda dapat menggunakan SSE untuk mengirim notifikasi push dari aplikasi web Anda. SSE mengirimkan informasi dalam satu arah, sehingga Anda tidak akan menerima pembaruan dari dengan klien besar.
Konsep SSE mungkin sudah tidak asing lagi. Aplikasi web "berlangganan" ke aliran pembaruan yang dihasilkan oleh server dan, setiap kali peristiwa baru terjadi, notifikasi dikirim ke klien. Tapi untuk benar-benar memahami peristiwa yang dikirim server, kita perlu memahami keterbatasan pendahulu AJAX. Hal ini mencakup:
Polling: Aplikasi berulang kali polling data di server. Teknik ini digunakan oleh sebagian besar aplikasi AJAX. Dengan protokol HTTP, pengambilan data berkisar pada format permintaan dan respons. Klien membuat permintaan dan menunggu server untuk merespons dengan data. Jika tidak ada yang tersedia, kolom kosong ditampilkan. Polling tambahan menghasilkan overhead HTTP yang lebih besar.
Polling panjang (Hanging GET / COMET): Jika server tidak memiliki data tersedia, server menahan permintaan hingga data baru tersedia. Oleh karena itu, teknik ini sering disebut sebagai "Hanging GET". Kapan informasi menjadi tersedia, server merespons, menutup koneksi, dan prosesnya diulang. Jadi, server terus-menerus merespons dengan data baru. Untuk menyiapkannya, developer biasanya menggunakan peretasan seperti menambahkan tag skrip menjadi 'tak terbatas' iframe.
Peristiwa yang dikirim server, telah dirancang dari awal agar menjadi efisien. Saat berkomunikasi dengan SSE, server dapat mengirimkan data ke kapan saja, tanpa perlu membuat permintaan awal. Dengan kata lain, {i>SUMIF<i} memiliki daftar sel pembaruan dapat di-{i>streaming <i}dari server ke klien saat terjadi. SSE membuka saluran searah tunggal antara server dan klien.
Perbedaan utama antara peristiwa yang dikirim server dan polling panjang adalah bahwa SSE ditangani langsung oleh {i>browser<i} dan pengguna hanya perlu mendengarkan pesan.
Peristiwa yang dikirim server versus WebSockets
Mengapa Anda memilih peristiwa yang dikirim server daripada WebSockets? Pertanyaan bagus.
WebSockets memiliki protokol yang kaya dengan komunikasi dua arah, {i>full-duplex<i}. Saluran dua arah lebih baik untuk {i>game<i}, aplikasi pesan, dan kasus penggunaan apa pun yang membutuhkan pembaruan yang mendekati {i>real-time<i} di kedua arah.
Namun, terkadang Anda hanya memerlukan komunikasi satu arah dari server.
Misalnya, saat teman memperbarui status, saham, umpan berita, atau
mekanisme push data otomatis lainnya. Dengan kata lain,
{i>SUMIF<i} memiliki daftar sel
pembaruan pada Database SQL Web sisi klien atau penyimpanan objek IndexedDB.
Jika Anda perlu mengirim data ke server, XMLHttpRequest
akan selalu menjadi teman.
SSE dikirim melalui HTTP. Tidak ada protokol atau server khusus implementasi agar berhasil. WebSockets memerlukan full-duplex koneksi dan server WebSocket baru untuk menangani protokol tersebut.
Selain itu, peristiwa yang dikirim server memiliki berbagai fitur yang tidak dimiliki WebSockets sesuai dengan desainnya, termasuk sambungan kembali otomatis, ID peristiwa, dan kemampuan untuk peristiwa arbitrer.
Membuat EventSource dengan JavaScript
Untuk berlangganan aliran peristiwa, buat objek EventSource
dan teruskan
URL streaming Anda:
const source = new EventSource('stream.php');
Selanjutnya, siapkan pengendali untuk peristiwa message
. Anda dapat memilih
dengarkan open
dan error
:
source.addEventListener('message', (e) => {
console.log(e.data);
});
source.addEventListener('open', (e) => {
// Connection was opened.
});
source.addEventListener('error', (e) => {
if (e.readyState == EventSource.CLOSED) {
// Connection was closed.
}
});
Saat update didorong dari server, pengendali onmessage
diaktifkan
dan data baru akan tersedia di properti e.data
. Bagian ajaibnya adalah
setiap kali koneksi ditutup, {i>browser<i}
secara otomatis terhubung kembali ke
sumber setelah ~3 detik. Implementasi server Anda bahkan
memiliki kontrol atas
waktu tunggu koneksi kembali ini.
Selesai. Klien Anda kini dapat memproses peristiwa dari stream.php
.
Format streaming peristiwa
Mengirim aliran peristiwa dari sumber adalah masalah menyusun
respons teks biasa, yang disajikan dengan Content-Type text/event-stream
,
yang mengikuti format SSE.
Dalam bentuk dasarnya, respons harus berisi baris data:
, diikuti oleh elemen
pesan, diikuti dengan dua "\n" untuk mengakhiri streaming:
data: My message\n\n
Data multibaris
Jika pesan lebih panjang, Anda dapat memisahkannya menggunakan beberapa baris data:
.
Dua atau lebih baris berturut-turut yang dimulai dengan data:
diperlakukan sebagai
satu bagian data, yang berarti hanya satu peristiwa message
yang diaktifkan.
Setiap baris harus diakhiri dengan satu "\n" (kecuali yang terakhir, yang harus berakhir
dengan dua). Hasil yang diteruskan ke pengendali message
Anda adalah string tunggal
yang digabungkan oleh karakter baris baru. Contoh:
data: first line\n
data: second line\n\n</pre>
Ini menghasilkan "baris pertama\nbaris kedua" di e.data
. Kemudian seseorang
dapat menggunakan
e.data.split('\n').join('')
untuk merekonstruksi pesan sans "\n" karakter.
Mengirim data JSON
Menggunakan beberapa baris membantu Anda mengirim JSON tanpa melanggar sintaksis:
data: {\n
data: "msg": "hello world",\n
data: "id": 12345\n
data: }\n\n
Dan kemungkinan kode sisi klien untuk menangani streaming tersebut:
source.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
console.log(data.id, data.msg);
});
Mengaitkan ID dengan peristiwa
Anda dapat mengirim ID unik bersama acara streaming dengan menyertakan baris yang dimulai dengan
id:
:
id: 12345\n
data: GOOG\n
data: 556\n\n
Dengan menetapkan ID, browser dapat melacak peristiwa terakhir yang diaktifkan, sehingga jika,
koneksi ke server terputus, header HTTP khusus (Last-Event-ID
)
tetapkan dengan permintaan baru. Hal ini memungkinkan browser menentukan peristiwa yang sesuai untuk diaktifkan.
Peristiwa message
berisi properti e.lastEventId
.
Mengontrol waktu tunggu koneksi ulang
Browser mencoba menghubungkan kembali ke sumber sekitar 3 detik
setelah setiap koneksi ditutup. Anda dapat mengubah waktu tunggu tersebut dengan menyertakan
baris yang diawali dengan retry:
, diikuti dengan jumlah milidetik
menunggu sebelum mencoba
menghubungkan kembali.
Contoh berikut mencoba menghubungkan kembali setelah 10 detik:
retry: 10000\n
data: hello world\n\n
Menentukan nama peristiwa
Satu sumber peristiwa dapat menghasilkan berbagai jenis peristiwa dengan menyertakan
nama peristiwa. Jika ada baris yang diawali dengan event:
,
diikuti dengan nama unik untuk peristiwa, peristiwa tersebut akan dikaitkan dengan nama tersebut.
Di klien, pemroses peristiwa dapat disiapkan untuk memproses peristiwa tertentu tersebut.
Misalnya, {i>output<i} server berikut ini mengirimkan tiga jenis kejadian, 'pesan' umum {i>event<i}, '{i>userlogon<i}', dan '{i>update<i}' acara:
data: {"msg": "First message"}\n\n
event: userlogon\n
data: {"username": "John123"}\n\n
event: update\n
data: {"username": "John123", "emotion": "happy"}\n\n
Dengan penyiapan pemroses peristiwa pada klien:
source.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
console.log(data.msg);
});
source.addEventListener('userlogon', (e) => {
const data = JSON.parse(e.data);
console.log(`User login: ${data.username}`);
});
source.addEventListener('update', (e) => {
const data = JSON.parse(e.data);
console.log(`${data.username} is now ${data.emotion}`);
};
Contoh server
Berikut adalah penerapan server dasar di PHP:
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache'); // recommended to prevent caching of event data.
/**
* Constructs the SSE data format and flushes that data to the client.
*
* @param string $id Timestamp/id of this connection.
* @param string $msg Line of text that should be transmitted.
**/
function sendMsg($id, $msg) {
echo "id: $id" . PHP_EOL;
echo "data: $msg" . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
?>
Berikut adalah implementasi serupa pada Node JS menggunakan Pengendali Express:
app.get('/events', (req, res) => {
// Send the SSE header.
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// Sends an event to the client where the data is the current date,
// then schedules the event to happen again after 5 seconds.
const sendEvent = () => {
const data = (new Date()).toLocaleTimeString();
res.write("data: " + data + '\n\n');
setTimeout(sendEvent, 5000);
};
// Send the initial event immediately.
sendEvent();
});
sse-node.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<script>
const source = new EventSource('/events');
source.onmessage = (e) => {
const content = document.createElement('div');
content.textContent = e.data;
document.body.append(content);
};
</script>
</body>
</html>
Membatalkan streaming acara
Biasanya, browser akan terhubung kembali secara otomatis ke sumber peristiwa saat koneksi ditutup, tetapi perilaku itu dapat dibatalkan dari klien atau server.
Untuk membatalkan streaming dari klien, panggil:
source.close();
Untuk membatalkan streaming dari server, tanggapi dengan non text/event-stream
Content-Type
atau tampilkan status HTTP selain 200 OK
(misalnya 404 Not Found
).
Kedua metode tersebut mencegah browser membuat koneksi kembali.
Sekilas tentang keamanan
Permintaan yang dihasilkan oleh EventSource tunduk pada kebijakan asal yang sama dengan API jaringan lainnya seperti pengambilan. Jika Anda memerlukan endpoint SSE di server Anda dapat diakses dari berbagai sumber, baca cara mengaktifkan dengan Cross Origin Resource Sharing (CORS).