Monolithic Versus Distributed Architectures (สถาปัตยกรรมแบบ Monolithic กับแบบ Distributed)

ดังที่คุณได้เรียนรู้ใน Part I แล้วว่า architecture style สามารถแบ่งออกเป็นสองประเภทหลัก ได้แก่ monolithic (deployment unit เดียวสำหรับโค้ดทั้งหมด) และ distributed (มี deployment unit หลายตัวที่เชื่อมต่อกันผ่าน remote access protocol) แม้ว่าจะไม่มีแผนการจำแนกประเภทใดที่สมบูรณ์แบบ แต่ distributed architecture ทั้งหมดล้วนมีชุดความท้าทายและปัญหาร่วมกันที่ไม่พบใน monolithic architecture style ทำให้แผนการจำแนกประเภทนี้เป็นการแบ่งแยกที่ดีระหว่าง architecture style ต่าง ๆ

ใน Part II ของหนังสือเล่มนี้ เราจะอธิบาย architecture style ต่อไปนี้อย่างละเอียด:

Monolithic

  • Layered architecture ( Chapter 10 )

  • Pipeline architecture ( Chapter 12 )

  • Microkernel architecture ( Chapter 13 )

Distributed

  • Service-based architecture ( Chapter 14 )

  • Event-driven architecture ( Chapter 15 )

  • Space-based architecture ( Chapter 16 )

  • Service-oriented architecture ( Chapter 17 )

  • Microservices architecture ( Chapter 18 )

distributed architecture style แม้จะทรงพลังกว่า monolithic architecture style มากในแง่ของ performance, scalability และ availability แต่ก็มี trade-off ที่สำคัญ ปัญหากลุ่มแรกที่ distributed architecture ทุกแบบต้องเผชิญถูกอธิบายไว้ใน “fallacies of distributed computing” ซึ่งถูกระบุขึ้นเป็นครั้งแรกโดย L. Peter Deutsch และเพื่อนร่วมงานจาก Sun Microsystems ในปี 1994 fallacy คือสิ่งที่เป็นเท็จแต่มีคนเชื่อหรือสันนิษฐานว่าเป็นจริง fallacy ทั้งแปดข้อของ distributed computing ยังคงใช้ได้กับ distributed architecture ในปัจจุบัน หัวข้อต่อไปนี้จะอธิบาย fallacy แต่ละข้อ

Fallacy #1: The Network Is Reliable (Fallacy ข้อที่ 1: เครือข่ายเชื่อถือได้)

Fallacy 1

Figure 9-7. เครือข่ายไม่ได้เชื่อถือได้เสมอไป

ทั้งนักพัฒนาและสถาปนิกต่างสันนิษฐานว่าเครือข่ายเชื่อถือได้ แต่ในความเป็นจริงแล้วไม่ใช่ แม้ว่าเครือข่ายจะเชื่อถือได้มากขึ้นตามกาลเวลา แต่ความจริงก็คือเครือข่ายโดยทั่วไปยังคงไม่น่าเชื่อถืออยู่ดี เรื่องนี้มีความสำคัญมากสำหรับ distributed architecture style ทุกแบบ เพราะพวกมันต้องพึ่งพาเครือข่ายในการสื่อสารไปยัง จาก และระหว่าง service ต่าง ๆ ดังที่แสดงใน Figure 9-7 , Service B อาจทำงานได้ปกติดีทุกอย่าง แต่ Service A กลับไม่สามารถเข้าถึงมันได้เนื่องจากปัญหาเครือข่าย ที่แย่ไปกว่านั้นคือ Service A อาจร้องขอให้ Service B ประมวลผลข้อมูลบางอย่าง แต่กลับไม่ได้รับการตอบกลับเลยเพราะปัญหาเครือข่าย นี่คือเหตุผลที่สิ่งอย่าง timeout และ circuit breaker ถูกนำมาใช้ระหว่าง service ยิ่งระบบพึ่งพาเครือข่ายมากเท่าไร (ดังที่ microservices architecture ทำอย่างเห็นได้ชัด) ก็ยิ่งมีโอกาสที่จะกลายเป็นระบบที่ไม่น่าเชื่อถือมากขึ้นเท่านั้น

