Service Discovery
Order service ต้องเรียก Customer service ถ้าเป็น deployment แบบตายตัว คุณก็แค่ hard-code http://customer-service:8080 ลงไปแล้วจบ แต่ใน environment สมัยใหม่ service รันเป็น instance หลายตัวภายใต้ autoscaling container ถูกจัดตารางใหม่ข้ามโฮสต์ และแพลตฟอร์มกำหนดตำแหน่งบนเครือข่ายแบบไดนามิก ชุด instance ของ Customer ที่ยังมีชีวิตอยู่ พร้อมทั้ง IP address และพอร์ต จึงเปลี่ยนทุกนาที ที่อยู่ที่ผู้เรียกต้องใช้ไม่ได้อยู่นิ่งเป็นค่าคงที่ที่ไหนเลย
ผู้เรียกต้องรู้ตำแหน่งบนเครือข่ายของ instance ปลายทาง แต่ตำแหน่งนั้นเป็นแบบไดนามิก instance เกิดและดับตามการ scale บ้างก็ crash บ้างก็ redeploy แล้วย้ายที่อยู่ นอกจากนี้ผู้เรียกยังอยากกระจาย request ไปยัง instance ที่แข็งแรงทุกตัว และต้องไม่ส่ง traffic ไปหา instance ที่ตายแล้ว การ hard-code ที่อยู่จึงเป็นไปไม่ได้ ส่วนไฟล์ config แบบ static ก็ล้าสมัยทันทีที่ instance restart บนโฮสต์ใหม่ คำถามคือผู้เรียกจะค้นหา instance ที่ยังมีชีวิตได้อย่างน่าเชื่อถืออย่างไร
วิธีแก้
หัวข้อที่มีชื่อว่า “วิธีแก้”หัวใจสำคัญคือ service registry ที่เป็นฐานข้อมูลที่เก็บรายชื่อ service, instance ของแต่ละ service พร้อมตำแหน่งและสุขภาพ โดย instance เข้าสู่ registry ได้สองวิธี:
- Self-registration — แต่ละ instance ลงทะเบียนตัวเองตอนเริ่มทำงาน ส่ง heartbeat เป็นระยะ และถอนการลงทะเบียนตอนปิด เรียบง่าย แต่ผูกมัดทุกบริการเข้ากับ API ของ registry
- Third-party registration — มี registrar แยกต่างหาก ซึ่งมักเป็นแพลตฟอร์ม deployment คอยเฝ้าดู instance แล้วอัปเดต registry ให้แทน ตัว code ของ service จึงไม่ต้องรู้เรื่องนี้เลย
เมื่อมี registry แล้ว ผู้เรียกเข้าถึง instance ได้สองวิธี
Client-side discovery ฝั่ง client query registry เพื่อขอรายการ instance ที่แข็งแรง แล้วเลือกมาหนึ่งตัวโดยทำ load balancing เองในฝั่ง client จากนั้นเรียกไปที่ instance นั้นโดยตรง
sequenceDiagram participant C as Client (Order Service) participant R as Service Registry participant I as Customer Instance C->>R: where is customer-service? R-->>C: [10.0.1.7:8080, 10.0.2.3:8080] C->>C: pick one (load-balance) C->>I: GET /customers/42 I-->>C: 200 OK
Server-side discovery client เรียกที่อยู่ที่เสถียร — router หรือ load balancer — ซึ่งสอบถาม registry เลือก instance และส่งต่อคำขอ client ไม่รู้อะไรเกี่ยวกับ registry เลย
sequenceDiagram participant C as Client (Order Service) participant LB as Load Balancer / Router participant R as Service Registry participant I as Customer Instance C->>LB: GET /customers/42 LB->>R: where is customer-service? R-->>LB: healthy instances LB->>I: forward request I-->>LB: 200 OK LB-->>C: 200 OK
ตัวอย่าง
หัวข้อที่มีชื่อว่า “ตัวอย่าง”รายการ registry และบันทึกเกี่ยวกับ load-balancer registry เก็บตำแหน่งและสุขภาพของแต่ละ instance; load balancer จัดเส้นทางตามชื่อบริการไปยัง instance ใดก็ตามที่มีสุขภาพดีอยู่ในขณะนั้น
# Service registry entry for customer-serviceservice: customer-serviceinstances: - id: customer-7a3f address: 10.0.1.7:8080 health: passing ttl: 10s - id: customer-9b2c address: 10.0.2.3:8080 health: passing ttl: 10s - id: customer-4d1e address: 10.0.3.9:8080 health: critical ttl: 10s # excluded from routing
# Load balancer routing note (server-side discovery)# Clients call the stable name "customer-service".# The LB resolves it against the registry and forwards# only to instances whose health == passing, spreading# load round-robin. An instance that misses its TTL# heartbeat is marked critical and dropped automatically.ผลลัพธ์ที่ตามมา
หัวข้อที่มีชื่อว่า “ผลลัพธ์ที่ตามมา”สิ่งที่คุณได้:
- การจัดเส้นทางที่ไดนามิกและทนทาน ผู้เรียกเข้าถึง instance ที่มีสุขภาพดีในขณะนั้นได้เสมอ ไม่ว่า instance จะขยายขนาด crash หรือย้ายที่อยู่บ่อยแค่ไหน instance ที่ตายแล้วจะหลุดออกจากการหมุนเวียนโดยอัตโนมัติ
- load balancing ในตัว คำขอกระจายไปยัง instance ที่มีสุขภาพดีทั้งหมด ไม่ว่า client (client-side) หรือ router (server-side) จะเป็นผู้กระจาย
สิ่งที่คุณต้องจ่าย:
- registry กลายเป็น infrastructure ที่สำคัญที่สุด ต้อง available สูงและสอดคล้องมากพอจะเชื่อถือได้ ถ้า registry ดับหรือคืนข้อมูลเก่า ผู้เรียกก็จะส่ง traffic ไปหา instance ที่ตายแล้ว ปกติจึงรันเป็น cluster ที่ทำ replication ไว้
- การแลกได้แลกเสียระหว่างสองรูปแบบ client-side discovery มีประสิทธิภาพ (ไม่มี hop เพิ่ม) แต่ใส่ logic การ discovery และ load-balancing เข้าไปในทุก client และทุกภาษาที่คุณใช้ server-side discovery ทำให้ client เรียบง่าย แต่เพิ่ม network hop และองค์ประกอบอีกตัวให้ต้องทำงาน
- การปรับจูน heartbeat health check และ TTL ต้องถูกปรับจูน: ช้าเกินไปแล้ว instance ที่ตายแล้วก็ค้างอยู่; ก้าวร้าวเกินไปแล้วการสะดุดชั่วครู่ก็ขับ instance ที่มีสุขภาพดีออกไป
แพลตฟอร์มหลายตัว เช่น container orchestrator ที่มี DNS ในตัว และ service mesh ให้ server-side discovery มาพร้อมใช้งานทันที registry และการจัดเส้นทางจึงกลายเป็นส่วนหนึ่งของ infrastructure มากกว่าจะเป็น code แอปพลิเคชันของคุณ
เนื้อหาที่เกี่ยวข้อง
หัวข้อที่มีชื่อว่า “เนื้อหาที่เกี่ยวข้อง”- Remote Procedure Invocation — การเรียกแบบ synchronous ที่ต้องการที่อยู่ instance ที่จะส่งไปหา
- API Gateway — พึ่งพา discovery เพื่อจัดเส้นทางไปยัง instance ที่ยังมีชีวิตอยู่ของบริการภายใน
- Backends for Frontends — แต่ละ BFF ทำ discovery เพื่อหา service ภายในที่ต้องนำมาประกอบ
| ข้อดี | ข้อแลกเปลี่ยน |
|---|---|
| service ค้นหากันเองได้โดยไม่ต้อง hardcode IP | เพิ่ม infrastructure component (registry) ที่ต้องดูแล |
| scale service ขึ้นลงได้โดย registry อัปเดตเอง | registry ล้มหมายถึงทุก service หาคู่สื่อสารไม่ได้ |
| deploy service ใหม่โดยไม่ต้องแก้ config ของ caller | health check ที่ไม่ดีทำให้ส่ง request ไป service ที่ตายแล้ว |
| รองรับ environment หลายแบบ (dev, staging, prod) | เพิ่ม latency เล็กน้อยจากการ lookup registry |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”Static IP Discovery — hardcode IP address ของ service ใน config
อาการ:
- เพิ่ม replica ต้องแก้ config ของทุก caller
- restart service ได้ IP ใหม่ทำให้ caller ส่ง request ไม่ถึง
- ทำงานไม่ได้ใน Kubernetes ที่ Pod IP เปลี่ยนทุก restart
Stale Registry — registry มีข้อมูล service ที่ตายแล้ว
อาการ:
- service deregister ไม่สำเร็จ registry ยังมีชื่อเก่า
- caller ส่ง request ไปหา instance ที่ไม่มีแล้ว
- error rate สูง client side retry เพิ่มขึ้น
💡 ตัวอย่างจากของจริง
Netflix (Eureka):
- สร้าง Eureka เพื่อ Service Discovery บน AWS ที่ IP เปลี่ยนบ่อย
- แต่ละ service register ตัวเองเมื่อ start และ deregister เมื่อ stop
- ปัจจุบัน open source และเป็นส่วนหนึ่งของ Spring Cloud
Kubernetes:
- DNS-based Service Discovery built-in ผ่าน CoreDNS
- Service object ทำหน้าที่เป็น stable endpoint แม้ Pod จะ restart