ข้ามไปยังเนื้อหา

วงจรชีวิตของ Connection

จนถึงตอนนี้เราปฏิบัติต่อ WebSocket ราวกับว่า มีอยู่ เฉย ๆ คุณเปิด คุณส่ง คุณรับ แต่ connection จริง ๆ ไม่ได้เรียบร้อยขนาดนั้น connection ถือกำเนิด มีชีวิตอยู่ระยะหนึ่ง บางครั้งก็ตายไปโดยไม่เตือนล่วงหน้า และ — ถ้าแอปของคุณดีพอ — ก็ฟื้นคืนชีพกลับมาเอง โมดูลนี้ว่าด้วยวงจรชีวิตทั้งหมดนั้น และหน้านี้คือแผนที่

ลองคิดถึง connection หนึ่งตัวเหมือนตัวละครที่มีเรื่องราว:

  • Connecting — handshake กำลังดำเนินอยู่ ยังส่งอะไรไม่ได้
  • Live — socket เปิดอยู่ ข้อความไหลไปมาทั้งสองทิศทาง นี่คือสถานะที่มีความสุข และโค้ดส่วนใหญ่ของคุณก็สมมติว่าคุณอยู่ในสถานะนี้
  • Dropped — เน็ตเวิร์กกะพริบหายไปแวบหนึ่ง proxy timeout หรือ server รีสตาร์ต connection ตายไปแล้ว แต่ — และนี่คือกับดัก — codeของคุณอาจยังไม่ได้รับแจ้ง
  • Reconnecting — เมื่อตัดสินใจแล้วว่า connection หายไป client จะรอสักครู่แล้วลองใหม่ จากนั้นรอนานขึ้น แล้วก็นานขึ้นอีก จนกว่าจะสำเร็จหรือยอมแพ้
  • Closed — การปิดอย่างตั้งใจและสะอาดหมดจด ฝ่ายหนึ่งบอกลา อีกฝ่ายตอบรับ แล้วสายก็ถูกปลดระวางอย่างจงใจ

แนวคิดที่สำคัญที่สุดในโมดูลนี้คือความแตกต่างระหว่าง dropped และ closed connection ที่ closed จบลงเพราะมีคนตั้งใจให้จบ ส่วน connection ที่ dropped จบลงโดยอุบัติเหตุ — และการตรวจจับอุบัติเหตุนั้นอย่างน่าเชื่อถือกลับยากอย่างน่าประหลาดใจ เพราะ connection ที่พังสามารถดูเหมือน connection ที่เงียบสงบและแข็งแรงได้เป๊ะ ๆ

stateDiagram-v2
  [*] --> Connecting
  Connecting --> Live: handshake ok
  Connecting --> Reconnecting: handshake failed
  Live --> Dropped: heartbeat missed / network lost
  Live --> Closed: clean close (1000)
  Dropped --> Reconnecting: schedule retry
  Reconnecting --> Connecting: backoff elapsed
  Reconnecting --> Closed: gave up
  Closed --> [*]
ชีวิตของ WebSocket connection หนึ่งตัว

อ่านไดอะแกรมนี้เหมือนคำสัญญาเกี่ยวกับcodeของคุณ: ทุกลูกศรคือสิ่งที่คุณต้องจัดการ ลูกศร Live to Dropped คือลูกศรที่แอปส่วนใหญ่ลืม เพราะ WebSocket ของเบราว์เซอร์จะไม่ยิง onclose ทันทีเสมอไปเมื่อเน็ตเวิร์กตาย — ซึ่งนั่นคือเหตุผลที่บทเรียนถัดไปมีอยู่

โมดูลนี้ — Connection Lifecycle — เปลี่ยนแต่ละลูกศรข้างบนให้กลายเป็นcodeที่ใช้งานได้จริง:

  1. Connection lifecycle (คุณอยู่ที่นี่) — วงจรชีวิตทั้งหมดในรูปแบบ state machine เดียว
  2. Heartbeats — ทำไม dead connection ถึงดูเหมือนยังมีชีวิต และ ping/pong พิสูจน์การหายใจได้อย่างไร
  3. Detecting dead connections — เปลี่ยน heartbeat ที่หายไปให้กลายเป็นความมั่นใจว่า “อันนี้ไปแล้ว ปิดทิ้งเลย”
  4. Reconnection and backoff — กลับมาอัตโนมัติโดยไม่กระหน่ำ server ที่กำลังดิ้นรน
  5. Close codes and resume — close handshake ที่สะอาด ความหมายของ code ตัวเลขต่าง ๆ และการเล่นซ้ำ state ที่คุณพลาดไป

เมื่อจบโมดูล คุณจะได้ connection ที่รักษาตัวเองได้: รู้ตัวว่าตายไปแล้ว backoff อย่างสุภาพ reconnect แล้วตามเก็บทุกอย่างที่พลาดไประหว่างที่หายไป

ก่อนจะผ่าทีละขั้น ลองดู connection ตัวหนึ่งใช้ชีวิตครบทั้งวงจรในการรันครั้งเดียว เดโมด้านล่างใช้ socket ในหน้าที่มี WebSocket API จริง — onopen/onmessage/onclose เดียวกัน — ที่เปิดขึ้น รันสักครู่ ถูก drop และถูก reconnect แล้วในที่สุดก็ถูก close อย่างตั้งใจ ดู console บรรยายแต่ละสถานะ

JavaScript

คุณควรจะเห็น: connecting, live, dropped (code 1006 ซึ่งคือการจบแบบผิดปกติ) จากนั้น connecting อีกครั้ง live อีกครั้ง และสุดท้าย close อย่างสะอาดด้วย code 1000 การรันครั้งเดียวนั้นคือโมดูลทั้งหมดนี้ในเวอร์ชันย่อ — ทุกบทเรียนต่อจากนี้จะซูมเข้าไปยังหนึ่งใน transition เหล่านั้น

อะไรคือความแตกต่างสำคัญระหว่าง connection แบบ "dropped" และ "closed"?
ทำไม transition แบบ "live to dropped" ถึงเป็นอันที่แอปพลาดบ่อยที่สุด?
ในเดโม connection หนึ่งตัวเคลื่อนผ่านลำดับสถานะใด?