Fallacy #2: Latency Is Zero (Fallacy ข้อที่ 2: Latency เท่ากับศูนย์)

Fallacy 2

Figure 9-8. Latency ไม่ได้เท่ากับศูนย์

ดังที่ Figure 9-8 แสดงให้เห็น เมื่อมีการเรียกใช้ component อื่นแบบ local ผ่าน method หรือ function call เวลาที่ใช้เข้าถึง component นั้น ( t_local ) จะวัดกันเป็นหน่วยนาโนวินาทีหรือไมโครวินาที แต่เมื่อการเรียกแบบเดียวกันนี้ทำผ่าน remote access protocol (เช่น REST, messaging หรือ RPC) เวลา ( t_remote ) จะวัดกันเป็นมิลลิวินาที และจะมากกว่า t_local เสมอ latency ใน distributed architecture ใด ๆ ไม่มีทางเป็นศูนย์ แต่สถาปนิกส่วนใหญ่กลับมองข้าม fallacy ข้อนี้ไป โดยยืนยันว่าเครือข่ายของตนเร็ว ลองถามตัวเองดูว่า คุณรู้หรือไม่ว่า average round-trip latency ของการเรียก RESTful call ใน production environment ของคุณเป็นเท่าไร 60 มิลลิวินาทีหรือ 500 มิลลิวินาที?

เมื่อพิจารณาจะใช้ distributed architecture ใด ๆ โดยเฉพาะ microservices สถาปนิกต้องรู้ค่า latency เฉลี่ยนี้ เพราะเป็นวิธีเดียวที่จะพิจารณาได้ว่า distributed architecture นั้นเป็นไปได้จริงหรือไม่ เนื่องจากธรรมชาติของ service ที่มีขนาดเล็กและมีการสื่อสารระหว่างกันมาก

