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

Protocol Buffers Intro

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 ที่เล็กที่สุดแต่ยังน่าสนใจ:

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
JSON ส่งชื่อทุกครั้ง; protobuf ส่ง tag ที่เป็นหมายเลขเล็ก ๆ

การตัดสินใจออกแบบอย่างเดียวนี้ให้ผลตามมา 2 อย่าง:

  1. กระชับ ไม่มีชื่อ key ซ้ำ ไม่มีเครื่องหมายคำพูด ไม่มี whitespace — มีแค่ tag กับค่า
  2. evolve ได้ เพราะ หมายเลข คือตัวระบุ field คุณจึง rename name เป็น full_name ใน .proto ได้โดยไม่ทำให้ใครพัง — wire เห็นแค่ field 2 มาตลอด นี่คือรากฐานของการทำ schema evolution อย่างปลอดภัย (มีทั้งบทรออยู่ข้างหน้า)

JSON เยี่ยมมากสำหรับ public API — อ่านออก, ใช้ได้ทุกที่, setup เป็นศูนย์ แต่สำหรับการเรียกกันระหว่าง service ที่ปริมาณสูง ต้นทุนพวกนี้มีจริง และ protobuf เลี่ยงได้:

JSONProtobuf
ขนาดส่ง key เป็น text ทุกครั้งtag ที่เป็นหมายเลข, ค่า binary
cost การ parseparse text แบบ reflectivedecode 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 ได้

วินัยที่ protobuf บังคับคือ เขียน contract ก่อน ก่อนที่ทีมไหนจะเขียน code ทั้งสองฝั่งตกลง .proto กันก่อน: นี่คือ message เหล่านี้, นี่คือ method เหล่านี้ แล้ว codegen ก็สร้าง type ที่ตรงกันขึ้นมาทั้งสองฝั่ง schema จะ drift เงียบ ๆ ไม่ได้ เพราะทั้ง client และ server generate มาจากไฟล์เดียวกัน

Protocol Buffers เล่น 2 บทบาทอะไร?
จริง ๆ แล้ว protobuf ส่งอะไรบน wire สำหรับ field หนึ่ง?
ทำไมคุณถึง rename field ใน `.proto` ได้โดยไม่ทำให้ client เดิมพัง?
ข้อเสียหลักของ protobuf เมื่อเทียบกับ JSON คืออะไร?