Distributed Systems · บทที่ 2 จาก 11

Interprocess Communication

ก่อนที่ process สอง process จะ "ร่วมมือ" กันได้ในระบบกระจาย ต้องมีกลไกส่งข้อมูลระหว่างกันก่อน — บทนี้ลงลึกตั้งแต่ socket ระดับล่างไปจนถึงการแปลงข้อมูลให้เป็นมาตรฐานกลาง (marshalling) และการส่งข้อความแบบกลุ่ม (multicast)

📚
อิงเนื้อหาจาก Coulouris บทที่ 4 (Interprocess Communication) — สำหรับพื้นฐาน socket/TCP/UDP ที่ละเอียดกว่านี้ ดูวิชา Network Computing หัวข้อที่ 1 (Network Programming) ประกอบ บทนี้เน้นสิ่งที่ Coulouris เพิ่มเข้ามาเหนือกว่าระดับ socket ธรรมดา คือ marshalling และ multicast

1Message Passing: พื้นฐานที่สุดของการสื่อสาร

Operation คู่พื้นฐาน
send(destination, message) และ receive(source, message) — ฝั่งผู้รับมักมีคิว (queue) เก็บข้อความที่ส่งมาถึงแล้วรอให้โปรแกรมมาอ่าน
รูปแบบพฤติกรรม
Synchronous communicationผู้ส่งหยุดรอ (block) จนกว่าข้อความจะถูกส่งมอบให้ผู้รับสำเร็จ (หรือบางนิยามคือรอจน receive ถูกเรียก) — เข้าใจง่าย แต่ผูก sender กับ receiver ไว้ด้วยกันตลอดเวลา
Asynchronous communicationผู้ส่งส่งข้อความแล้วทำงานต่อทันที ไม่ต้องรอผู้รับมาอ่าน — มีกลไกแจ้งภายหลังเมื่อข้อความถูกอ่านแล้ว (เช่น callback หรือ future)
เชื่อมโยงกับ Network Computing
Synchronous/Asynchronous ในบทนี้คือรากศัพท์เดียวกับ "Request–Response" กับ "Asynchronous Messaging" ในวิชา Network Computing หัวข้อที่ 4 (Microservice Patterns) — pattern อย่าง Message Queue และ Pub-Sub คือ asynchronous communication ที่ implement ในระดับ middleware

2Socket, UDP, TCP — ทบทวนสั้น ๆ

รายละเอียดระดับโค้ด (bind/listen/accept, byte-stream ของ TCP, packet framing) อยู่ในวิชา Network Computing หัวข้อที่ 1 แล้ว บทนี้สรุปเฉพาะคุณสมบัติที่ต้องใช้ต่อในบทถัดไป

โพรโทคอลคุณสมบัติผลต่อการออกแบบ middleware
UDPConnectionless, ไม่มี acknowledgement, ไม่มี retry, ไม่มีการเรียงลำดับ; มี checksum ตรวจสอบความเสียหายแล้วทิ้งข้อความที่เสียเหมาะกับ multicast และงานที่ทนต่อข้อความหายได้ (เช่น video streaming, discovery protocol)
TCPConnection-oriented, มี flow control, retransmit อัตโนมัติเมื่อ packet หาย, รับประกันลำดับไบต์เหมาะกับ request-reply ที่ต้องการความน่าเชื่อถือ — เป็นฐานของ RPC/RMI ในบทที่ 3

3External Data Representation และ Marshalling

ปัญหาที่ต้องแก้
เครื่องต่างสถาปัตยกรรมอาจมี byte ordering ต่างกัน (Big-Endian vs Little-Endian) และการแทนค่าตัวเลขทศนิยม/ตัวอักษรต่างกัน — ข้อมูลที่ส่งข้ามเครือข่ายจึงต้องแปลงเป็นรูปแบบกลาง (external data representation) ที่ทั้งสองฝั่งตกลงร่วมกัน
Marshalling

แปลงข้อมูลในหน่วยความจำ (structure/object) ให้เป็นลำดับไบต์ที่พร้อมส่งผ่านเครือข่าย

Unmarshalling

แปลงลำดับไบต์ที่ได้รับกลับมาเป็นโครงสร้างข้อมูลที่ใช้งานต่อได้ในฝั่งผู้รับ