ตัวอย่างเช่น สมมติว่า latency เฉลี่ยอยู่ที่ 100 มิลลิวินาทีต่อ request การเชื่อม service call ต่อ ๆ กันเพื่อทำ business function หนึ่งอย่างจะเพิ่มเวลาให้ request นั้นถึง 1,000 มิลลิวินาที! การรู้ latency เฉลี่ยเป็นสิ่งสำคัญ แต่การรู้ latency ที่ percentile ที่ 95 ถึง 99 ยิ่งสำคัญกว่า แม้ว่า latency เฉลี่ยของระบบอาจจะอยู่ที่แค่ 60 มิลลิวินาที (ซึ่งถือว่าดี) แต่ percentile ที่ 95 อาจสูงถึง 400 มิลลิวินาที! latency แบบ “long tail” นี้เองที่มักจะฆ่า performance ใน distributed architecture ในกรณีส่วนใหญ่ network administrator จะสามารถให้ค่า latency ได้ (ดู “Fallacy #6: There Is Only One Administrator” )

Fallacy #3: Bandwidth Is Infinite (Fallacy ข้อที่ 3: Bandwidth ไม่มีจำกัด)

Fallacy 3

Figure 9-9. Bandwidth ไม่ได้ไม่มีจำกัด

bandwidth มักไม่ใช่ปัญหาใน monolithic architecture เพราะเมื่อ business request เข้ามาใน monolith แล้ว แทบไม่ต้องใช้ bandwidth เลยในการประมวลผลมัน แต่ดังที่แสดงใน Figure 9-9 เมื่อระบบถูกแตกออกเป็น deployment unit ย่อย ๆ (service) ใน distributed architecture เช่น microservices การสื่อสารไปยังและระหว่าง service เหล่านี้จะใช้ bandwidth จำนวนมาก สิ่งนี้ทำให้เครือข่ายช้าลง ส่งผลกระทบต่อ latency (fallacy ข้อ 2) และความน่าเชื่อถือ (fallacy ข้อ 1)

เพื่อแสดงให้เห็นความสำคัญของ fallacy ข้อนี้ ลองพิจารณา service สองตัวที่แสดงใน Figure 9-9 สมมติว่า Service A จัดการรายการ wish list ของเว็บไซต์ และ Service B จัดการโปรไฟล์ของลูกค้า ทุกครั้งที่มี request สำหรับ wish list เข้ามาที่ Service A , Service A จะต้องใช้ชื่อลูกค้าสำหรับ response contract ของ wish list เพื่อให้ได้ชื่อมา มันต้องเรียก interservice call ไปยัง Service B . Service B จะส่ง attribute กลับมา 45 ตัว รวมขนาด 500 KB ให้กับ Service A ทั้งที่ต้องการแค่ชื่อ (200 ไบต์) เท่านั้น เรื่องนี้อาจฟังดูไม่สำคัญมาก แต่ request สำหรับรายการ wish list เกิดขึ้นประมาณ 2,000 ครั้งต่อวินาที ซึ่งหมายความว่า Service A เรียก Service B ถึง 2,000 ครั้งต่อวินาที ด้วยขนาด 500 KB ต่อ request แต่ละครั้ง แต่ละครั้ง ของ interservice call จึงใช้ bandwidth ถึง 1 GBps เลยทีเดียว!

coupling รูปแบบนี้ เรียกว่า stamp coupling ใช้ bandwidth จำนวนมากใน distributed architecture ถ้า Service B ส่งกลับ เฉพาะ ข้อมูล 200 ไบต์ที่ Service A ต้องการเท่านั้น ก็จะใช้ bandwidth รวมแค่ 400 Kbps

stamp coupling สามารถแก้ไขได้ด้วยวิธีดังต่อไปนี้:

  • สร้าง private RESTful API endpoint

  • ใช้ field selector ใน contract

  • ใช้ GraphQL เพื่อ decouple contract

  • ใช้ value-driven contract ร่วมกับ consumer-driven contract

  • ใช้ internal messaging endpoint

ไม่ว่าจะใช้เทคนิคใดก็ตาม วิธีที่ดีที่สุดในการรับมือกับ fallacy ข้อนี้ใน distributed architecture คือการทำให้แน่ใจว่า service หรือระบบส่งเฉพาะข้อมูลที่จำเป็นเท่านั้น

Fallacy #4: The Network Is Secure (Fallacy ข้อที่ 4: เครือข่ายปลอดภัย)

Fallacy 4

Figure 9-10. เครือข่ายไม่ได้ปลอดภัย

สถาปนิกและนักพัฒนาส่วนใหญ่คุ้นเคยกับการใช้ virtual private network (VPN), trusted network และ firewall จนมักลืม fallacy ข้อนี้ของ distributed computing ไป แต่ความจริงคือ เครือข่ายไม่ได้ปลอดภัย ดังที่แสดงใน Figure 9-10 endpoint ทุกจุดของทุก deployment unit ใน distributed architecture ต้องได้รับการป้องกันจาก request ที่ไม่รู้จักหรือเป็นอันตราย พื้นที่เสี่ยงต่อภัยคุกคามและการโจมตีจะเพิ่มขึ้นหลายเท่าตัวเมื่อย้ายจาก monolithic ไปเป็น distributed architecture ทำให้เรื่อง security ท้าทายมากขึ้นอย่างมาก การป้องกัน endpoint ทุกจุด แม้แต่ในการสื่อสารระหว่าง service ก็เป็นอีกเหตุผลหนึ่งที่ performance มักจะช้าลงใน architecture style แบบ synchronous ที่ distributed สูง เช่น microservices และ service-based architecture

Fallacy #5: The Topology Never Changes (Fallacy ข้อที่ 5: Topology ไม่เคยเปลี่ยนแปลง)

Fallacy 5

Figure 9-11. network topology เปลี่ยนแปลงอยู่ตลอดเวลา

Fallacy ข้อ 5 ดังที่แสดงใน Figure 9-11 หมายถึง network topology โดยรวม ซึ่งรวมถึง router, hub, switch, firewall, เครือข่าย และอุปกรณ์ต่าง ๆ ทั้งหมด สถาปนิกมักสันนิษฐานว่า topology นั้นคงที่และไม่เคยเปลี่ยนแปลง แต่ในความเป็นจริงมันเปลี่ยนแปลงอยู่เสมอ มันเปลี่ยนแปลงตลอดเวลา แล้ว fallacy ข้อนี้มีความสำคัญอย่างไร?

สมมติว่าคุณมาทำงานในเช้าวันจันทร์แล้วพบว่าทุกคนวิ่งกันวุ่นวาย เพราะ service ต่าง ๆ timeout อยู่เรื่อยใน production คุณทำงานร่วมกับทีมต่าง ๆ พยายามอย่างเร่งรีบเพื่อหาสาเหตุว่าเกิดอะไรขึ้น ไม่มี service ใหม่ถูก deploy ในช่วงสุดสัปดาห์เลย แล้วมันคืออะไรกันแน่? หลังจากผ่านไปหลายชั่วโมง คุณก็พบว่าการอัปเกรดเครือข่ายที่ดูเหมือนจะเป็นเรื่อง “เล็กน้อย” เมื่อตอนตีสองของเช้าวันนั้น ได้ทำให้สมมติฐานเรื่อง latency ทั้งหมดของระบบใช้ไม่ได้อีกต่อไป จนกระตุ้นให้เกิด timeout และ circuit breaker ทำงาน

สถาปนิกต้องสื่อสารกับทีม operations และ network administrator อย่างต่อเนื่องว่ามีอะไรเปลี่ยนแปลงบ้างและเมื่อไร เพื่อให้สามารถปรับตัวได้ทันและหลีกเลี่ยงเรื่องน่าประหลาดใจแบบนี้ เรื่องนี้อาจดูเหมือนชัดเจนและง่าย แต่ในความเป็นจริงไม่ใช่ทั้งสองอย่างเลย อันที่จริง fallacy ข้อนี้นำไปสู่ fallacy ข้อถัดไปโดยตรง

Fallacy #6: There Is Only One Administrator (Fallacy ข้อที่ 6: มีผู้ดูแลระบบเพียงคนเดียว)

Fallacy 6

Figure 9-12. มี network administrator หลายคน ไม่ใช่แค่คนเดียว

สถาปนิกมักตกหลุมพราง fallacy ข้อนี้อยู่เสมอ นั่นคือสันนิษฐานว่าตนต้องร่วมมือและสื่อสารกับ administrator เพียงคนเดียวเท่านั้น ดังที่ Figure 9-12 แสดงให้เห็น บริษัทใหญ่ทั่วไปมี network administrator อยู่หลายสิบคน แล้วสถาปนิกควรคุยกับใครเรื่อง latency หรือการเปลี่ยนแปลง topology? fallacy ข้อนี้ชี้ให้เห็นถึงความซับซ้อนของ distributed architecture และปริมาณการประสานงานที่ต้องเกิดขึ้นเพื่อให้ทุกอย่างทำงานได้อย่างถูกต้อง แอปพลิเคชันแบบ monolithic ที่มี deployment unit เดียวไม่ต้องการการสื่อสารและความร่วมมือในระดับนี้

Fallacy #7: Transport Cost Is Zero (Fallacy ข้อที่ 7: ต้นทุนการขนส่งเท่ากับศูนย์)

Fallacy 7

Figure 9-13. การเข้าถึงแบบ remote มีค่าใช้จ่าย

สถาปนิกซอฟต์แวร์หลายคนมักสับสน fallacy ข้อนี้ ซึ่งแสดงใน Figure 9-13 กับ fallacy ข้อ 2 (latency เท่ากับศูนย์) Transport cost ในที่นี้ไม่ได้หมายถึง latency แต่หมายถึง ค่าใช้จ่ายที่เป็นตัวเงิน จริง ๆ ในการทำ “simple RESTful call” สักครั้งหนึ่ง สถาปนิกมักสันนิษฐานผิด ๆ ว่ามี infrastructure ที่จำเป็นและเพียงพอพร้อมอยู่แล้วสำหรับการทำ simple RESTful call หรือการแตก monolithic application ออกเป็นส่วน ๆ แต่โดยทั่วไปแล้วไม่ใช่แบบนั้น distributed architecture มีค่าใช้จ่ายสูงกว่า monolithic architecture อย่างมาก ส่วนใหญ่เป็นเพราะความต้องการที่เพิ่มขึ้นด้าน hardware, server, gateway, firewall, subnet ใหม่ ๆ, proxy และอื่น ๆ

เราขอแนะนำให้สถาปนิกที่กำลังจะเริ่มทำ distributed architecture วิเคราะห์ server และ network topology ปัจจุบันของตนในแง่ของ capacity, bandwidth, latency และ security zone เพื่อหลีกเลี่ยงความประหลาดใจจาก fallacy ข้อนี้

Fallacy #8: The Network Is Homogeneous (Fallacy ข้อที่ 8: เครือข่ายมีความเป็นเนื้อเดียวกัน)

Fallacy 8

Figure 9-14. เครือข่ายไม่ได้มีความเป็นเนื้อเดียวกัน

สถาปนิกและนักพัฒนาส่วนใหญ่สันนิษฐานว่าเครือข่ายมีความเป็นเนื้อเดียวกัน ดังที่แสดงใน Figure 9-14 คือประกอบขึ้นจาก network hardware ของผู้ผลิตรายเดียวเท่านั้น แต่ความจริงกลับตรงข้ามอย่างสิ้นเชิง infrastructure ของบริษัทส่วนใหญ่มี network-hardware หลายผู้ผลิตปะปนกันอยู่

แล้วมันสำคัญอย่างไร? ความสำคัญของ fallacy ข้อนี้คือ hardware จากผู้ผลิตที่แตกต่างกันไม่ได้ทำงานร่วมกันได้ดีเสมอไป hardware ของ Juniper Networks จะทำงานร่วมกับ hardware ของ Cisco Systems ได้อย่างไร้รอยต่อหรือไม่? ส่วนใหญ่ก็ทำงานได้ และมาตรฐานเครือข่ายก็พัฒนาขึ้นมากตลอดหลายปีที่ผ่านมา ทำให้ปัญหานี้ลดน้อยลง อย่างไรก็ตาม ไม่ใช่ทุกสถานการณ์ ทุกโหลด และทุกเงื่อนไขที่ได้รับการทดสอบอย่างครบถ้วน ดังนั้น network packet จึงยังคงสูญหายได้เป็นครั้งคราว ซึ่งส่งผลกระทบต่อความน่าเชื่อถือของเครือข่ายและสมมติฐานหรือข้อกล่าวอ้างเกี่ยวกับ latency และ bandwidth พูดอีกอย่างคือ fallacy ข้อนี้ย้อนกลับไปเชื่อมโยงกับ fallacy ข้ออื่น ๆ ทั้งหมด กลายเป็นวงจรไม่รู้จบของความสับสนและความหงุดหงิดเมื่อต้องรับมือกับเครือข่าย (ซึ่งเป็นสิ่งที่หลีกเลี่ยงไม่ได้เมื่อใช้ distributed architecture)

The Other Fallacies (Fallacy ข้ออื่น ๆ)

fallacy ทั้งแปดข้อที่กล่าวไปแล้วรวมกันเป็นชุดข้อสังเกตที่มีชื่อเสียง สถาปนิกทุกคนต่างเรียนรู้มันจากรายการของ Deutsch หรือไม่ก็เรียนรู้แบบเจ็บตัวทีละข้อตลอดเส้นทางอาชีพของตน ผู้เขียนเองก็ได้เรียนรู้บทเรียนที่เจ็บปวดและเป็นสากลอยู่บ้าง ซึ่งเราขอนำเสนอเพิ่มเติมจากรายการที่มีชื่อเสียงนี้

Fallacy #9. Versioning is easy (Fallacy ข้อที่ 9: การทำ Versioning เป็นเรื่องง่าย)

เมื่อ service สองตัวต้องสื่อสารกัน พวกมันจะส่งข้อมูลผ่าน contract ซึ่งประกอบด้วยข้อมูลที่จำเป็นสำหรับการสื่อสารนั้น หลายครั้ง implementation ภายในของ service จะพัฒนาเปลี่ยนแปลงไปตามเวลา ทำให้ field ที่ service ยอมรับและส่งต่อไปยัง service อื่นเปลี่ยนไปด้วย วิธีหนึ่งในการแก้ปัญหานี้คือการทำ versioning ให้กับ contract นั่นคือสร้าง contract คนละเวอร์ชันสำหรับแบบเก่าและแบบใหม่ ซึ่งมีชุดข้อมูลต่างกัน อย่างไรก็ตาม การตัดสินใจที่ดูเหมือนง่ายนี้กลับนำไปสู่ trade-off มากมาย:

  • ทีมควรทำ versioning ในระดับ service แต่ละตัว หรือทำสำหรับทั้งระบบ?

  • versioning ควรครอบคลุมไปไกลแค่ไหน? ส่วนใดของ architecture ที่ต้องรองรับมันบ้าง?

  • ทีมควรรองรับกี่เวอร์ชันในเวลาใดเวลาหนึ่ง? (บางทีมพบว่าตัวเองต้องดูแลหลายสิบเวอร์ชันสำหรับวัตถุประสงค์ต่าง ๆ กันโดยไม่ได้ตั้งใจ)

  • ทีมควร deprecate เวอร์ชันเก่าในระดับระบบ หรือทำทีละ service ?

แม้ว่า versioning จะเป็นแนวทางที่สมเหตุสมผลสำหรับการพัฒนาการสื่อสารระหว่าง service แต่ก็มี trade-off มากมายที่สถาปนิกควรคาดการณ์ไว้ล่วงหน้า

Fallacy #10. Compensating updates always work (Fallacy ข้อที่ 10: Compensating Update ใช้ได้ผลเสมอ)

Compensating update เป็น architectural pattern ที่มีกลไกบางอย่าง (เช่น service Orchestrator ) คอยตรวจสอบให้แน่ใจว่า service ที่เกี่ยวข้องกันหลายตัวจะอัปเดตพร้อมกันทั้งหมด ถ้าไม่เป็นเช่นนั้น orchestrator ก็จะย้อนกลับการอัปเดตนั้น compensating update คือการที่ orchestrator ออกคำสั่งย้อนกลับเพื่อคืนสถานะให้กลับไปเป็นเหมือนเดิมก่อนหน้านั้น

นี่เป็น pattern ที่พบได้ทั่วไปซึ่งสถาปนิกส่วนใหญ่มักสันนิษฐานอย่างไม่ทันคิดว่ามันจะได้ผลเสมอ... แต่ในความเป็นจริงไม่ใช่ แล้วจะเกิดอะไรขึ้นถ้า compensating update ล้มเหลว? เมื่อสถาปนิกอธิบายว่าการโต้ตอบที่ซับซ้อนใน distributed architecture อย่าง microservices ทำงานอย่างไร พวกเขาก็ต้องแสดงให้เห็นด้วยว่า compensating update ทำงานอย่างไร ดังนั้นสถาปนิกที่ออกแบบ transactional workflow ใน microservices จึงควรรองรับ compensation workflow แบบ “ปกติ” แต่ก็ต้องพิจารณาด้วยว่าจะกู้คืนอย่างไรหากทั้งการอัปเดตและ compensating update (หรือบางส่วนของมัน) ทั้งคู่ ล้มเหลว

Fallacy #11. Observability is optional (for distributed architectures) (Fallacy ข้อที่ 11: Observability เป็นทางเลือก (สำหรับ Distributed Architecture))

คุณลักษณะทางสถาปัตยกรรมที่พบได้ทั่วไปซึ่งสถาปนิกควรให้ความสำคัญใน distributed architecture คือ observability ความสามารถในการสังเกตการโต้ตอบของแต่ละ service กับ service อื่น ๆ และกับ ecosystem โดยจับข้อมูลผ่าน monitor หรือ log ในขณะที่ logging มีประโยชน์ใน monolithic architecture แต่มันกลับเป็นสิ่ง สำคัญยิ่ง ใน distributed architecture ซึ่งมี failure mode ในการสื่อสารมากมายที่ debug ได้ยากหากไม่มี interaction log ที่ครอบคลุม