Perintah dan konteks
Pertanyaan ini menguji apakah seorang insinyur frontend memperlakukan WebSocket sebagai koneksi yang tidak dapat diandalkan. Perubahan Wi-Fi, mode tidur (sleep), reset proxy, restart server, dan soket half-open yang senyap adalah hal yang wajar. Memanggil connect() secara langsung dari onclose akan menciptakan badai koneksi ulang (reconnect storm) dan tidak dapat memulihkan event yang terlewat saat offline. Bahas status klien, backoff, heartbeat, pemutaran ulang (replay), autentikasi, dan siklus hidup halaman.
Hal yang diuji oleh pewawancara
Jawaban yang kuat mendefinisikan semantik pesan dan batasan pemulihan, kemudian menegakkan transisi dengan state machine yang eksplisit. Jawaban tersebut menggunakan exponential backoff dengan jitter, heartbeat aplikasi untuk deteksi half-open, pemutaran ulang urutan atau kursor untuk event yang terlewat, serta antrean yang membedakan notifikasi yang dapat dibuang dari perintah yang memerlukan konfirmasi (acknowledged). Mereka juga membahas tab latar belakang, perubahan jaringan, token kedaluwarsa, pembatasan server (throttling), dan status yang terlihat oleh pengguna.
Pertanyaan untuk diklarifikasi
- Apakah sebuah pesan berupa notifikasi yang dapat dibuang, event yang dapat diputar ulang, atau perintah yang wajib diproses? Apakah server mendukung pemutaran ulang kursor?
- Bagaimana autentikasi diperbarui? Apa yang terjadi jika token, izin, atau versi protokol berubah selama koneksi ulang?
- Siapa yang mengirim dan mengonfirmasi heartbeat? Berapa lama proxy dapat membuang paket secara senyap?
- Apakah klien harus mendukung banyak tab, aplikasi seluler di latar belakang, browser sleep, dan transisi offline?
- Apakah pengguna dapat terus mengedit saat terputus? Bagaimana konflik, pengurutan, dan pengiriman duplikat ditangani?
Kerangka jawaban 30 detik
“Saya akan memodelkan klien dengan status idle, connecting, open, suspect, backoff, dan closed. Setelah terbuka, ping/pong aplikasi dengan batas waktu akan mendeteksi kegagalan senyap. Penutupan abnormal atau batas waktu heartbeat akan memicu exponential backoff dengan jitter sehingga klien tidak menyambung ulang secara bersamaan. Setiap event memiliki urutan monotonik; setelah terhubung kembali, klien melanjutkan dari kursor berurutan terakhirnya sebelum memproses event langsung. Perintah membawa kunci idempotensi dan memerlukan konfirmasi (acknowledgement), sedangkan notifikasi dapat dibuang. Status offline, tersembunyi, dan token kedaluwarsa akan menjeda atau melakukan autentikasi ulang dengan umpan balik pengguna yang jelas.”
Jawaban mendalam langkah demi langkah
Langkah 1: Definisikan state machine koneksi
Pusatkan status dan transisi yang valid daripada membiarkan callback memodifikasi isConnected secara langsung. Jalur tipikal adalah connecting -> open -> suspect -> backoff -> connecting; penutupan oleh pengguna akan memasuki closed dan menonaktifkan koneksi ulang otomatis. Catat alasan, jumlah percobaan, dan ID koneksi untuk setiap transisi.
Langkah 2: Bedakan penutupan, kesalahan, dan soket half-open
Event error pada browser mungkin tidak berisi alasan yang dapat ditindaklanjuti, dan close mungkin tidak akan pernah tiba untuk jalur half-open. Heartbeat mencatat waktu kirim, batas waktu pong, dan pesan terakhir yang diterima. Saat batas waktu habis, tutup soket lama secara eksplisit sebelum menjadwalkan yang baru agar dua koneksi tidak mengirimkan event duplikat.
Langkah 3: Terapkan backoff dan jitter
RFC 6455 memperingatkan bahwa koneksi ulang langsung yang terus-menerus dapat menjadi badai yang mirip dengan denial-of-service. Gunakan min(cap, base * 2^attempt) + random(0, jitter) dan setel ulang jumlah percobaan hanya setelah koneksi stabil. Patuhi petunjuk pemeliharaan atau throttling server seperti Retry-After.
Langkah 4: Pulihkan kursor event
Event membawa urutan aliran monotonik. Klien mempertahankan urutan berurutan terakhir dan mengirimkannya dalam handshake pemulihan. Server mengembalikan rentang yang hilang, lalu beralih ke pengiriman langsung. Jika kursor telah kedaluwarsa, server mengembalikan versi snapshot; klien memuatnya dan melanjutkan setelah urutan snapshot tersebut.
Langkah 5: Desain antrean pengiriman dan idempotensi
Notifikasi kehadiran dan pengetikan dapat dibuang; pengeditan dan niat pembayaran memerlukan konfirmasi. Perintah membawa clientMessageId, dan server melakukan deduplikasi berdasarkan kunci tersebut serta menyimpan hasilnya. Pertahankan antrean offline yang terbatas (bounded); jika penuh, hentikan penerimaan perintah baru dan jelaskan statusnya alih-alih menyembunyikan antrean memori yang tak terbatas.
Langkah 6: Tangani autentikasi dan perubahan protokol
Periksa apakah masa berlaku token hampir habis sebelum menyambung kembali dan perbarui bila diperlukan. Kode penutupan tanpa izin (unauthorized) menghentikan percobaan buta dan masuk ke alur login. Sertakan versi protokol dalam handshake; negosiasikan versi yang kompatibel atau tampilkan jalur peningkatan daripada melakukan perulangan tanpa henti.
Langkah 7: Integrasikan siklus hidup halaman dan jaringan
visibilitychange, online/offline, dan penempatan aplikasi di latar belakang pada perangkat seluler mengubah strategi. Halaman yang tersembunyi dapat mengurangi frekuensi heartbeat atau menjeda pengiriman langsung; saat kembali aktif, jalankan pemeriksaan kesehatan dan sinkronisasi kursor. Status offline segera menghentikan upaya panggilan dan status online memulai jadwal backoff alih-alih membuang-buang percobaan yang gagal.
Langkah 8: Pantau pengalaman pengguna dan beban server
Catat waktu koneksi, upaya koneksi ulang, batas waktu heartbeat, jumlah pemutaran ulang, celah kursor, dan item antrean yang dibuang. Server memantau soket bersamaan, kegagalan handshake, lonjakan koneksi ulang, dan pesan duplikat berdasarkan tenant dan versi klien. Jangan pernah mencatat token atau badan pesan ke dalam log. Tampilkan "menyambungkan kembali" dan "sinkronisasi selesai" alih-alih kode kesalahan transport.
Pseudocode state-machine minimal
on_open(socket):
state = OPEN
send({type: "resume", lastSeen: cursor})
on_heartbeat_timeout():
socket.close()
state = BACKOFF
delay = min(MAX, BASE * 2 ** attempts) + random(0, JITTER)
schedule(connect, delay)Pertimbangan trade-off dan batasan
| Keputusan | Pilihan | Alasan |
|---|---|---|
| Penundaan koneksi ulang | Truncated exponential backoff ditambah jitter | Mengurangi lonjakan tersinkronisasi |
| Pemulihan | Pemutaran ulang kursor, snapshot jika diperlukan | Menghindari keharusan refresh halaman |
| Keandalan | Notifikasi yang dapat dibuang, perintah terkonfirmasi yang idempoten | Menyesuaikan biaya teknis dengan nilai bisnis |
| Tab latar belakang | Perlambat (throttle) atau jeda, lalu sinkronkan saat kembali | Menghemat daya dan koneksi menganggur |
WebSocket menyediakan pesan yang berurutan, bukan exactly-once tingkat bisnis, antrean offline, atau sinkronisasi status. Semantik tersebut berada pada level protokol aplikasi. Jika suatu produk hanya memerlukan push server satu arah yang dapat menoleransi kehilangan data, pertimbangkan SSE, tetapi jangan bingung antara pilihan transport dan jaminan pemulihan.
Rencana peluncuran dan bukti
Rilis state machine, heartbeat, dan jittered backoff terlebih dahulu, lalu tambahkan pemulihan kursor dan perintah idempoten. Lakukan simulasi perubahan jaringan, restart server, browser sleep, token kedaluwarsa, dan pemutusan hubungan tersinkronisasi dari banyak klien. RFC 6455 merekomendasikan penundaan awal acak dan peningkatan backoff setelah penutupan abnormal; MDN mendokumentasikan event WebSocket browser dan readyState.
Kriteria keluar uji coba (pilot exit criteria)
Halaman pulih tanpa perlu refresh; perintah yang dikonfirmasi tidak terduplikasi maupun hilang; lonjakan koneksi ulang tidak membebani server; halaman tersembunyi tidak menahan soket yang tidak perlu; serta pengguna dapat melihat status koneksi dan waktu sinkronisasi terakhir. Setiap kegagalan berarti harus memperbaiki protokol atau kebijakan siklus hidup terlebih dahulu.
Cara membuktikan peningkatan performa
Bandingkan tingkat keberhasilan pemulihan, waktu pemulihan p95, tingkat event duplikat, celah kursor, lonjakan handshake, dan penggunaan energi seluler sebelum dan sesudah penerapan. Segmentasikan berdasarkan jaringan, browser, dan visibilitas halaman sehingga nilai rata-rata tidak menyembunyikan kegagalan pada platform tertentu.
Kesalahan umum dan tindak lanjut
Menyambung kembali seketika pada onclose
Ketika server melakukan restart, setiap klien akan mencoba tersambung pada saat yang sama. Gunakan backoff, jitter, dan petunjuk throttling server, serta setel ulang upaya hanya setelah koneksi stabil.
Hanya mengandalkan event close
Jalur half-open mungkin tidak akan pernah memancarkan event close. Gunakan heartbeat dan batas waktu, lalu tutup sendiri soket yang sudah basi (stale).
Mengasumsikan koneksi ulang yang sukses berarti data sudah lengkap
Pemulihan koneksi tidak memutar ulang event yang terlewat saat offline. Gunakan kursor terakhir yang terlihat, versi snapshot, dan pemeriksaan urutan berurutan.
Bagaimana cara mencegah perintah duplikat?
Berikan kunci idempotensi klien pada setiap perintah, simpan hasilnya di server, dan hapus item antrean hanya setelah adanya konfirmasi atau kueri hasil.
Bagaimana jika token kedaluwarsa saat koneksi ulang?
Perbarui token dan lakukan handshake kembali. Penutupan akibat tidak terotorisasi (unauthorized) menghentikan percobaan ulang dan masuk ke alur login; ini bukan kesalahan jaringan sementara.
Bagaimana cara mengontrol koneksi di beberapa tab?
Gunakan BroadcastChannel atau SharedWorker sehingga satu tab menjadi pemilik soket dan tab lainnya berlangganan. Pindahkan kepemilikan saat tab pemilik ditutup dan validasi ulang kursor.