แนวทางลักษณะ
CORBA CDR (Common Data Representation)ระบุ byte order ตามฝั่งผู้ส่ง ข้อมูลแต่ละตัวไม่มี type กำกับมาด้วย (client/server ต้องรู้ type ล่วงหน้าจาก IDL)
Java Serializationแปลง object เป็น byte stream โดยอัตโนมัติผ่าน implements Serializable — มีข้อมูล class กำกับมาด้วย ทำให้ deserialize ฝั่งรับรู้ชนิดได้เอง
ทันสมัย: มาตรฐานที่ใช้จริงในปัจจุบัน
รูปแบบลักษณะ
JSONText-based, มนุษย์อ่านได้ ไม่ต้องมี schema ตายตัว แลกกับขนาดใหญ่กว่าและ parse ช้ากว่า binary — มาตรฐานของ REST API (ดู Network Computing หัวข้อที่ 2)
Protocol Buffers (protobuf)Binary, กำหนด schema ล่วงหน้าด้วย .proto file คล้าย IDL ของ CORBA แต่กระชับกว่ามาก ขนาดข้อมูลเล็กและเร็วกว่า JSON — ใช้เป็นมาตรฐานของ gRPC (ดูบทที่ 3)
Apache Thrift / Avroแนวทางคล้าย protobuf จาก Facebook/Apache — schema-based binary serialization สำหรับ RPC ข้ามภาษา
หลักการพื้นฐานไม่เปลี่ยน (ต้องมีรูปแบบกลางที่ตกลงร่วมกัน) แต่ implementation เปลี่ยนจาก CORBA CDR ไปเป็น protobuf/JSON ที่เบากว่าและเข้ากับ web ได้ดีกว่า

4Request–Reply Protocol

แนวคิด
โพรโทคอลระดับสูงกว่า TCP/UDP ที่ออกแบบมาสำหรับรูปแบบ "ถาม-ตอบ" โดยเฉพาะ เป็นฐานของ RPC และ RMI ในบทถัดไป

รูปแบบข้อความประกอบด้วย: ชนิดข้อความ (Request/Reply), หมายเลขข้อความ (message ID) เรียงตามลำดับที่ส่ง, remote reference ของ object/service ที่เรียก, หมายเลข method ที่เรียก, และ argument ที่ส่งไป

ความเชื่อถือได้ของ Request-Reply บน TCP
เพราะ TCP รับประกันการส่งมอบอยู่แล้ว protocol ระดับบนจึงต้องเพิ่มแค่:
timeout สำหรับข้อความที่หาย + ตรวจสอบ duplicate request จาก message ID
สองกลไกนี้คือรากฐานของ at-least-once และ at-most-once invocation semantics ที่จะขยายความในบทที่ 3
เชื่อมโยงกับ Network Computing
Timeout และการตรวจจับ duplicate request ด้วย message ID คือแนวคิดเดียวกับ Idempotency Key ที่สอนในวิชา Network Computing หัวข้อที่ 3 — Coulouris เขียนไว้ในระดับ protocol ทั่วไป ส่วน Network Computing สอนวิธี implement จริงในระดับ HTTP API

5Group Communication และ Multicast

ทำไมต้องมี
บางสถานการณ์ต้องส่งข้อความเดียวกันไปยังกลุ่มผู้รับหลายคนพร้อมกัน แทนที่จะ loop ส่งทีละคน (unicast ซ้ำ ๆ) — ใช้ operation เดียวคือ multicast(group, message)
Fault tolerance

ส่งอัปเดตไปยังกลุ่ม replica เพื่อให้ทุกสำเนามีค่าตรงกัน (เชื่อมกับบทที่ 9)

Service/Object discovery

ถามหากลุ่มเครื่องว่าใครมี object หรือ service ที่ต้องการอยู่บ้าง

Replicated performance

อ่านข้อมูลจาก replica ที่ใกล้/ว่างที่สุดได้ เพราะทุก replica ได้รับข้อความอัปเดตเดียวกัน

Event notification

แจ้งกลุ่ม process ที่สนใจเหตุการณ์เดียวกัน — บรรพบุรุษของ Pub-Sub

ปัญหาของ IP Multicast บน UDP

