Maklum balas dan konteks
Soalan ini menguji sama ada jurutera frontend menganggap WebSocket sebagai sambungan yang tidak boleh dipercayai. Perubahan Wi-Fi, mod tidur, tetapan semula proksi, but semula pelayan, dan soket separuh buka (half-open) senyap adalah perkara biasa. Memanggil connect() secara serta-merta daripada onclose menghasilkan lonjakan sambung semula (reconnect storm) dan tidak dapat memulihkan peristiwa yang terlepas semasa luar talian. Berikan liputan tentang keadaan klien, backoff, denyutan nadi, main semula (replay), pengesahan, dan kitaran hayat halaman.
Perkara yang diuji oleh penemu bual
Jawapan yang kukuh mentakrifkan semantik mesej dan sempadan pemulihan, kemudian menguatkuasakan peralihan dengan mesin keadaan yang jelas. Mereka menggunakan backoff eksponen dengan jitter, denyutan nadi aplikasi untuk pengesanan separuh buka, main semula jujukan atau kursor untuk peristiwa yang terlepas, dan baris gilir yang membezakan pemberitahuan yang boleh digugurkan daripada arahan yang diperakui (acknowledged). Mereka juga membincangkan tab latar belakang, perubahan rangkaian, token tamat tempoh, pendikit pelayan (throttling), dan keadaan yang boleh dilihat oleh pengguna.
Soalan untuk penjelasan
- Adakah mesej merupakan pemberitahuan yang boleh digugurkan, peristiwa yang boleh dimainkan semula, atau arahan yang mesti diproses? Adakah pelayan menyokong main semula kursor?
- Bagaimanakah pengesahan disegarkan semula? Apakah yang berlaku apabila token, kebenaran, atau versi protokol berubah semasa sambung semula?
- Siapa yang menghantar dan mengesahkan denyutan nadi? Berapa lamakah proksi boleh menggugurkan paket secara senyap?
- Adakah klien perlu menyokong pelbagai tab, mod latar belakang mudah alih, mod tidur pelayar, dan peralihan luar talian?
- Bolehkah pengguna terus menyunting semasa terputus sambungan? Bagaimanakah konflik, susunan, dan penyerahan pendua dikendalikan?
Rangka kerja jawapan 30 saat
“Saya akan memodelkan klien dengan keadaan idle, connecting, open, suspect, backoff, dan closed. Selepas dibuka, ping/pong aplikasi dengan tarikh akhir mengesan kegagalan senyap. Penutupan tidak normal atau masa tamat denyutan nadi akan memasuki backoff eksponen dengan jitter supaya klien tidak menyambung semula secara serentak. Setiap peristiwa mempunyai jujukan monotonik; selepas menyambung semula, klien menyambung dari kursor bersebelahan terakhirnya sebelum menggunakan peristiwa langsung. Arahan membawa kunci keidempotenan dan memerlukan perakuan, manakala pemberitahuan boleh digugurkan. Keadaan luar talian, tersembunyi, dan token tamat tempoh akan dijeda atau disahkan semula dengan maklum balas pengguna yang jelas.”
Jawapan terperinci langkah demi langkah
Langkah 1: Takrifkan mesin keadaan sambungan
Pusatkan keadaan dan peralihan yang sah daripada membiarkan panggilan balik mengubah isConnected. Laluan biasa ialah connecting -> open -> suspect -> backoff -> connecting; penutupan oleh pengguna memasuki closed dan melumpuhkan sambung semula automatik. Rekodkan sebab, bilangan percubaan, dan id sambungan bagi setiap peralihan.
Langkah 2: Bezakan penutupan, ralat, dan soket separuh buka
Peristiwa error pelayar mungkin tidak mengandungi sebab yang boleh diambil tindakan, dan close mungkin tidak akan tiba untuk laluan separuh buka. Denyutan nadi merekodkan masa hantar, tarikh akhir pong, dan mesej terakhir yang diterima. Apabila masa tamat, tutup soket lama secara eksplisit sebelum menjadualkan yang baharu supaya dua sambungan tidak dapat menghantar peristiwa pendua.
Langkah 3: Laksanakan backoff dan jitter
RFC 6455 memberi amaran bahawa sambung semula serta-merta yang berterusan boleh menjadi lonjakan seperti penafian perkhidmatan (DoS). Gunakan min(cap, base * 2^attempt) + random(0, jitter) dan tetapkan semula bilangan percubaan hanya selepas sambungan stabil. Patuhi petunjuk penyelenggaraan atau pendikit pelayan seperti Retry-After.
Langkah 4: Pulihkan kursor peristiwa
Peristiwa membawa jujukan strim monotonik. Klien mengekalkan jujukan bersebelahan yang terakhir dan menghantarnya dalam jabat tangan penyambungan semula. Pelayan mengembalikan julat yang hilang, kemudian bertukar kepada penghantaran langsung. Jika kursor telah tamat tempoh, kembalikan versi syot kilat (snapshot); klien memuatkannya dan menyambung semula selepas jujukan syot kilat tersebut.
Langkah 5: Reka bentuk baris gilir penghantaran dan keidempotenan
Pemberitahuan kehadiran dan menaip boleh digugurkan; suntingan dan niat pembayaran memerlukan perakuan. Arahan membawa clientMessageId, dan pelayan menyahduplikasi mengikut kunci tersebut serta menyimpan hasilnya. Simpan baris gilir luar talian yang terhad (bounded); apabila penuh, berhenti menerima lebih banyak arahan dan jelaskan keadaan daripada menyembunyikan baris gilir memori tanpa had.
Langkah 6: Kendalikan pengesahan dan perubahan protokol
Semak sama ada token hampir tamat tempoh sebelum menyambung semula dan segarkan semula apabila diperlukan. Kod penutupan tanpa kebenaran (unauthorized) menghentikan percubaan semula membuta tuli dan memasuki aliran log masuk. Sertakan versi protokol dalam jabat tangan; rundingkan versi yang serasi atau tunjukkan laluan peningkatan daripada terus bergelung tanpa henti.
Langkah 7: Sepadukan kitaran hayat halaman dan rangkaian
visibilitychange, online/offline, dan proses latar belakang mudah alih mengubah strategi. Halaman yang tersembunyi boleh mengurangkan kekerapan denyutan nadi atau menjeda penghantaran langsung; apabila kembali, jalankan pemeriksaan kesihatan dan penyegerakan kursor. Luar talian menghentikan pendailan serta-merta dan dalam talian memulakan jadual backoff daripada membuang masa dengan percubaan yang gagal.
Langkah 8: Perhatikan pengalaman pengguna dan beban pelayan
Rekodkan masa sambungan, percubaan sambung semula, masa tamat denyutan nadi, kiraan main semula, jurang kursor, dan item baris gilir yang digugurkan. Pelayan memerhatikan soket serentak, kegagalan jabat tangan, puncak sambung semula, dan mesej pendua mengikut penyewa dan versi klien. Jangan sekali-kali log token atau badan mesej. Paparkan “menyambung semula” dan “terkini” dan bukannya kod ralat pengangkutan.
Pseudokod minimum mesin keadaan
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)Pertukaran dan sempadan
| Keputusan | Pilihan | Sebab |
|---|---|---|
| Kelewatan sambung semula | Backoff eksponen terpenggal ditambah jitter | Mengurangkan puncak yang disegerakkan |
| Pemulihan | Main semula kursor, syot kilat apabila diperlukan | Mengelakkan keperluan untuk memuat semula halaman |
| Kebolehpercayaan | Pemberitahuan boleh digugurkan, arahan diperakui idempoten | Memadankan kos dengan nilai perniagaan |
| Tab latar belakang | Dikit atau jeda, kemudian segerakkan apabila kembali | Menjimatkan kuasa dan sambungan melahu |
WebSocket menyediakan mesej tersusun, bukan exactly-once peringkat perniagaan, baris gilir luar talian, atau penyegerakan keadaan. Semantik tersebut tergolong dalam protokol aplikasi. Jika produk hanya memerlukan tolak pelayan satu arah yang boleh menanggung kehilangan data, bandingkan SSE, tetapi jangan kekeliruan antara pilihan pengangkutan dengan jaminan pemulihan.
Pelan pelancaran dan bukti
Lancarkan mesin keadaan, denyutan nadi, dan backoff dengan jitter terlebih dahulu, kemudian tambahkan pemulihan kursor dan arahan idempoten. Lakukan latih tubi perubahan rangkaian, but semula pelayan, mod tidur pelayar, tamat tempoh token, dan pemutusan sambungan serentak banyak klien. RFC 6455 mengesyorkan kelewatan awal rawak dan backoff yang meningkat selepas penutupan tidak normal; MDN mendokumentasikan peristiwa WebSocket pelayar dan readyState.
Kriteria keluar rintis
Halaman pulih tanpa memuat semula; arahan yang diperakui tidak diduplikasi atau hilang; puncak sambung semula tidak membebankan pelayan; halaman tersembunyi tidak memegang soket yang tidak perlu; dan pengguna boleh melihat keadaan sambungan serta masa penyegerakan terakhir. Sebarang kegagalan bermakna membetulkan protokol atau dasar kitaran hayat terlebih dahulu.
Cara membuktikan peningkatan adalah nyata
Bandingkan kejayaan pemulihan, masa pemulihan p95, kadar peristiwa pendua, jurang kursor, puncak jabat tangan, dan tenaga mudah alih sebelum dan selepas pelaksanaan. Bahagikan mengikut rangkaian, pelayar, dan keterlihatan halaman supaya purata tidak menyembunyikan kegagalan pada satu platform tertentu.
Kesilapan biasa dan tindakan susulan
Menyambung semula serta-merta dalam onclose
Apabila pelayan dibut semula, setiap klien mendail pada masa yang sama. Gunakan backoff, jitter, dan petunjuk pendikit pelayan, serta tetapkan semula percubaan hanya selepas sambungan stabil.
Bergantung hanya pada close
Laluan separuh buka mungkin tidak pernah mengeluarkan peristiwa close. Gunakan denyutan nadi dan tarikh akhir, kemudian tutup soket lapuk itu sendiri.
Menganggap sambung semula yang berjaya bermakna data lengkap
Pemulihan sambungan tidak memainkan semula peristiwa yang terlepas semasa luar talian. Gunakan kursor yang terakhir dilihat, versi syot kilat, dan pemeriksaan jujukan bersebelahan.
Bagaimanakah anda menghalang arahan pendua?
Berikan setiap arahan kunci keidempotenan klien, simpan hasil pelayan, dan alih keluar item baris gilir hanya selepas perakuan atau pertanyaan hasil.
Bagaimana jika token tamat tempoh semasa sambung semula?
Segarkan semula dan lakukan jabat tangan sekali lagi. Penutupan tanpa kebenaran menghentikan percubaan semula dan memasuki aliran log masuk; ia bukan ralat rangkaian sementara.
Bagaimanakah anda mengawal sambungan merentas tab?
Gunakan BroadcastChannel atau SharedWorker supaya satu tab memiliki soket dan tab lain melanggan. Pindahkan pemilikan apabila pemilik ditutup dan sahkan semula kursor.