ทำความรู้จัก Orchestration และ Choreography รูปแบบการสื่อสารใน Microservice Architecture
พร้อมตัวอย่าง Usecase เบสิค ใน NodeJS และ RabbitMQ
วิธีการสื่อสารใน Microservice Architecture นั้นที่พบเห็นบ่อยมี 2 วิธีหลักๆ ได้แก่ Orchetration และ Choreography โดยในบทความนี้จะพูดถึงว่า แต่ละตัวนั้นมี Pattern การทำงานอย่างไร แนวคิดการทำงาน และ ข้อดี ข้อเสีย
และตอนสุดท้ายของบทความพูดถึง Message Broker ที่จะเข้ามาช่วยเพิ่มประสิทธิภาพ ในการแลกเปลี่ยนข้อมูล เป็นตัวกลางในการสื่อสาร ระหว่าง Microservice โดยในบทความนี้ใช้ RabbitMQ สำหรับใช้ในอธิบายประกอบบทความ
ก่อนตัดสินใจเลือก Microservice Architecture
ก่อนจะใช้ Architecture ใดกับระบบที่เรากำลังจะเริ่มพัฒนา ความเห็นส่วนตัวผมอยากให้ลองมองหลายๆมิติ Microservice Architecture นั้น ไม่ได้เหมาะกับทุกปัญหา ข้อดีของ architecture แบบนี้ คือ functional scaling dimension แต่ก็แลกมาด้วยความซับซ้อนของระบบ / performance / งานที่เพิ่มเข้ามาเกี่ยวกับ distributed tracing / Data consistency
หากพบว่าระบบไม่ได้ซับซ้อนมาก การสร้างระบบโดยยึดแนวทาง Monolith แต่แบ่ง Project structure เป็น Business Domain / Module และมีเทส ก็สามารถช่วยให้เราจัดการโปรเจคได้สะดวก ยืดหยุ่น ต่อการเปลี่ยนแปลงในอนาคต แถมได้ Productivity อีกด้วย แล้วในอนาคตหากระบบมันใหญ่ขึ้น การจะถอด Module ที่เราทำไว้ แยกออกไปเป็น Microservice ก็คงไม่ยาก
การสื่อสารใน Microservice Architecture มีอยู่ด้วยกัน 2 รูปแบบหลักๆ คือ Orchestration และ Choreography
Orchestration
วิธีการสื่อสารแบบ Synchronous ใช้การสื่อสารแบบ Request/Response ทุก Request จาก Sender ส่ง message ไปหา Receiver หรือ Remote server นั้นต้องเกิดการรอคอยให้ผลลัพธ์ดำเนินงานเสร็จ และส่งกลับ จึงจะถือว่าการสื่อสารข้อมูลชุดนั้นสมบูรณ์
One‑to‑one Interaction Style
แต่ละ Request ถูกโปรเซสด้วย 1 service instance หรือ receiver เดียว ไม่สามารถกระจายไปหลายๆ instance ในช่วงเวลาเดียวได้ ซึ่งมักพบเห็นทั่วไปในเรื่อง Blocking I/O
ตัวอย่างระบบ Creating a new customer
จากตัวอย่างเป็นภาพของ ระบบลงทะเบียนลูกค้า ซึ่งสามารถแบ่ง service domain ได้ ดังนี้
- Customer Service: สร้าง Customer record สำหรับข้อมูลใหม่
- Loyalty Service: ระบบคะแนนของลูกค้า เช่น ระบบเพิ่มคะแนน
- Post Service: สร้าง Welcome pack ให้กับ Customer และจัดส่ง
- Email Service: ส่ง E-mail ให้กับลูกค้าใหม่
การ Communicate ด้วย Orchestration Pattern
Customer Server ทำหน้าที่เป็น Hub ที่คอยรับ Request มาจาก Frontend หรือ server-to-server ซึ่ง flow การทำงานทั้งหมดจะอยู่ที่นี่ และทำแบบ synchonous ในแต่ละ stage การทำงานจะมีการหยุดรอผลลัพธ์ ก่อนไปยัง stage ถัดไป ตามลำดับ
ด้วย Usecase แบบนี้ หากใช้การสื่อสารแบบ orchestration ที่มีรูปแบบ one-to-one เป็นการสื่อสารได้ทีละ instance ในช่วงเวลาหนึ่ง รูปแบบนี้ง่ายต่อการทำความเข้าใจ แต่ก็ทำให้เกิดผลเสีย ตามมา ดังนี้
- Multiple responsibility principle คือ Customer service ทำหน้าที่หลายอย่างมากเกินไป แทนที่จะทำหน้าที่เดียว
- Higher coupling การทำให้ service นั้นผูดติดกันมากไป ยากต่อการแก้ไขในอนาคต
- Synchronous เกิดข้อเสีย ทำให้เราไม่สามารถทำงานอื่นได้รอ ในขณะที่รอผลลัพธ์
- Bigger and not cleaner ขนาดโค๊ดของ Customer service ใหญ่ขึ้น มีโค๊ดส่วนอื่นมาเกี่ยวข้อง
ลองมาดูวิธีการแก้ไขด้วยการสื่อสารแบบ Choreography กันครับ
Choreography
หัวข้อก่อนหน้านี้แสดงให้เห็นถึงการสื่อสารแบบ Orchestration ที่ใช้รูปแบบ Request/Response อาจไม่เหมาะกับวิธีการสื่อสารใน Microservice มากนัก ดังนั้นในหัวข้อนี้จะพูดถึงสถาปัตยกรรม Event driven ซึ่งสนับสนุนการทำงานแบบ one-to-many และการทำงานแบบ Asynchronous
One-to-many
- Asynchronous การสื่อสารแบบนี้ทำให้ request กระจายตัวไปยังหลายๆ instance ได้ในเวลาเดียว และไม่ต้องรอ ส่วน receiver ทำหน้าที่แค่บอกว่าได้รับ message ก็ถือว่าการสื่อสารนั้นสมบูรณ์
- Notification ส่งไปหลายๆ instance ในเวลาเดียว แต่ไม่ต้องการผลลัพธ์ว่าได้รับ message แล้วจาก receiver
การ Communicate ด้วย Choreography
Customer service ต่อจากนี้มีหน้าที่เดียว คือ publish topic “Customer created event” ออกไป
ทำให้ Loyalty
Post
และ Email
แต่ละ service ต้องรู้หน้าที่ของตัวเอง และทำการ subscribes “Customer created event” topic ไว้ เมื่อใดก็ตามเกิด event ใหม่ subscriber สามารถรับรู้การเปลี่ยนแปลง และทำหน้าที่ต่อ event นั้นๆ แบบ Asynchronous ได้ทันที
วิธีนี้ คือ แนวคิดของสถาปัตยกรรม Event driven
ข้อดี
- Single responsibility principle คือ Customer service มีเพียงหน้าที่เดียว ไม่ได้ทำหน้าที่เป็นคนกลาง คุยกับหลายๆ service อีกแล้ว
- Lower coupling แต่ละ service ไม่ผูกติดกัน ต่างคนต่างทำหน้าที่ของตัวเอง
- Asynchronous เมื่อ Customer service ทำงานเสร็จแล้ว ผู้ใช้ไม่ต้องรอผลลัพธ์จาก service ที่เหลือ เพราะ topic “Customer created event” ถูกสร้างขึ้นมาให้ กับ service ที่ subscribe ไว้แล้ว
- Thin and clean เราสามารถเรียกได้ว่า นี่ คือ microservice จริงๆ เพราะ 1 service มีโค๊ดเพียงชุดเดียว ที่ทำหน้าที่เดียว
อย่างไรก็ดี มีงานที่ต้องทำเพิ่มนะ
- Monitoring and tracking หากต้องการ track ว่าแต่ละงานสำเร็จ หรือไม่ จำเป็นต้องทำระบบนี้ขึ้นมา เช่น ระบบ Loyalty มี Bug เกิดขึ้น หรือ คำนวนคะแนนผิด
- Health check แต่ละ service ทำงานแยกกันจำเป็นต้องมี service health ไว้ตรวจสอบความพร้อมของการทำงาน
- Traceability ระบบมีความซับซ้อนอยู่หรือไม่ จำเป็นต้องสร้างระบบ distribute tracing ขึ้นมา ลองพิจารณาพวก Zipkin, Jeager ดู
- Container Orchestration ถ้าเราใช้ Dockerize จำเป็นต้องมีซอฟท์แวร์ที่ช่วยจัดการ หลายๆ container ไม่งั้นเหนื่อยแน่ๆ
- Standard การทำแบบนี้เราสามารถใช้ได้ทั้ง REST และ gRPC ฉนั้นต้องกำหนด standard ให้ดีตอน implement
Message Broker
ในหัวข้อนี้เราจะมาทำความรู้จัก Message Broker ผู้ทำหน้าที่เป็นตัวกลางระหว่าง Microservice ซึ่งหัวข้อนี้จะใช้ RabbitMQ สำหรับ Message Borker และใช้โปรโตคอล AMQP (Advanced Message Queuing Protocol) สำหรับการส่ง และ รับ Event
ประเภทของการ Exchange
RabbitMQ รองรับการ exchange 3 รูปแบบหลัก ได้แก่
- Direct ทำการส่ง message ไปยัง Queue ที่ระบุใน message routing key แบบ exact match
- Topic ทำการกระจาย Topic ไปยัง Queue ที่ match wildcard ระหว่าง routing key และ routing pattern
- Fanout ทำการส่ง message ไปทุก Queue ที่ Available อยู่
ส่วนการ exchange headers คือ การใช้ header attribute สำหรับการ routing
มาลองสร้างโปรเจคง่ายๆ จาก Use case Choreography และกระจายแบบ fanout กันครับ
ติดตั้ง RabbitMQ ผ่านวิธี docker-compose.yml
สร้าง Customer Service สำหรับ Producer
ทำการสร้าง producer.js
เป็นไฟล์ตัวอย่างง่ายๆ ที่ทำหน้าที่เป็น Publisher สร้าง Topic “Customer created event”
สร้าง Consumer
จากตัวอย่างโค๊ดเป็นการสร้างไฟล์ consumer.js
ที่ทำหน้าที่เป็น loyaltyService, postService และ emailService คอยติดตาม subscribe topic “Customer created event” แบบง่ายๆ เพื่อให้เห็นการทำ pub/sub event โดยใช้ Message Broker เป็นตัวกลาง ซึ่งไม่แนะนำให้เอาไปใช้กับงานจริง เพราะจากตัวอย่างเป็นเพียงกรณีศึกษาเท่านั้น
การทดสอบระบบ
[consumer ทำการ subscribe topic]
node consumer.js[producer ทำการ publish new message ลง queue]
node producer.js
RabbitMQ Dashboard
RabbitMQ เตรียม dashboard ให้เราสามารถเข้าถึง ข้อมูล ของ Queue ได้ง่ายๆ โดยที่ผู้อ่านสามารถดูรายละเอียด ผ่านทาง http://localhost:15672/
สำหรับหัวข้อนี้ก็เป็นตัวอย่างง่ายๆ สำหรับการประยุกต์ใช้ Message Broker ในการช่วยสื่อสารระหว่าง Microservice ซึ่งช่วยให้ การสื่อสารนั้นมีประสิทธิภาพมากขึ้น ไม่จำเป็นต้องเกิดการรอ เพราะ ตามทฤษฏีของการสร้าง Microservice นั้น แต่ละ service สามารถทำงานแยกกันได้ จึงจำเป็นอย่างยิ่งในการพิจารณานำเอา Message Broker ไปปรับใช้ หากต้องเจอกับ Service ที่เป็น Long-run process ที่มีโอกาส Blocking service อื่นๆ และขอย้ำอีกครั้งว่าโค๊ดตัวอย่างเป็นเพียงกรณีศึกษา ไม่ใช่แนวทางที่เอาไปใช้กับ Production ได้เลย
และหากผู้อ่านสนใจศึกษาเพิ่มเติมจาก Reference links ข้างใต้บทความนี้ได้ครับผม