ข้อจำกัด
IP Multicast สืบทอดปัญหาของ UDP มาเต็ม ๆ: (1) Omission failure — ข้อความอาจไปไม่ถึงสมาชิกบางคนในกลุ่ม (2) ไม่รับประกันลำดับ — ข้อความจากผู้ส่งเดียวกันอาจถึงสมาชิกแต่ละคนในลำดับต่างกัน (3) Multicast router ที่ทำงานผิดพลาดได้ ทำให้บาง subnet ไม่ได้รับข้อความเลย

เพราะข้อจำกัดเหล่านี้กระทบการใช้งานจริงโดยตรง (กลุ่ม replica ต้องได้ค่าตรงกันทุกตัว ไม่ใช่บางตัว) จึงต้องมี multicast ระดับที่แข็งแรงกว่า

Reliable Multicast และ Ordered Multicast

ประเภทการรับประกัน
Reliable multicastถ้า process หนึ่งในกลุ่มได้รับข้อความ ทุก process ที่ยังทำงานอยู่ในกลุ่มต้องได้รับข้อความเดียวกันนั้นด้วย (integrity + validity + agreement)
FIFO orderingข้อความจากผู้ส่งคนเดียวกันถึงผู้รับทุกคนตามลำดับที่ส่งจริง
Causal orderingถ้าการส่งข้อความ A "เป็นเหตุ" ให้เกิดการส่งข้อความ B ทุกคนต้องได้รับ A ก่อน B เสมอ
Total orderingทุก process ในกลุ่มได้รับข้อความทั้งหมดตามลำดับเดียวกัน ไม่ว่าจะเป็นลำดับใดก็ตาม (arbitrary but consistent)
ต่อยอดในบทที่ 10
กลไกที่ใช้สร้าง reliable/ordered multicast จริง (B-multicast, R-multicast พร้อม sequencer หรือ vector timestamp) จะอธิบายละเอียดในบทที่ 10 (Coordination and Agreement) เพราะเป็นปัญหาเดียวกับการตกลงลำดับเหตุการณ์แบบกระจาย

6ทันสมัย: จาก Multicast สู่ Message Broker

ระบบยุคปัจจุบันไม่ค่อยใช้ IP multicast ตรง ๆ อีกต่อไป (เพราะ ISP และ cloud ส่วนใหญ่ไม่ route multicast traffic ข้าม subnet) แต่ปัญหาเดิม — ส่งข้อความเดียวกันให้กลุ่มผู้รับพร้อมกันอย่างเชื่อถือได้และมีลำดับ — ยังต้องแก้อยู่ จึงย้ายไปแก้ที่ระดับ application ผ่าน message broker

เทคโนโลยีแก้ปัญหาเดิมอย่างไร
Apache KafkaTopic แบบ partition เก็บ log เรียงลำดับต่อ partition (คล้าย FIFO ordering) ผู้ subscribe หลายคนอ่าน log เดียวกันได้ (คล้าย reliable multicast แต่ทำงานผ่าน broker กลางแทนเครือข่าย)
RabbitMQ / Message QueuePub-Sub และ Competing Consumers (ดู Network Computing หัวข้อที่ 4) แก้ปัญหาการกระจายงานและแจ้งเหตุการณ์แทน multicast ระดับเครือข่าย
ข้อสังเกตสำคัญ
Message broker สมัยใหม่คือ "multicast ที่ implement ในระดับ application แทนระดับเครือข่าย" — ได้ความน่าเชื่อถือและการควบคุมลำดับที่ IP multicast ให้ไม่ได้ แลกกับการมี broker เป็น component เพิ่มเติมที่ต้อง manage เอง
คำถามซ้อมสอบ
  1. อธิบายความแตกต่างระหว่าง synchronous กับ asynchronous communication และเชื่อมโยงกับ Request-Response/Async Messaging ในวิชา Network Computing
  2. ทำไมข้อมูลที่ส่งข้ามเครือข่ายจึงต้อง marshal ก่อนเสมอ ยกตัวอย่างปัญหาที่เกิดถ้าไม่ทำ
  3. เปรียบเทียบ CORBA CDR กับ Protocol Buffers ว่าแก้ปัญหาเดียวกันอย่างไรต่างกัน
  4. อธิบายว่า timeout และการตรวจ duplicate request ใน Request-Reply Protocol เกี่ยวข้องกับ idempotency อย่างไร
  5. อธิบายว่าทำไม IP multicast บน UDP จึงไม่เพียงพอสำหรับกลุ่ม replica ที่ต้องการความสอดคล้องของข้อมูล และยกตัวอย่างเทคโนโลยีสมัยใหม่ที่แก้ปัญหานี้แทน

