Speedbump - TCP Proxy ที่รองรับ latency แบบปรับเปลี่ยนได้
(github.com/kffl)- Speedbump คือ TCP proxy ที่เขียนด้วย Go ซึ่งเพิ่ม network latency แบบปรับเปลี่ยนได้ให้กับ TCP traffic ที่ถูก proxy เพื่อจำลองสถานการณ์ความหน่วง
- สามารถเพิ่มองค์ประกอบ latency ในรูปแบบ คลื่นไซน์, คลื่นฟันเลื่อย, คลื่นสี่เหลี่ยม, คลื่นสามเหลี่ยม เข้าไปใน latency พื้นฐานได้ และสามารถผสมองค์ประกอบ latency หลายแบบพร้อมกันได้
- ตัวอย่างแสดงคอนฟิกที่ proxy traffic ไปยัง
localhost:80ผ่านพอร์ต2000พร้อมกำหนด latency พื้นฐาน100ms, แอมพลิจูดคลื่นไซน์100msและคาบ1m - การติดตั้งทำได้ด้วยการดาวน์โหลดไบนารีที่ build ไว้ล่วงหน้าตามแต่ละ release, ใช้
go buildจากซอร์ส หรือรัน container imagekffl/speedbump - นอกจาก CLI แล้วยังสามารถใช้เป็น library ผ่านแพ็กเกจ
libของ Go ได้ และปรับขนาดบัฟเฟอร์, ขนาดคิวหน่วงเวลา, log level, host และ port สำหรับ listen ฯลฯ ผ่านอาร์กิวเมนต์ได้
Proxy สำหรับจำลองความหน่วง TCP
- Speedbump คือ TCP proxy ที่เขียนด้วย Go และสามารถจำลอง network latency แบบปรับเปลี่ยนได้
- เป้าหมายที่จะ proxy ระบุด้วยอาร์กิวเมนต์
<destination>ของ CLI โดยระบุรูปแบบไว้เป็นhost:post - การทำงานพื้นฐานคือ proxy TCP traffic ไปยังปลายทางพร้อมเพิ่มความหน่วงตามที่ตั้งค่าไว้
วิธีติดตั้งและรัน
- วิธีติดตั้งที่ง่ายที่สุดคือดาวน์โหลด ไบนารีที่ build ไว้ล่วงหน้า ซึ่งแนบอัตโนมัติใน
Assetsของแต่ละ release - หากต้องการ build จากซอร์ส ให้ clone repository แล้วรัน
go build - หากต้องการรันด้วย container สามารถใช้อิมเมจ kffl/speedbump ได้
ตัวอย่างการใช้งานพื้นฐาน
- สามารถ listen ที่พอร์ต
2000และ proxy TCP traffic ไปยังlocalhost:80พร้อมใช้ความหน่วงพื้นฐาน100ms, แอมพลิจูดคลื่นไซน์100msและคาบ1m- คอนฟิกนี้ทำให้เกิด latency เพิ่มเติมสูงสุด
200msและ latency เพิ่มเติมต่ำสุด0 - ตัวอย่างการรันคือ
speedbump --latency=100ms --sine-amplitude=100ms --sine-period=1m --port=2000 localhost:80
- คอนฟิกนี้ทำให้เกิด latency เพิ่มเติมสูงสุด
- สามารถรันคอนฟิกเดียวกันด้วย container image ได้เช่นกัน
- ตัวอย่างคือ
docker run --net=host kffl/speedbump:latest --latency=100ms --sine-amplitude=100ms --sine-period=1m --port=2000 localhost:80
- ตัวอย่างคือ
- สามารถตั้งค่าองค์ประกอบความหน่วงแบบคลื่นฟันเลื่อยได้ด้วย
- ตัวอย่างคือคอนฟิก latency พื้นฐาน
300ms, แอมพลิจูดคลื่นฟันเลื่อย200ms, คาบ2m, พอร์ต2000, ปลายทางlocalhost:80 - คำสั่งรันคือ
speedbump --latency=300ms --saw-amplitude=200ms --saw-period=2m --port=2000 localhost:80
- ตัวอย่างคือคอนฟิก latency พื้นฐาน
การผสมองค์ประกอบ latency
- Speedbump สามารถใช้องค์ประกอบ latency หลายแบบพร้อมกันได้
- README มีตัวอย่างกราฟความหน่วงที่ผสมคลื่นฟันเลื่อยกับคลื่นไซน์
อาร์กิวเมนต์ CLI และการใช้งานเป็น library
speedbump --helpแสดงวิธีใช้ในรูปแบบspeedbump [<flags>] <destination>- การตั้งค่าเครือข่ายหลักมีดังนี้
--host: IP หรือ hostname ที่จะ listen หากไม่ระบุจะ bind กับทุก network interface--port: พอร์ตที่ listen ค่าเริ่มต้นคือ8000--buffer: ขนาดบัฟเฟอร์ที่ใช้สำหรับอ่าน TCP ค่าเริ่มต้นคือ64KB--queue-size: ขนาดคิวหน่วงเวลาที่เก็บบัฟเฟอร์อ่าน ค่าเริ่มต้นคือ1024
- มีค่าเริ่มต้นที่เกี่ยวกับความหน่วงและตัวเลือก waveform
--latency: latency พื้นฐานที่เพิ่มให้กับ traffic ที่ถูก proxy ค่าเริ่มต้นคือ5ms--sine-amplitude,--sine-period: แอมพลิจูดและคาบของ latency แบบคลื่นไซน์--saw-amplitude,--saw-period: แอมพลิจูดและคาบของ latency แบบคลื่นฟันเลื่อย--square-amplitude,--square-period: แอมพลิจูดและคาบของ latency แบบคลื่นสี่เหลี่ยม--triangle-amplitude,--triangle-period: แอมพลิจูดและคาบของ latency แบบคลื่นสามเหลี่ยม
- รวมถึงตัวเลือกด้านการปฏิบัติการ
--log-level: ระดับ log โดยค่าที่ใช้ได้คือDEBUG,TRACE,INFO,WARN,ERROR--version: แสดงเวอร์ชันของแอปพลิเคชัน
- Speedbump สามารถใช้เป็น library ของ Go ได้ด้วย โดยให้ผ่านแพ็กเกจ
lib - License คือ Apache 2.0 License
1 ความคิดเห็น
ความคิดเห็นบน Hacker News
ผมเคยลองมองหาอะไรคล้าย ๆ กันเพื่อทดสอบ ActivityPub implementation หลายตัวในสเกลและเงื่อนไขเครือข่ายที่ต่างกัน แต่กลายเป็นว่าในเครื่องผมมีทุกอย่างที่ต้องใช้ติดตั้งอยู่แล้วผ่าน
tcบนดิสโทรของผมมันอยู่ในแพ็กเกจ iproute2 และมีคำอธิบายอยู่ที่นี่ด้วย: https://wiki.archlinux.org/title/advanced_traffic_control
ถ้าจะเพิ่มดีเลย์ให้กับอินเทอร์เฟซหนึ่ง ๆ ก็รันประมาณ
tc qdisc add dev eth0 root netem delay 100msใช้ง่าย ทำงานได้ดีใน Docker container ด้วย และสามารถใส่เงื่อนไขอย่างดีเลย์ แพ็กเก็ตสูญหาย แพ็กเก็ตซ้ำ ฯลฯ ได้ แถมอาจติดตั้งอยู่แล้วก็ได้
tc/netem/tbfดีมากจริง ๆ ผมเคยทำ Python GUI ง่าย ๆ ครอบมัน แล้วรันบน Pi ที่ใส่เคสจอสัมผัสไว้ เป็นกล่องดำเล็ก ๆ ที่มีปุ่มอย่าง “ดรอปแพ็กเก็ต: [0%] [1%] [10%] [50%] / ทำให้แพ็กเก็ตเสียหาย: ...” ลูกค้าค่อนข้างทึ่งเลยน่าแปลกที่ไม่ค่อยเห็นฟรอนต์เอนด์แบบเดียวกันในรูปสินค้า hardware เชิงพาณิชย์ ถ้าผมไม่ได้ค้นพลาด ก็ดูเหมือนตลาดจะยังไม่มี
tcคือถ้าจะใช้กับ แพ็กเก็ตขาเข้า จะค่อนข้างแปลกและยุ่งยากเมื่อก่อนผมเคยทำอีมูเลเตอร์เองเพื่อเลียนแบบเทอร์มินัลดาวเทียมเชิงพาณิชย์บางรุ่น เทอร์มินัลนั้นจะคิวแพ็กเก็ตไว้ แล้วเมื่อถึง threshold บางอย่างหรือเกิน timeout ก็จะปล่อยออกมาเป็นก้อน และมันพยายาม “ใจดี” ด้วยการจัดเรียงแพ็กเก็ตเล็ก ๆ ไปไว้หน้าคิวเพื่อลด latency แต่ TCP stack ไม่ชอบแบบนั้นมาก ๆ
tcทำแบบนั้นไม่ได้น่าจะมีประโยชน์พอสมควรในการจำลองผลกระทบจากสภาพอากาศของดาวเทียม/RF ที่เปลี่ยนไปตามเวลา
สิ่งที่ Netflix ทำก็คืออันนี้เลย ชื่อว่า latency monkey
พวกเขาพบว่าการตัดสินว่า service ย่อย “ช้า” หรือไม่นั้นยากกว่าการตัดสินว่า “ใช้ไม่ได้” มาก และมันจึงเป็นวิธีสำคัญในการทดสอบว่า service รับมือกับการช้าลงและปัญหาเครือข่ายอย่างไร
implementation นั้นเรียบง่ายมาก คือดรอปแพ็กเก็ตตามสัดส่วนที่ตั้งค่าได้ ซึ่งจะบังคับให้มีการ retransmit ทำให้อีกฝั่งได้รับแพ็กเก็ตล่าช้าและลำดับสลับกัน
สุดท้ายก็เจอปัญหามากมายใน โค้ดจัดการ error ที่เกี่ยวกับการเข้าถึงเครือข่าย
ผมคิดว่า software engineer ทุกคนที่สร้างแอปอินเทอร์เน็ตแบบ interactive ควรใช้เครื่องมือแบบนี้เป็นประจำในงานประจำวัน ไม่ใช่แค่ TCP แต่ควรมี QUIC ด้วย และถ้าจะครอบ DNS ให้ได้ อุดมคติคือควรรวม UDP ทั้งหมดด้วย
ผมมั่นใจว่า 90% ของการบวมเกินจำเป็นของเว็บแอปคงหายไป ถ้าคนสร้างไม่ได้ใช้แต่สภาพแวดล้อมคอมพิวติ้งแบบ Cadillac ชุบทอง
https://firefox-source-docs.mozilla.org/devtools-user/networ...
แน่นอนว่านี่ใช้ได้เฉพาะการทดสอบฝั่งฟรอนต์เอนด์ที่อิงเบราว์เซอร์เท่านั้น
แอปจำนวนมากทำงานได้แย่มากในสภาพแวดล้อมที่การเชื่อมต่อเครือข่าย หลุดเป็นช่วง ๆ เช่น สถานการณ์บรรเทาภัยพิบัติ
ถ้านักพัฒนาแอปมากขึ้นจำลองและทดสอบการเชื่อมต่อแบบติด ๆ ดับ ๆ ก็น่าจะช่วยคนอื่นได้
จาก “Toxiproxy is a framework for simulating network conditions” (2021) https://news.ycombinator.com/item?id=29084277#29088775:
เพราะกำลังส่งแต่แพ็กเก็ตขนาด 50 ไบต์ และในนั้นหายไป 1 ใน 10 ระหว่างนั้น thread หนึ่งของเซิร์ฟเวอร์ก็ไม่ได้ทำงานที่มีประโยชน์อะไรเลย
บน Mac ใช้แค่เครื่องมือที่มีมาให้ก็ทำแบบเดียวกันได้
มีโปรเจกต์หนึ่งที่ไม่ได้ active มาสักพักแล้ว แต่แค่ชื่อก็บอกอะไรได้มาก: https://github.com/tylertreat/comcast
เมื่อไม่นานมานี้ผมพยายามจำลองเครือข่ายช้าบน Mac แล้วเจอ Network Link Conditioner ซึ่งค่อนข้างดี ไม่ต้องตั้งค่า proxy อะไรเลย
ต้องติดตั้งจากเครื่องมือเพิ่มเติมของ Xcode
https://nshipster.com/network-link-conditioner/
เครื่องมือยอดเยี่ยมของ Shopify อย่าง toxiproxy ก็น่าดู: https://github.com/Shopify/toxiproxy
นี่ยังเป็นวิธีที่ดีมากในการทดสอบ networking library ที่คุณ implement เองด้วย เพราะ stack ควรรับมือกับสถานการณ์เลวร้ายส่วนใหญ่ได้อย่างถูกต้อง
แนวคิดเรื่อง ‘chaos engineering’ นั้นเจ๋งดี
ผมกำลังพัฒนา progress bar สำหรับเว็บครอว์เลอร์ แต่ถ้าทดสอบบน localhost มันเร็วเกินไปจนดูยากว่ามีปัญหาหรือเปล่า
พอใช้ speedbump ก็แค่รัน
podman run --net=host kffl/speedbump:latest --latency=1s --port=8001 localhost:8000แล้วทดสอบครอว์เลอร์ที่ http://localhost:8001 ได้เลยเป็นเครื่องมือที่เรียบร้อยดี
มีเครื่องมือคล้าย ๆ กันที่ผมเคยใช้บน Windows
https://jagt.github.io/clumsy/
FreeBSD ก็มี dummynet เป็นส่วนหนึ่งของ ipfw ซึ่งสามารถฉีด delay, จำกัด bandwidth, กำหนด queue size และ packet loss ได้ เป็นฟีเจอร์เดียวกับที่มีบน MacOS
tcบน Linux หรือเปล่า?