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

Observability & Testing

ความเร็วของ protobuf แลกมาด้วยการอ่านด้วยตาไม่ออก คุณมอง gRPC call ด้วยตาแบบที่มอง REST request ไม่ได้ ecosystem จึงแทน “อ่าน traffic” ด้วย reflection, telemetry ที่อยู่บน interceptor และ in-process test

Server reflection ให้ server อธิบาย service และ message ของตัวเองตอน runtime ได้ ทำให้เครื่องมือเรียก method ได้โดยไม่ต้องมี .proto ในมือ เมื่อเปิดใช้ grpcurl จะกลายเป็น curl ของ gRPC:

Terminal window
# List services on a running server, then call one — no local .proto needed
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext -d '{"id": 42}' localhost:50051 user.v1.UserService/GetUser

เปิด reflection ใน non-production หรือหลัง auth — reflection คือสิ่งที่ทำให้ server ที่รันอยู่สำรวจได้

คุณเจอ interceptor มาแล้วในฐานะที่ ๆ ใส่ cross-cutting concern observability คือการใช้งานที่ใหญ่ที่สุดของตัวเอง: interceptor chain เดียวเพิ่ม metrics (latency, จำนวน request, status code), traces (span ที่ตาม call ข้าม service) และ log แบบมีโครงสร้าง — ให้ทุก method โดยไม่แตะ handler code

flowchart LR
  call["incoming RPC"] --> mw["interceptor chain"]
  mw --> m["metrics
(latency, count, code)"]
  mw --> t["trace span
(propagate context)"]
  mw --> l["structured log"]
  mw --> handler["your handler"]
interceptor chain เดียวทำให้ทุก method observe ได้

เส้นทางมาตรฐานคือ instrumentation แบบ OpenTelemetry ซึ่งมาเป็น gRPC interceptor สำเร็จรูปในทุกภาษาหลัก — register ครั้งเดียวแล้วได้ metrics และ distributed trace ทั่วทั้ง fleet

การเปิด port จริงทำให้ test ช้าและ flaky gRPC ให้คุณรัน server และ client จริง ใน memory ได้ โดยผ่านทั้งเส้นทาง serialization และ interceptor เต็ม ๆ โดยไม่มี network:

// bufconn: a real gRPC server over an in-memory listener
lis := bufconn.Listen(1024 * 1024)
s := grpc.NewServer()
userv1.RegisterUserServiceServer(s, &server{})
go s.Serve(lis)
conn, _ := grpc.NewClient("passthrough:///bufnet",
grpc.WithContextDialer(func(ctx context.Context, _ string) (net.Conn, error) {
return lis.DialContext(ctx)
}),
grpc.WithTransportCredentials(insecure.NewCredentials()))
client := userv1.NewUserServiceClient(conn)
got, err := client.GetUser(ctx, &userv1.GetUserRequest{Id: 42})

gRPC มักเร็วกว่า REST/JSON สำหรับ call ที่ปริมาณสูง ขนาดเล็ก และมีโครงสร้าง — byte น้อยกว่า, decode ถูกกว่า, connection แบบ multiplex แต่ต้องวัดกับ workload ของคุณเอง: สำหรับ call นาน ๆ ที ครั้งหรือ blob ใหญ่ที่ถูก compress มาแล้ว ความต่างอาจแทบไม่มี และ tooling ของ REST อาจชนะโดยรวม benchmark ด้วย payload และ concurrency ที่สมจริง ไม่ใช่ loop ของเล่น ก่อนจะเอาไปเป็นจุดขาย

server reflection เปิดให้ทำอะไรได้?
metrics, traces และ logs เกาะเข้ากับ gRPC ตรงไหนโดยธรรมชาติ?
ทำไมต้อง test ด้วย in-process server (เช่น Go bufconn หรือ ephemeral port)?
จุดยืนที่ตรงไปตรงมาเรื่อง "gRPC เร็วกว่า REST" คืออะไร?