7Message ไม่ใช่เพียงข้อมูล แต่เป็นข้อตกลง

เมื่อเริ่มเขียนโปรแกรมเครือข่าย เรามักมอง message เป็นข้อความหรือ byte ที่ต้องส่งให้ถึงปลายทาง แต่ในระบบกระจาย message มีอย่างน้อยสี่ส่วนซ้อนกันอยู่: รูปแบบข้อมูล ความหมายของคำสั่ง identity ของคำขอ และกติกาเมื่อเกิด failure ถ้าตกลงเพียง JSON field แต่ไม่ตกลงว่าคำสั่งส่งซ้ำได้หรือไม่ protocol ยังไม่สมบูรณ์

Metaphor: จดหมายในซอง
ข้อมูลเหมือนเนื้อจดหมาย serialization คือวิธีพับและเขียนภาษา framing คือซองที่บอกว่าจดหมายหนึ่งฉบับจบตรงไหน address พาไปหาผู้รับ ส่วน protocol บอกว่าจดหมายนี้เป็นคำขอ ใบตอบรับ หรือใบแจ้งข้อผิดพลาด ต่อให้ไปรษณีย์ส่งซองถึงถูกบ้าน ผู้รับก็ยังทำงานไม่ได้ถ้าอ่านภาษาไม่ออกหรือไม่รู้ว่าต้องตอบอย่างไร
ส่วนของสัญญาคำถามที่ต้องตอบตัวอย่าง
Syntaxfield, type, encoding และ framing คืออะไรProtobuf message มี order_id เป็น string
Semanticsคำสั่งหมายถึงอะไร state เปลี่ยนเมื่อใดCreateOrder สำเร็จเมื่อรับเรื่องหรือเมื่อตัดเงินแล้ว
Identityข้อความนี้สัมพันธ์กับ request/event ใดrequest_id, correlation_id, event_id
Failuretimeout, retry, duplicate และ error แปลว่าอะไรdeadline exceeded ไม่ยืนยันว่า operation ไม่เกิด
Evolutionเพิ่ม/เลิก field โดยไม่ทำให้ของเดิมพังอย่างไรoptional field และ version compatibility

Command, Event และ Document ต่างกันที่เจตนา

Command ขอให้อีกฝ่ายทำบางอย่างและอาจถูกปฏิเสธ เช่น ReserveStock ส่วน Event บอกว่าเหตุการณ์เกิดขึ้นแล้ว เช่น StockReserved ผู้รับไม่ควร “ปฏิเสธอดีต” แต่เลือกว่าจะตอบสนองอย่างไร ขณะที่ Document/message บางชนิดเพียงส่งข้อมูลโดยไม่เน้นคำสั่งหรือเหตุการณ์

การตั้งชื่อผิดทำให้ flow สับสน ถ้าส่ง event ชื่อ CreateOrder แต่จริง ๆ ต้องการให้ consumer ตัดสินว่าจะสร้างหรือไม่ เรากำลังซ่อน command ในเสื้อ event และจะตอบ error ยาก ในทางกลับกัน event ควรใช้ past tense เพื่อสื่อว่าข้อเท็จจริงเกิดแล้ว ไม่ใช่คำขอที่ยังรออนุมัติ

8Transport property กับ Application guarantee เป็นคนละชั้น

TCP รับประกัน byte stream ที่เรียงลำดับและส่งซ้ำ packet ที่หายใน connection เดียว แต่ไม่ได้รับประกันว่า business operation เกิดครั้งเดียว ถ้า server ทำงานเสร็จแล้ว connection ขาดก่อน client ได้ response client อาจเปิด connection ใหม่และส่ง request ซ้ำ TCP ทั้งสอง connection ทำงานถูกต้อง แต่ application สร้าง order ซ้ำได้

