Protocol Buffers Intro
protobuf คือ contract
หัวข้อที่มีชื่อว่า “protobuf คือ contract”Protocol Buffers (protobuf) คือภาษาที่คุณใช้อธิบายข้อมูลและ service ของคุณ และเป็น format ที่ข้อมูลนั้นเดินทางไปด้วย ทำหน้าที่ 2 บทบาทพร้อมกัน:
- ภาษา schema — ไฟล์
.protoที่ประกาศ message และ service ของคุณในแบบที่ภาษาใดก็ compile ได้ - wire format — binary encoding ที่กระชับ ซึ่ง message เหล่านั้นใช้ตอนวิ่งข้าม network
ใน gRPC protobuf คือ contract ทุกอย่าง — type safety, codegen, ประสิทธิภาพ — ไหลมาจาก contract นี้
message คือ field ที่มีหมายเลข
หัวข้อที่มีชื่อว่า “message คือ field ที่มีหมายเลข”นี่คือ message ที่เล็กที่สุดแต่ยังน่าสนใจ:
syntax = "proto3";
message User { int64 id = 1; string name = 2; string email = 3;}= 1, = 2, = 3 คือ field number และเป็นแนวคิดที่สำคัญที่สุดใน protobuf เวลาส่งจริงบน wire นั้น protobuf ไม่ ส่งชื่อ field แต่ส่ง หมายเลข ของ field คู่กับค่า ดังนั้น name = "Ada" จึงกลายเป็นประมาณว่า “field 2, string, Ada” — tag ไม่กี่ byte บวกกับค่า
flowchart LR
subgraph json["JSON บน wire"]
j["{"id":1,"name":"Ada","email":"[email protected]"}
— key ซ้ำเป็น text"]
end
subgraph pb["Protobuf บน wire"]
p["[1→1][2→Ada][3→[email protected]]
— tag ที่เป็นหมายเลข + ค่า binary"]
end การตัดสินใจออกแบบอย่างเดียวนี้ให้ผลตามมา 2 อย่าง:
- กระชับ ไม่มีชื่อ key ซ้ำ ไม่มีเครื่องหมายคำพูด ไม่มี whitespace — มีแค่ tag กับค่า
- evolve ได้ เพราะ หมายเลข คือตัวระบุ field คุณจึง rename
nameเป็นfull_nameใน.protoได้โดยไม่ทำให้ใครพัง — wire เห็นแค่ field2มาตลอด นี่คือรากฐานของการทำ schema evolution อย่างปลอดภัย (มีทั้งบทรออยู่ข้างหน้า)
ทำไมไม่ใช้ JSON ไปเลย?
หัวข้อที่มีชื่อว่า “ทำไมไม่ใช้ JSON ไปเลย?”JSON เยี่ยมมากสำหรับ public API — อ่านออก, ใช้ได้ทุกที่, setup เป็นศูนย์ แต่สำหรับการเรียกกันระหว่าง service ที่ปริมาณสูง ต้นทุนพวกนี้มีจริง และ protobuf เลี่ยงได้:
| JSON | Protobuf | |
|---|---|---|
| ขนาด | ส่ง key เป็น text ทุกครั้ง | tag ที่เป็นหมายเลข, ค่า binary |
| cost การ parse | parse text แบบ reflective | decode binary ที่ขับด้วย schema |
| schema | มีก็ได้ drift ง่าย | บังคับ และ compiler enforce |
| type safety | ตอน runtime ลุ้นเอา | type ที่ generate มา ตอน compile |
| การ evolve | อาศัย convention กับความกล้า | field number ทำให้เป็นระบบ |
trade-off คือความตรงไปตรงมาอีกด้าน: protobuf คนอ่านไม่ออก บน wire และคุณต้องมี .proto เพื่อแปลความหมายของ byte นั่นเป็นดีลที่ดีสำหรับ traffic ภายใน และเป็นดีลที่แย่สำหรับ public API ที่อยาก curl ได้
workflow แบบ contract-first
หัวข้อที่มีชื่อว่า “workflow แบบ contract-first”วินัยที่ protobuf บังคับคือ เขียน contract ก่อน ก่อนที่ทีมไหนจะเขียน code ทั้งสองฝั่งตกลง .proto กันก่อน: นี่คือ message เหล่านี้, นี่คือ method เหล่านี้ แล้ว codegen ก็สร้าง type ที่ตรงกันขึ้นมาทั้งสองฝั่ง schema จะ drift เงียบ ๆ ไม่ได้ เพราะทั้ง client และ server generate มาจากไฟล์เดียวกัน