Metadata
ข้อมูลเกี่ยวกับ call ไม่ใช่ข้อมูลใน message
หัวข้อที่มีชื่อว่า “ข้อมูลเกี่ยวกับ call ไม่ใช่ข้อมูลใน message”request และ response message ถือข้อมูล domain ของคุณ — user, order แต่ข้อมูลบางอย่างเป็นเรื่อง เกี่ยวกับตัว call เอง: ใครเป็นคนเรียก, call นี้อยู่ใน trace ไหน, จะ log ด้วย request id อะไร ถ้าเอาพวกนี้ยัดเข้าไปในทุก message จะทำให้ schema รก gRPC ให้ช่องแยกต่างหาก: metadata
metadata คือชุดของคู่ key/value ที่ส่งไปพร้อม call เหมือน HTTP header เป๊ะ — ซึ่งจริง ๆ แล้วก็กลายเป็น header บน HTTP/2 wire นั่นเอง
header และ trailer
หัวข้อที่มีชื่อว่า “header และ trailer”metadata มาในสองตำแหน่ง:
- header เดินทาง ก่อน message ตอนเริ่ม call ที่นี่คือที่ที่ client ใส่ auth token หรือ trace id — อะไรก็ตามที่ server ต้องรู้ตั้งแต่ต้น
- trailer เดินทาง หลัง response ตอนจบ call ที่นี่คือที่ที่ server ใส่ข้อมูลที่รู้ก็ต่อเมื่อทำงานเสร็จแล้ว — เช่น รายละเอียด status สุดท้ายหรือ metrics ฝั่ง server response แบบ streaming พึ่ง trailer เป็นพิเศษ เพราะ status รู้ได้ก็หลัง message สุดท้ายเท่านั้น
flowchart LR client["client"] -->|"headers: authorization, x-trace-id"| server["server"] server -->|"response message(s)"| client server -->|"trailers: final status, metrics"| client
อ่านและเขียน metadata
หัวข้อที่มีชื่อว่า “อ่านและเขียน metadata”client ตั้ง header ก่อน call; server อ่าน header เหล่านั้น และส่ง header กับ trailer กลับได้
// Client: attach metadata to the outgoing call.ctx := metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token, "x-request-id", reqID,)resp, err := client.GetUser(ctx, req)
// Server: read incoming metadata.md, _ := metadata.FromIncomingContext(ctx)reqID := md.Get("x-request-id")# Client: pass metadata as a list of (key, value) tuples.metadata = [("authorization", f"Bearer {token}"), ("x-request-id", req_id)]resp = client.GetUser(request, metadata=metadata)
# Server: read it off the context.def GetUser(self, request, context): incoming = dict(context.invocation_metadata()) req_id = incoming.get("x-request-id")// Client: build a Metadata object.const md = new Metadata();md.set('authorization', `Bearer ${token}`);md.set('x-request-id', reqId);client.getUser(request, md, (err, resp) => { /* ... */ });
// Server: read it off the call.function getUser(call, callback) { const reqId = call.metadata.get('x-request-id')[0];}กฎและ convention
หัวข้อที่มีชื่อว่า “กฎและ convention”- key เป็นตัวพิมพ์เล็ก gRPC normalize key ของ metadata เป็น lowercase ASCII; ให้ปฏิบัติแบบ case-insensitive
- value แบบ binary ใช้ suffix
-binkey ที่ลงท้ายด้วย-bin(เช่นtrace-context-bin) ถูกมองเป็น raw bytes และ base64-encode บน wire ส่วนอย่างอื่นต้องเป็น ASCII string ที่ถูกต้อง - อย่าใช้ key ที่สงวนไว้ซ้ำ key ที่ขึ้นต้นด้วย
grpc-ถูกสงวนโดย framework — อย่าตั้งเอง - metadata ไม่ได้เข้ารหัสในตัวเอง — ความเป็นส่วนตัวมาจาก TLS บน channel token ใน metadata บน channel ที่ insecure ถือว่าเปิดเผย
การ propagate ข้าม call chain
หัวข้อที่มีชื่อว่า “การ propagate ข้าม call chain”metadata คือวิธีที่ context ไหลผ่าน chain ของ service เมื่อ service A เรียก B ที่เรียก C คุณมักจะ propagate trace id และ request id: แต่ละ hop อ่าน metadata ขาเข้าและ copy key ที่เกี่ยวข้องไปยัง call ขาออก ปกติทำใน interceptor เพื่อไม่ให้ handler ต้องจำ นั่นคือวิธีที่ distributed trace ยังเชื่อมกันได้ข้าม gRPC หลาย hop