สิ่งที่ต้องการTransport ช่วยอะไรApplication ยังต้องทำอะไร
ข้อมูลไม่เสียระหว่าง connectionTCP checksum, sequence, retransmissionvalidation และ integrity ที่ข้าม storage/processing
ข้อความหนึ่งฉบับTCP ไม่มี message boundarydelimiter, length prefix หรือ protocol framing
ทำงานไม่ซ้ำTCP ไม่รู้ business operationidempotency key, deduplication, unique constraint
ส่งถึงในที่สุดconnection มีขอบเขตอายุdurable queue, retry policy, poison-message handling
ลำดับข้ามหลาย producerTCP เรียง byte ต่อ connectionpartition key, sequence/causal metadata หรือ consensus

UDP กลับกัน มันรักษาขอบเขต datagram แต่ไม่รับประกันการมาถึง ลำดับ หรือการไม่ซ้ำ จึงเหมาะเมื่อ application ยอมเสียข้อมูลบางส่วนได้ ต้องการ latency ต่ำ หรือสร้าง reliability ที่เฉพาะกับงานเอง เช่น realtime media สนใจ packet ปัจจุบันมากกว่า packet เก่าที่ retransmit มาช้า

Backpressure: เมื่อผู้รับช้ากว่าผู้ส่ง

การสื่อสารไม่ได้มีเพียงส่งสำเร็จ/ล้มเหลว ยังมีกรณีส่งได้เร็วเกินกว่าผู้รับจะประมวลผล ถ้าไม่มี backpressure ข้อมูลสะสมใน memory, socket buffer หรือ queue จน latency สูงและระบบล้มในที่สุด การเพิ่ม buffer เพียงเลื่อนเวลาล้ม ไม่ได้แก้สมการอัตราเข้าเกินอัตราออก

แนวทางมีทั้ง block ผู้ส่ง, จำกัดจำนวน in-flight request, ใช้ bounded queue, ส่ง credit จาก consumer, ลดคุณภาพ/ทิ้งข้อมูลบางประเภท หรือปฏิเสธด้วย status ที่ชัดเจน การเลือกขึ้นกับ semantics: transaction การเงินทิ้งไม่ได้ แต่ telemetry บางชนิดอาจ sample ได้ และ video realtime อาจทิ้ง frame เก่าดีกว่าเล่นตามหลังไปเรื่อย ๆ

9Marshalling: ความยากไม่ได้จบเมื่อ encode/decode ได้

External Data Representation แก้ความต่างเรื่อง byte order, primitive type และโครงสร้างข้อมูลระหว่างเครื่อง แต่ระบบที่มีอายุยาวต้องรับมือ schema evolution ด้วย producer และ consumer ไม่ได้ upgrade พร้อมกันเสมอ ช่วง deployment จะมีทั้ง version เก่าและใหม่ทำงานพร้อมกัน

การเปลี่ยน schemaความเสี่ยงแนวทาง
เพิ่ม optional fieldconsumer เก่าไม่รู้จักกำหนดให้ข้าม unknown field และมี default ที่ปลอดภัย
ลบ fieldproducer/consumer เก่ายังใช้เลิกใช้เป็นช่วงก่อนลบจริง ไม่ reuse field number ใน Protobuf
เปลี่ยน typeparse ได้แต่ความหมายผิดหรือ overflowเพิ่ม field ใหม่และ migrate แทนการเปลี่ยนเงียบ ๆ
เปลี่ยนความหมายsyntax compatible แต่ semantic incompatibleตั้งชื่อ/version ใหม่และประกาศ contract ชัด
เปลี่ยนหน่วยตัวเลขดูถูกแต่ต่างหน่วยใส่หน่วยในชื่อ/schema และใช้ type ที่บังคับได้
ตัวอย่างที่ JSON ช่วยไม่ได้
field timeout: 5 parse ได้ทุกภาษา แต่ 5 หมายถึงวินาที มิลลิวินาที จำนวนครั้ง หรือเวลาสิ้นสุด? Serialization ทำให้รูปแบบเดินทางได้ ไม่ได้ทำให้ความหมายตรงกัน Contract ต้องระบุ semantics ที่เครื่องมือ encode ไม่สามารถเดาแทนเรา

Text กับ Binary ไม่ได้มีคำตอบเดียว

