- สำหรับวิดีโอสดและการสนทนาผ่านอินเทอร์เน็ต สิ่งสำคัญไม่ใช่แค่ “การส่งแบบไม่รับประกัน” แต่คือการทิ้งข้อมูลเก่าและส่งข้อมูลล่าสุดให้ถึงทันเวลา
- หากเราเตอร์ไม่ทิ้งแพ็กเก็ตตอนเกิดความหนาแน่น แต่ปล่อยให้ค้างในคิวนาน จะเกิด bufferbloat ซึ่งในบริการเรียลไทม์อาจทำให้เกิดความหน่วงที่เลวร้ายกว่าการบัฟเฟอร์
- ถ้าสร้างโปรโตคอลบน UDP โดยตรง ต้องเขียนการส่งซ้ำ การควบคุมความหนาแน่น การเข้ารหัส การประเมิน RTT การตรวจสอบเส้นทาง การควบคุมการไหล ฯลฯ ใหม่ทั้งหมด จึงปลอดภัยกว่าที่จะใช้ไลบรารี QUIC
- ใช้แค่ QUIC ก็สามารถสร้างความสามารถในการส่งข้อมูลให้ทันเวลาได้โดยไม่ต้องพึ่ง datagram ด้วยการผสานการควบคุมความหนาแน่นแบบอิงความหน่วง การแยกสตรีมอย่างอิสระ และการกำหนดลำดับความสำคัญ
- แม้มาตรฐาน QUIC, WebTransport และ MoQ จะมีการรองรับ datagram อยู่ แต่ข้อสรุปคือควรไปในทิศทางของ Media over QUIC แทนการสร้างโปรโตคอลวิดีโอใหม่ซ้อนบน UDP อีกชั้น
เป้าหมายไม่ใช่ “ความไม่เสถียร” แต่คือความทันเวลา
- การเลือก TCP หรือ UDP มักถูกอธิบายว่าเป็นเรื่องของ “การส่งแบบเชื่อถือได้” กับ “การส่งแบบไม่เชื่อถือได้” แต่สิ่งที่แอปพลิเคชันต้องการจริง ๆ ไม่ใช่ความไม่เสถียรนั้นเอง
- ในวิดีโอสดหรือการสนทนาผ่านอินเทอร์เน็ต การได้รับข้อมูลล่าสุดก่อน สำคัญกว่าการได้รับข้อมูลเก่าทั้งหมดครบถ้วน
- ในการสนทนาแบบเรียลไทม์ ควรหลีกเลี่ยงสถานการณ์ที่มีวงล้อบัฟเฟอร์ขึ้นบนใบหน้า หรือได้ยินเสียงเมื่อ 5 วินาทีก่อน
- อุตสาหกรรมวิดีโอสดและเกมจึงมักใช้ UDP datagram แทน TCP stream เพื่อให้ได้ความทันเวลาแบบนี้
Datagram และคิวในเครือข่าย
- Datagram คือซองของ 0 และ 1 ที่ส่งจากต้นทางไปยังปลายทาง และโดยทั่วไปถือว่า 1200 ไบต์ เป็นขนาดที่ปลอดภัย
- Datagram อาจสูญหายแบบเงียบ ๆ ได้ และอาจมาถึงโดยสลับลำดับกัน
- ในชั้นกายภาพ ข้อมูลจะถูกแปลงเป็นสัญญาณแอนะล็อกแล้วเดินทางผ่านสื่อ ทำให้เกิดการ serialize, deserialize, buffering, queueing, retransmission, discard, corruption, delay, reordering, duplication และ loss ได้
- เมื่อมีข้อมูลเข้าสู่เครือข่ายมากเกินไป เราเตอร์จะทิ้งข้อมูลที่ขอบเขตของแพ็กเก็ต ไม่ใช่ทิ้งบิตแบบสุ่ม
Bufferbloat และการควบคุมความหนาแน่น
- หากเราเตอร์ไม่ทิ้งแพ็กเก็ตทันทีแต่สะสมไว้ในคิว จะเกิด bufferbloat
- bufferbloat สามารถทำให้ทุกแพ็กเก็ตล่าช้าไปหลายวินาที ซึ่งเป็นสถานการณ์ที่เลวร้ายที่สุดสำหรับการส่งข้อมูลแบบเรียลไทม์
- หากต้องการหลีกเลี่ยงการคิว จำเป็นต้องประเมินการคิวของเราเตอร์จากฟีดแบ็กเรื่องเวลาที่แพ็กเก็ตมาถึง แล้วให้ฝั่งส่งลดอัตราการส่งเพื่อเคลียร์คิว
- เรื่องนี้อยู่ในขอบเขตของการควบคุมความหนาแน่น และการส่งแพ็กเก็ตด้วยความเร็วไม่จำกัดอาจนำไปสู่หายนะได้
สิ่งที่ต้องแบกรับเมื่อสร้างบน UDP โดยตรง
- หากจะใช้ UDP โดยตรงเพื่อสร้างโปรโตคอลขนส่ง อย่างน้อยต้องมีความสามารถต่อไปนี้
- หากต้องการให้เป็นโปรโตคอลที่ดีกว่าเดิม ยังต้องมีความสามารถเหล่านี้ด้วย
- หากต้องการให้พร้อมใช้งานจริง ยังต้องคิดถึงสภาพแวดล้อมและการดีพลอยด้วย
- โปรโตคอลวิดีโอสดอย่าง WebRTC, SRT, Sye, RIST เป็นตัวอย่างของสิ่งที่สร้างบน UDP
- จึงนำไปสู่ข้อสรุปว่าควรใช้ไลบรารี QUIC แทนการสร้างโปรโตคอลใหม่เองโดยตรง
สร้างความทันเวลาด้วย QUIC stream
- วิธีทำให้ QUIC ส่งข้อมูลได้ทันเวลาสรุปได้ 3 ข้อ
- หลีกเลี่ยงการพองตัวของบัฟเฟอร์: การควบคุมความหนาแน่นแบบอิงความหน่วงอย่าง BBR สามารถตรวจจับการคิวและลดปริมาณการส่งได้
- transport-wide-cc ของ WebRTC อาจถือเป็นตัวอย่างของวิธีที่ดีกว่า
- การแยกสตรีม: ไบต์ภายในแต่ละสตรีมยังคงมีลำดับและส่งอย่างเชื่อถือได้ โดยสตรีมสามารถเป็นหน่วยอะตอมอย่างเฟรมวิดีโอ การอัปเดตเกม ข้อความแชต หรือ JSON blob
- การกำหนดลำดับความสำคัญของสตรีม: สตรีมเป็นอิสระต่อกัน จึงมาถึงโดยไม่ต้องสัมพันธ์กับลำดับรวม และสามารถสั่งให้ QUIC stack ส่งสตรีมที่สำคัญก่อน
- สตรีมที่มีลำดับความสำคัญต่ำอาจถูกปล่อยให้รอ และหากต้องการหลีกเลี่ยงการสิ้นเปลืองแบนด์วิดท์ก็สามารถปิดทิ้งได้
- แนวทางนี้คือแก่นของ Media over QUIC
- คุณสมบัติแบบ fire-and-forget ของ datagram เหมาะเฉพาะเมื่อจำเป็นต้องลดความหน่วงแบบเรียลไทม์จริง ๆ เท่านั้น นอกเหนือจากนั้นสามารถใช้ QUIC stream ได้
ข้อแลกเปลี่ยนและข้อยกเว้นรอบ ๆ datagram
- QUIC และมาตรฐานที่เกี่ยวข้องก็รวมการรองรับ datagram ไว้ด้วย
- QUIC ให้การรองรับ datagram ผ่านส่วนขยาย
- WebTransport กำหนดให้ต้องรองรับ datagram
- MoQ เวอร์ชันล่าสุด เพิ่มการรองรับ datagram
- MoQ เวอร์ชันถัดไปมีแผนจะบังคับให้รองรับ datagram
- การรองรับ datagram อาจถูกรวมไว้เพราะติดตั้งใช้งานได้ไม่ยากและเอื้อต่อการทดลอง
- OPUS มี FEC มาในตัว จึงเป็นตัวอย่างที่ MoQ สามารถรองรับการส่ง “frame” เสียงแต่ละชิ้นเป็น datagram ได้
- สำหรับโปรโตคอลเก่าอย่าง DNS อาจถือเป็นข้อยกเว้นได้ แต่สำหรับการออกแบบใหม่ ผู้เขียนมองว่าควรไปในทิศทางอย่าง DNS over HTTPS
- ข้อสรุปคือ แทนที่จะสร้างโปรโตคอลวิดีโอใหม่บน UDP อีกครั้ง ควรเข้าร่วมกับ Media over QUIC
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ตัวอย่างเช่น ในสภาพแวดล้อมอย่าง NB-IoT ที่กรณีเลวร้ายที่สุดมีความหน่วงไป-กลับ 10 วินาที เวลาไป-กลับจะถูกเสียไปกับแฮนด์เชกและการค้นหา MTU ทั้งยังพยายามส่งข้อมูลที่ไม่เป็นประโยชน์แล้วต่อไป และเมื่อสัญญาณครอบคลุมแย่ลงจนความหน่วงเพิ่มขึ้น TCP จะมองว่าเป็นการสูญหายของแพ็กเก็ตจากความแออัดแล้วลดแบนด์วิดท์ลง
นอกจากนี้ load balancer หรือ middlebox อาจตัดการเชื่อมต่อโดยคิดว่า “ไม่มีการตอบกลับมา 4 วินาที คงหายไปแล้ว” และยังน่าเสียดายที่ TCP แบ่งแพ็กเก็ตโดยไม่คำนึงถึงโครงสร้างข้อมูล ทำให้ตีความไม่ได้จนกว่าจะได้รับครบทั้งหมด
ในบางกรณีก็มีประโยชน์ แต่หลายครั้งสามารถใช้ประโยชน์จากลำดับที่ n+1 ได้แม้ลำดับที่ n จะยังไม่มา
ในการโอนไฟล์ขนาดใหญ่ อาจใช้ erasure coding เพื่อจัดการการสูญหายของแพ็กเก็ต 5% โดยอัตโนมัติ หรือใช้ fountain code เพื่อส่งไปเรื่อย ๆ จนกว่าผู้รับจะบอกว่า “ได้รับครบแล้ว” ก็ได้
fountain code เป็นวิธีที่ยานสำรวจห้วงอวกาศลึกใช้ส่งข้อมูล และความหน่วงไปถึงดาวพฤหัสบดีหรือดาวอังคารก็ถือว่าหนักพอสมควร
ต้องให้ TCP handshake เสร็จก่อนจึงจะเริ่ม TLS handshake ได้ แต่ QUIC มีการรองรับในระดับโปรโตคอลเพื่อจัดการการเจรจา TLS ภายใน handshake เริ่มต้น
แนวทางที่ประกอบโปรโตคอลเครือข่ายกับการเข้ารหัสเข้าด้วยกันแบบหลวม ๆ อาจดูสง่างามกว่า แต่ในโลกที่แทบทุกการส่งข้อมูลถูกเข้ารหัสแล้ว ข้อดีเชิงปฏิบัติของการลดเวลาไป-กลับลงหนึ่งรอบต่อการเชื่อมต่อก็ดูสำคัญกว่า
ฝั่ง R&D สร้างระบบใหม่ที่ใช้ QUIC และแก้ปัญหาส่วนใหญ่เกี่ยวกับการมาถึงแบบสลับลำดับได้แล้ว แต่เซนเซอร์จาก third party ที่ต้องรองรับโดยตรงโดยไม่ผ่านอะแดปเตอร์รองรับได้แค่ UDP จึงยังใช้ UDP datagram กับทุกอย่างอยู่
คำที่พบบ่อยกว่าและดีกว่าคือ best-effort โดย UDP จะพยายามส่ง datagram ให้ดีที่สุด เพียงแต่ datagram อาจถูกทิ้งได้เท่านั้น
นั่นไม่ได้หมายความว่า UDP โดยเนื้อแท้แล้วเชื่อถือไม่ได้
https://en.wikipedia.org/wiki/Best-effort_delivery
ในความเป็นจริงมันไม่ได้หมายถึงการพยายามอย่างสุดทางเพื่อส่งข้อความจาก A ไป B แต่ใกล้เคียงกับ “ก็พยายามแล้วนะ” มากกว่า
หากเราเตอร์ระหว่างทางแออัด หรือเกิด black hole ราว 50ms ก่อนจะ reroute อย่างรวดเร็วเพราะ link flap ก็กลายเป็นว่า “เออ ก็ลองแล้ว”
ในทางกลับกัน reliable delivery ของ TCP จะลองส่งซ้ำหลายครั้งและให้สตรีมข้อมูลที่เรียงลำดับถูกต้องแก่แอปพลิเคชัน
reliable/unreliable ก็อาจเป็นคำที่ไม่ดี แต่ก็ยากจะบอกว่า best-effort ดีกว่า
ระบบที่ไม่รับประกันความน่าเชื่อถือทำงานได้ยอดเยี่ยมประมาณ 95% และดีต่อ raw throughput แต่ 5% สุดท้ายมักสร้างความแตกต่างอย่างมาก
“ความพยายาม” โดยทั่วไปหมายถึงความพากเพียรระดับหนึ่งเมื่อเจอความยากลำบาก แต่การทิ้งแพ็กเก็ตเมื่อมีปัญหาทรัพยากรนั้นเรียกว่าเป็นความพยายามได้ยาก และยิ่งไม่ใช่ best effort
คำว่า “best efforts” ในภาษากฎหมายและธุรกิจแม้อ่อนกว่าคำมั่นสัญญาแบบแน่นอน แต่ก็ไม่ใช่การปล่อยมืออย่างเปิดเผย ขณะที่การใช้ในงานเครือข่ายค่อนข้างต่างจากนั้นมาก
แยกต่างหากจากเรื่องนี้ checksum ของ UDP และ TCP ก็รับประกันความถูกต้องได้ไม่ดีนักเมื่อ datagram ถูกส่งถึง และดีกว่าฮาร์ดแวร์เพียงเล็กน้อยเท่านั้น
แต่ best-effort ให้ความรู้สึกเหมือนมีความพยายามบางอย่างเพื่อรับประกันการส่งถึง ทั้งที่จริง ๆ แพ็กเก็ตที่ดูผิดปกติหรือโชคร้ายไปเจอบัฟเฟอร์เต็มก็ถูกทิ้งไปเฉย ๆ
ผมชอบคำว่า lossy แต่เรื่องนี้คือปัญหาการตั้งชื่อที่เข้าข่าย “มีปัญหายากอยู่แค่สองอย่าง”
ถ้าแพ็กเก็ตต้องไปถึงก็ใช้ TCP ถ้าไม่ค่อยสำคัญก็ใช้ UDP
แม้เป็นคำอธิบายที่ย่อมาก แต่ best effort เป็นคำโง่ ๆ และในนั้นไม่มีความพยายามอยู่เลย
congestion control นั้นจำเป็นแน่นอน แต่นอกจากนั้นยังมีข้อสงสัยมาก
ถ้าเป็นโลกที่ให้ความสำคัญกับ datagram ก่อน ก็คงผูก data link หลายเส้นเข้าด้วยกันได้อย่างมีประสิทธิภาพมาก หรือโรมมิงข้ามขอบเขตเครือข่ายโดยไม่ตัดการเชื่อมต่อได้อย่างไร้ปัญหา
แอปพลิเคชันจำนวนมากสามารถจัดการเฟรมที่สลับลำดับกันได้โดยไม่มีต้นทุนเพิ่ม และถ้าเขียนให้เข้ากับโมเดล UDP ก็อาจเร็วขึ้นมาก
ในความเป็นจริง การย้ายไปใช้วิธีส่งที่มีความน่าเชื่อถือต่ำกว่าไม่ได้ทำให้ซอฟต์แวร์น่าเชื่อถือขึ้นหรือมีประสิทธิภาพขึ้นโดยอัตโนมัติ
กลับกัน มันเพิ่มโหมดความล้มเหลวและความซับซ้อนที่ทีมต้องจัดการขึ้นอย่างมาก
อย่างไรก็ตาม ถ้ามีคอขวดแคบ ๆ อยู่ที่ไหนสักแห่งในเส้นทางการเชื่อมต่อ การสร้างแพ็กเก็ตจำนวนมหาศาลที่จะถูกทิ้งที่คอขวดนั้นอยู่ดี ก็ดูจะไม่ค่อยมีความหมาย
เว็บไซต์, ออดิโอ, วิดีโอ โดยทั่วไปไม่ค่อยเข้ากับเฟรมที่ลำดับสลับกัน และคนส่วนใหญ่ก็ไม่ต้องการให้ออดิโอหรือวิดีโอกระตุก
วิดีโอเกมบางส่วนอาจมองข้ามแพ็กเก็ตที่หายไปได้ แต่กรณีแบบนั้นก็เขียนด้วย UDP อยู่แล้ว
เพราะมองว่าแพ็กเก็ตเหล่านั้นจะไม่ถูกส่งซ้ำ จึงเป็นวิธีที่มีประสิทธิภาพในการลดทราฟฟิกส่วนเกิน
ตอนนี้มีโปรโตคอลที่ส่งซ้ำอย่างก้าวร้าวบน UDP เกิดขึ้นแล้ว เลยสงสัยว่าสิ่งนี้เปลี่ยนสถานการณ์ไปอย่างไร
ยังจำได้ว่าเมื่อหลายปีก่อน QUIC เคยเจอปัญหาเรื่องการส่งซ้ำเมื่อเทียบกับ HTTP/1·HTTP/2 เพราะเรื่องนี้
ถ้าหาข้อความที่เป็นตัวแทนได้ดีกว่านี้จากบทความ ก็เปลี่ยนอีกได้
เป็นไปตามแนวทางการตั้งหัวข้อของ HN ที่ว่า “ให้ใช้หัวข้อเดิม ยกเว้นกรณีที่ชวนเข้าใจผิดหรือเป็นแนวล่อคลิก”: https://news.ycombinator.com/newsguidelines.html
ขึ้นอยู่กับแอปพลิเคชันแล้วถือว่าสมเหตุสมผล
เช่น ในเกมมัลติเพลเยอร์แบบเรียลไทม์ ถ้าการประมวลผลตามไม่ทัน รายการที่ล้าหลังก็ไม่สำคัญอีกต่อไป เพราะสถานะเกมเปลี่ยนไปแล้ว
แอปพลิเคชันซื้อขายความเร็วสูงก็เช่นกัน ในบางสถานการณ์ สิ่งสำคัญคือข้อมูลตลาดล่าสุด ไม่ใช่สิ่งที่เกิดขึ้นเมื่อ 100ms ก่อน
บทความยังยกเกมและวิดีโอถ่ายทอดสดเป็นตัวอย่างที่ UDP เหมาะสมด้วย
โครงสร้างคือเริ่มด้วยการเสนอ “ความเชื่อทั่วไป” แล้วค่อยโต้แย้งมัน
ตัวอย่างเช่น การค้นหาในเครือข่ายภายในอย่าง DHCP, SLAAC, UPnP, mDNS, tinc, BitTorrent, broadcast อย่างการสตรีมในเครือข่ายภายใน และการ encapsulate อย่าง WireGuard, IPSec, OpenVPN, VLAN
การส่งซ้ำ รวมถึงแค่การบัฟเฟอร์เพื่อจัดลำดับใหม่ก็เพิ่มความหน่วงแล้ว ดังนั้นยอมรับการสูญหายด้วยการแก้ไขข้อผิดพลาดหรือการปกปิดการสูญหายของแพ็กเก็ตจะดีกว่า
UDP กับ TCP มีพฤติกรรมและจุดแลกเปลี่ยนต่างกัน ทั้งหมดก็แค่ต้องทำความเข้าใจก่อนเลือกให้เหมาะกับกรณีใช้งาน
ไม่จำเป็นต้องมีการทำตัวเป็น gatekeeper แบบ “ห้ามทำ X เด็ดขาด”
แค่ดูหัวข้อก็ค่อนข้างชัดแล้วว่านี่ไม่ใช่บทความที่พยายามทำตัวเป็น gatekeeper ห้ามใช้ UDP
ที่จริง ตอนท้ายบทความผู้เขียนยังเสนอให้ใช้ QUIC ซึ่งมีพื้นฐานบน UDP ด้วย
แน่นอนว่าต้องดูแลรายละเอียดเองมากกว่ามาก
แถมยังเป็นวิธีที่ดีในการเรียนรู้แง่มุมระดับล่างของเครือข่ายด้วย