กระรอก Go สีฟ้าเขียวทดลองจัดข้อมูลบนโต๊ะทำงาน โทนสว่างสีมิ้นต์และครีม

Go Serialization 06: HTTP API และ benchmark ที่อ่านผลได้

พี่โชว์Engineering

บทสุดท้ายเราจะเชื่อม codec เข้ากับ HTTP และแยกให้ชัดว่าตัวเลขใดตอบคำถามอะไรครับ เวลาของ encode อย่างเดียวไม่รวมคิว เครือข่าย gzip หรือการอ่านข้อมูลฝั่ง client

เตรียมโปรเจกต์

ดาวน์โหลด โปรเจกต์ Go พร้อม schemas, generated code, tests และผลดิบ แล้วแตกไฟล์ ใช้ Go 1.27 ขึ้นไป เปิด terminal ในโฟลเดอร์ go-api-serialization และรัน go mod download ครั้งแรก ไม่ต้องติดตั้ง compiler ของ schema เพื่อเริ่มเรียน

โค้ดบนหน้านี้เป็นตัวอย่างบางส่วน (fragment) เพื่ออธิบายจุดสำคัญ คำสั่ง shell ตรวจ syntax ส่วน โปรเจกต์เต็มตรวจด้วย go test ./... ใน quality gate ไม่ควรนำ fragment ไปรันเป็นโปรแกรมเดี่ยว

1. เปิด API

go run ./cmd/server

เปิด terminal อีกหน้าต่างในโฟลเดอร์เดิม:

curl 'http://127.0.0.1:8090/catalog/json?count=1'
curl --compressed 'http://127.0.0.1:8090/catalog/json?count=100&compression=gzip'
go test -run TestHTTP -v

เปลี่ยน path เป็น /catalog/msgpack, /catalog/protobuf, /catalog/avro หรือ /catalog/flatbuffers เพื่อรับ bytes ของ format นั้น ผลที่ตรวจได้คือ HTTP 200 พร้อม Content-Type ที่ตรง และ client decode กลับได้ครบ tests ยังครอบคลุม count ผิด, route ผิด และ method ที่ไม่รองรับด้วย

Fixtures เตรียมไว้ก่อนรับคำขอ แต่ server encode ใหม่ทุก request ไม่ cache response ส่วนการเลือก gzip ทำผ่าน query ชัดเจนเพื่อไม่ให้ client เปิด compression ให้อัตโนมัติโดยไม่รู้ตัว

2. วัด codec และ HTTP แยกไฟล์

go test -run '^$' -bench . -benchmem -benchtime=300ms -count=5 > results/codec.txt
go run ./cmd/sizes > results/sizes.csv
go run ./cmd/bench -rounds=5 -duration=1s -concurrency=8 > results/http.csv
python3 summarize.py

คำสั่ง HTTP ที่ไม่ส่ง -url จะเปิด loopback server ใน process เดียวกับ client ก่อนวัด มี warm-up 20 requests ต่อกรณี สลับลำดับ format ระหว่างรอบ และนับว่าคำขอสำเร็จก็ต่อเมื่ออ่าน body และ decode ได้ครบแล้ว ตัวเลขจึงรวมทั้งสองฝั่งบนเครื่องเดียว ไม่มี TLS หรือ network ระยะไกล

วัด gzip เพิ่มเป็นไฟล์อีกชุดได้ดังนี้:

go run ./cmd/bench -rounds=5 -duration=1s -concurrency=8 -compression=gzip > results/http-gzip.csv

3. อ่านผลโดยไม่สรุปเกินข้อมูล

ns/op และ allocations มาจาก microbenchmark ส่วน requests/s, p50 และ p95 มาจาก HTTP client p95 คือค่าที่ 95% ของคำขอสำเร็จในรอบนั้นใช้เวลาไม่เกิน ค่ากลาง p95 ของ 5 รอบไม่ใช่ p95 จากการรวมคำขอทั้งหมด

HTTP generator นี้เป็น closed-loop: ยิ่ง response ช้า อัตราเริ่มคำขอใหม่ยิ่งลด จึงเหมาะกับการทดลองภายในและยังไม่ใช่ capacity test ของ production หากจะวางแผนรองรับ traffic ให้ใช้เครื่อง client/server แยกกัน พร้อม load แบบ arrival rate คงที่และข้อมูลจริง

ผลใหม่ของ Workshop — 6 กันยายน 2026

เครื่อง Apple M5 Pro · macOS 26.6.2 · go version go1.27.1 darwin/arm64 · 18 logical CPUs · RAM 48 GiB ใช้ library ตามเวอร์ชันใน go.mod และรายละเอียดใน results/environment.json

ตารางชุด 1,000 สินค้า: encode/decode และ HTTP เป็น median ของ 5 รอบ ขนาดวัดจาก payload deterministic ก่อน/หลัง gzip default ของ Go:

FormatRaw / gzip bytesEncode µsDecode ทั้งชุด µsHTTP req/sp50 / p95 ms
json169,241 / 9,653237.01719.14855.899.303 / 11.139
msgpack154,013 / 8,926196.84287.521,284.136.203 / 7.726
protobuf111,976 / 7,396158.64211.031,582.484.996 / 6.634
avro106,988 / 6,76688.58120.321,634.054.836 / 6.894
flatbuffers180,056 / 17,079265.75207.141,551.105.100 / 7.131

ผลครบทั้ง 1, 100, 1,000 รายการและ allocations อยู่ใน results/REPORT.md, medians.json และไฟล์ดิบใน ZIP ค่า HTTP ในตารางใช้ ไม่บีบอัด คอลัมน์ gzip เป็นการวัดขนาดแยก ไม่ได้หมายความว่า latency รวม compression แล้ว

ผลนี้รวม validation และการแปลง generated objects ส่วน direct field access ของ FlatBuffers อยู่ในแถวแยกของรายงาน เครื่องเดียวและการรันสั้นอาจมี scheduling noise จึงควรทำซ้ำบนสภาพแวดล้อมจริงก่อนเลือกใช้

แบบฝึกหัด

รันซ้ำด้วย concurrency 1 แล้ว 16 เก็บ raw CSV แยกชื่อ เปรียบเทียบทั้ง throughput และ p95 โดยไม่เลือกเฉพาะรอบที่ดีที่สุด จากนั้นเปิด gzip แล้วดูว่าขนาดที่ประหยัดคุ้ม CPU เพิ่มหรือไม่ หากเครื่องร้อนหรือมีงานอื่นแย่ง CPU ให้บันทึกเงื่อนไขและรันใหม่ทั้งชุด

ก่อนตัดสินใจเปลี่ยน production ให้นำ payload จริงที่ลบข้อมูลส่วนบุคคลแล้วมาทดสอบ และรวมต้นทุน schema rollout, debugging, client libraries และ caching ด้วย JSON อาจยังเป็นคำตอบที่เหมาะ แม้บาง microbenchmark จะชนะด้วย binary ครับ

อ่านต่อ