JSON อ่านและตรวจสอบด้วยคนง่าย เชื่อมกับ browser/tooling กว้าง และเหมาะกับ public API แต่มีขนาดมากกว่า type หลวมกว่า และตัวเลข/วันที่ต้องกำหนด convention เพิ่ม Binary format อย่าง Protobuf กระชับ เร็ว มี schema และ generate code ได้ แต่ debug ด้วยตาเปล่ายากและต้องจัดการ schema registry/version ให้ดี

อย่าเลือกด้วยคำว่าเร็วกว่าเพียงอย่างเดียว ถ้า payload เล็กและ latency ส่วนใหญ่อยู่ที่ database การเปลี่ยน JSON เป็น binary อาจแทบไม่ช่วย แต่เพิ่ม operational complexity ในทางกลับกัน stream ปริมาณสูงหรือ mobile link อาจคุ้มมาก ต้องวัด end-to-end ไม่ใช่ benchmark serialization แยกจากระบบ

Canonical representation และความปลอดภัย

ข้อมูลเดียวกันอาจ serialize ได้หลายแบบ เช่นลำดับ field หรือรูปแบบเลขต่างกัน ถ้าจะ hash/sign message ต้องกำหนด canonical representation ไม่เช่นนั้นสองฝ่ายเห็น semantic เดียวกันแต่ได้ byte ต่างกันและตรวจลายเซ็นไม่ผ่าน นอกจากนี้ decoder ต้องจำกัดขนาด ความลึก และจำนวน element เพราะ input จากเครือข่ายไม่ควรได้รับความเชื่อใจ

10Request–Reply เป็น Protocol เล็ก ๆ ที่มี State

ภาพพื้นฐานคือ client ส่ง request แล้ว server ส่ง reply แต่ implementation จริงต้องตอบเรื่อง request ID, duplicate, timeout, retry, cache reply และการจับคู่ response กับ request เมื่อมีหลายคำขอพร้อมกัน ถ้าใช้ connection เดียวแบบ multiplex ต้องมี correlation ID เพราะลำดับ response อาจไม่ตรงกับลำดับ request

Request  { request_id, method, deadline, metadata, body }
Reply    { request_id, status, metadata, body | error }

request_id ใช้จับคู่และ deduplicate deadline บอกเวลาที่ผลยังมีประโยชน์ status แยก application rejection ออกจาก transport failure และ metadata ส่ง authentication, trace context หรือ version โดยไม่ปน business body

Invocation semantics จากมุม request–reply

Semanticsพฤติกรรมเหมาะกับสิ่งที่ยังไม่ได้รับประกัน
Maybeส่งครั้งเดียว ไม่ retrytelemetry ที่เสียบางจุดได้ไม่รู้ว่าทำหรือไม่
At-least-onceretry จนได้ reply/หมดนโยบายidempotent operationอาจทำซ้ำ
At-most-onceserver ตรวจ request ID และคืนผลเดิมงานที่ duplicate อันตรายอาจไม่ทำเลยถ้า retry ไม่ถึง
Exactly-once effectผูก dedupe กับ state change แบบ atomicbusiness invariant สำคัญไม่ได้เกิดจาก network guarantee ตัวเดียว

คำว่า exactly-once มักใช้เกินจริง ในระบบทั่วไปเราสร้างผลที่ดูเหมือนครั้งเดียวด้วย unique key, transaction, idempotent consumer และการเก็บผลลัพธ์ ไม่ได้ทำให้ packet หรือ handler ถูกเรียกเพียงครั้งเดียวทางกายภาพ ความต่างนี้สำคัญเวลาวิเคราะห์ failure ตรงช่วงระหว่าง “ทำงานแล้ว” กับ “บันทึกว่าเคยทำแล้ว”

Deadline propagation

ถ้า Client ให้เวลา Gateway 2 วินาที และ Gateway เรียก A→B→C แต่ทุก hop ตั้ง timeout 2 วินาทีแยกกัน ระบบอาจทำงานต่อเป็นหลายวินาทีหลัง client เลิกรอ Deadline ควรเป็น budget ที่ลดลงตามสาย ผู้รับตรวจเวลาที่เหลือก่อนเริ่มงาน และยกเลิกงานที่ไม่มีผู้ใช้รอถ้างานนั้นยกเลิกได้อย่างปลอดภัย

อย่างไรก็ตาม cancellation เป็น request ไม่ใช่เครื่องย้อนเวลา งานอาจผ่านจุด commit ไปแล้ว การกดยกเลิกหน้าจอไม่ได้รับประกันว่าธุรกรรมไม่เกิด API ต้องแยก cancel-before-commit, cancel-requested และ compensate-after-commit ให้ชัด

11Group Communication: หนึ่งข้อความ หลายมุมมอง

การส่งให้กลุ่มดูเหมือนเพิ่มผู้รับจากหนึ่งเป็นหลายคน แต่ปัญหาใหม่คือ membership, ordering และ delivery semantics สมาชิกคนใดถือว่าอยู่ในกลุ่มขณะส่ง ถ้าสมาชิก join/leave ระหว่างทางควรได้รับหรือไม่ และผู้รับทุกคนต้องเห็นข้อความลำดับเดียวกันหรือเพียงลำดับของผู้ส่งแต่ละคน

Orderingรับประกันกรณีใช้
Unorderedไม่กำหนดลำดับmetric อิสระที่รวมภายหลังได้
FIFO per senderข้อความจาก producer เดียวไม่สลับevent stream ต่อ device/user
Causalถ้า B เกิดเพราะ A ทุกคนเห็น A ก่อน Bcollaboration/comment-reply
Total orderสมาชิกทุกคนเห็นลำดับเดียวกันreplicated state machine

Guarantee ที่เข้มขึ้นต้องจ่าย coordination และ latency มากขึ้น ถ้างานไม่ต้องการ total order การบังคับทุก event ผ่าน sequencer เดียวสร้างคอขวดโดยไม่เพิ่ม correctness ที่มีความหมาย ตัวอย่างเช่นยอด like จากผู้ใช้ต่างคนอาจรวมแบบ commutative ได้ แต่การเปลี่ยนสถานะ order อาจต้องควบคุมลำดับมากกว่า

Reliable multicast ไม่ได้หมายถึงทุกคนได้พร้อมกัน

Reliable อาจหมายถึงสมาชิก non-faulty ได้รับในที่สุด แต่ไม่ได้รับประกันเวลาเดียวกัน ถ้าต้องให้สมาชิกเปลี่ยน view ของกลุ่มอย่างสอดคล้อง เราต้องมี membership protocol และนิยาม virtual synchrony หรือแนวคิดใกล้เคียง การใช้คำว่า broadcast โดยไม่ระบุสมาชิกและช่วงเวลาจึงคลุมเครือ

12จาก Network Multicast สู่ Broker และ Event Streaming

IP multicast ส่ง packet ไปยังกลุ่มได้มีประสิทธิภาพในเครือข่ายที่รองรับ แต่ application สมัยใหม่จำนวนมากใช้ broker เพราะต้องการ persistence, consumer state, filtering, retry และการเชื่อมระบบข้าม administrative domain Broker ไม่ได้แทน multicast ทุกกรณี แต่มันยก semantics หลายอย่างขึ้นมาไว้ระดับ application

รูปแบบผู้รับการเก็บข้อความการกระจายงาน
Queueโดยทั่วไป consumer หนึ่งคนต่อ messageเก็บจน ack/หมดอายุcompeting consumers แบ่งงาน
Publish–Subscribeทุก subscription ที่ตรงเงื่อนไขขึ้นกับ brokerกระจาย event ไปหลายความสนใจ
Log/Streamconsumer group อ่านตาม offset ของตนเก็บตาม retentionอ่านซ้ำและสร้าง state ใหม่ได้
IP multicastสมาชิก multicast group บน networkโดยตัวมันไม่เก็บส่ง packet มีประสิทธิภาพ

Acknowledgment ย้ายความรับผิดชอบ ไม่ได้ลบ failure

ถ้า consumer ประมวลผลแล้ว crash ก่อน ack broker จะส่งซ้ำ ถ้า ack ก่อนประมวลผลแล้ว crash งานอาจหาย ดังนั้นรูปแบบทั่วไปคือประมวลผลและบันทึกผลให้ idempotent แล้วค่อย ack แต่ช่วงระหว่าง commit กับ ack ยังทำให้ส่งซ้ำได้อยู่

Dead Letter Queue ช่วยแยก message ที่ล้มซ้ำไม่ให้ block งานทั้งหมด แต่ DLQ ไม่ใช่ถังขยะที่ปล่อยไว้ตลอด ต้องมี owner, alert, วิธีตรวจข้อมูลส่วนตัว และขั้นตอน replay หลังแก้สาเหตุ Poison message อาจเกิดจาก schema incompatibility ซึ่ง retry ร้อยครั้งก็ไม่ดีขึ้น

Event schema และ consumer ที่เราไม่รู้จัก

Public event อาจมี consumer หลายทีมที่ producer ไม่รู้ การเปลี่ยนความหมายเงียบ ๆ จึงอันตรายกว่า API ที่รู้คู่สัญญา Event ควรมี schema governance, compatibility test, owner และเอกสารความหมาย ไม่ควรใส่ database row ทั้งแถวแล้วเรียกว่า domain event เพราะทำให้ consumer ผูกกับ schema ภายใน

13Workshop: ออกแบบ Protocol ที่ตั้งใจให้ Failure เกิด

สร้างระบบ Producer → Broker → Consumer สำหรับคำสั่งส่งอีเมลหรือสร้าง thumbnail โดยใช้เครื่องมือใดก็ได้ เป้าหมายไม่ใช่เพียงส่งได้ แต่ต้องแสดงพฤติกรรมเมื่อเกิด failure ตามขั้นตอนต่อไปนี้

  1. กำหนด message schema มี message_id, correlation_id, occurred_at, type, schema_version และ payload
  2. รัน happy path แล้วบันทึก latency และลำดับ log ทั้งสามส่วน
  3. ทำ consumer crash หลังทำงานสำเร็จแต่ก่อน ack พิสูจน์ว่า message ถูกส่งซ้ำ
  4. เพิ่ม deduplication ที่ผูกกับ side effect ไม่ใช่เก็บ ID ใน memory อย่างเดียว
  5. ส่ง payload ผิด schema และกำหนด retry limit/DLQ
  6. ทำ consumer ช้ากว่า producer วัด queue depth และ latency เพื่อเห็น backpressure
  7. เปลี่ยน schema เพิ่ม optional field โดยให้ consumer เก่ายังทำงาน
  8. เขียน timeline ว่าแต่ละฝ่ายรู้ข้อมูลอะไร ณ จุดที่ crash
หลักฐานสิ่งที่ต้องอธิบาย
Producer logส่ง message ใด สำเร็จระดับไหน ได้ broker acknowledgment หรือยัง
Broker statequeue depth, redelivery count, offset หรือ consumer lag
Consumer logเริ่ม/จบ side effect, dedupe decision, ack และ error
Business stateผลเกิดกี่ครั้ง ตรงกับ message delivery กี่ครั้งหรือไม่

14ข้อผิดพลาดในการออกแบบการสื่อสารที่พบเสมอ

15สรุปและขั้นตอนถัดไป

Interprocess Communication ไม่ได้จบที่เลือก TCP, UDP หรือ broker เพราะ transport รู้เพียง byte/datagram ส่วน application ต้องกำหนด message boundary, schema, meaning, identity, deadline และพฤติกรรมเมื่อส่งซ้ำหรือคำตอบหาย Serialization ทำให้ข้อมูลอ่านได้ข้ามภาษา แต่ไม่รับประกันว่าความหมายตรงกัน และ queue ทำให้ผู้ส่งกับผู้รับไม่ต้องพร้อมกัน แต่เพิ่มคำถามเรื่อง duplicate, ordering และ consumer lag

หลักคิดที่ควรติดไปคือแยก delivery ออกจาก effect ข้อความอาจส่งหลายครั้งแต่เราสร้างผลครั้งเดียวได้ด้วย idempotency หรือข้อความอาจส่งครั้งเดียวแต่ผลไม่เกิดเพราะ consumer crash การออกแบบต้องระบุให้ชัดว่ารับประกันตรงขอบเขตใด

ขั้นตอนถัดไปคือ Distributed Objects & Remote Invocation เราจะดูความพยายามยกระดับ request–reply ให้ดูเหมือนเรียก method ธรรมดา ความสะดวกนั้นสร้าง productivity สูง แต่ก็ซ่อน latency, partial failure และ invocation semantics จนกลายเป็นบทเรียนสำคัญของ API และ microservices ในปัจจุบัน