คู่มือพิมพ์เขียวเชิงลึกและการวิเคราะห์ทางเทคนิค: การสร้างพายุความพังจำลองเพื่อทดสอบกลไก Resilience (Timeout, Retry, Circuit Breaker และ Fallback) ในระดับไมโครเซอร์วิส
ชื่อบท/หัวข้อหลัก: Module 4: Chaos Engineering & Fault Injection (Latency, 5xx, Dropped Connections, Service-Scoped Chaos)
ตอบ: ในระบบ Microservices พฤติกรรมที่อันตรายที่สุดไม่ใช่ตอนที่ระบบตอบกลับถูกต้อง (Happy Path) แต่คือตอนที่ระบบปลายทางตอบช้า (Latency), ล่มเป็นช่วงๆ (HTTP 5xx) หรือสายหลุดกลางคัน (Dropped Connections) หากเราทดสอบเฉพาะ Happy Path เมื่อเกิดเหตุการณ์ความพังขึ้นจริงบน Production แอปพลิเคชันมักจะเกิด Cascading Failure บทนี้ตั้งชื่อว่า Chaos Engineering & Fault Injection เพราะ MockServer ช่วยให้เราสามารถจงใจฉีดความพังทลายในระดับเครือข่ายและบริการแบบจำกัดขอบเขต (Service-Scoped Chaos) เข้าไปในสภาพแวดล้อมการทดสอบ เพื่อพิสูจน์ว่ากลไก Resilience (Timeout, Retry, Circuit Breaker, Fallback) ของแอปพลิเคชันทำงานได้จริงก่อนขึ้น Production
วิกฤตการณ์ระบบล่มทั้งกระดานในเทศกาลลดราคา Black Friday จากปัญหา Cascading Failure
แพลตฟอร์มอีคอมเมิร์ซแห่งหนึ่งเตรียมรับทราฟฟิกมหาศาลในคืนวัน Black Friday ในระหว่างการทดสอบในทีม ทุกอย่างผ่าน 100% เพราะระบบยิงหา Mocks ที่ตอบกลับรวดเร็วทันใจ (Happy Path)
แต่เมื่อถึงเวลาจริง บริการภายนอกของ Third-party แนะนำสินค้า (Recommendation API) เกิดสภาวะโหลดค้างและตอบกลับช้าลงจากปกติ 100ms กลายเป็น 8 วินาที แอปพลิเคชันหลักฝั่ง Order Service ไม่มีกลไก Timeout และ Circuit Breaker ที่รัดกุม ทำให้ Thread ทั้งหมดในระบบหลักค้างรอคำตอบจาก Recommendation API จน Thread Pool เต็ม ในที่สุด Order Service ก็พังล้มตามไปด้วย สรุปคือแค่บริการแนะนำสินค้าช้าลงนิดเดียว แต่กลับทำให้ระบบสั่งซื้อของทั้งบริษัทล่มยาวนานถึง 4 ชั่วโมง เสียหายหลายสิบล้านบาท
💡 การนำ Chaos Engineering & Fault Injection เข้ามาแก้ปัญหา: ทีมงานปรับปรุงชุดการทดสอบใหม่ โดยใช้ MockServer ฉีด Fault Injection เช่น ฉีด Latency Injection 5 วินาที ใส่เฉพาะ API แนะนำสินค้า เพื่อพิสูจน์ว่า Order Service มี Timeout ตัดการเชื่อมต่อภายใน 1.5 วินาทีหรือไม่ และฉีด Connection Drop เพื่อดูว่าแอปพลิเคชันสลับไปใช้ Fallback Data จาก Cache หรือไม่ ทำให้ระบบทนทานต่อความพังทลายอย่างสมบูรณ์
แปลตรงตัว: วิศวกรรมความโกลาหลเพื่อทดสอบระบบ
อุปมาอุปไมย: เหมือน "การฉีดวัคซีนเชื้อตายเข้าไปในร่างกาย" เพื่อสร้างภูมิคุ้มกัน การจงใจสร้างสภาวะความพังทลายในสภาพแวดล้อมทดสอบ จะช่วยฝึกให้แอปพลิเคชันมีกลไกป้องกันตัวก่อนไปเจอภัยพิบัติจริงบน Production
แปลตรงตัว: การฉีดความล่าช้าในการตอบกลับ
อุปมาอุปไมย: เหมือน "การสร้างด่านชะลอความเร็วบนทางด่วน" เพื่อทดสอบว่ารถ (Requests) ฝั่งผู้เรียกจะจอดรอจนน้ำมันหมด (Connection Timeout) หรือรู้จักหาทางเลี่ยง (Fallback Route) ออกไปใช้เส้นทางอื่น
แปลตรงตัว: การตัดการเชื่อมต่อระดับ Socket ทันที
อุปมาอุปไมย: เหมือน "คู่สายโทรศัพท์ถูกกรรไกรตัดขาดฉับพลันขณะกำลังคุย" ฝั่งผู้ฟังจะไม่ได้ยินแม้แต่คำว่า "ลาก่อน" (ไม่มี HTTP Status Code ส่งกลับไป) เพื่อทดสอบว่าฝั่งผู้เรียกรับมือกับ SocketException ได้โดยไม่ crash
แปลตรงตัว: การจำกัดขอบเขตความพังเฉพาะบริการที่ระบุ
อุปมาอุปไมย: เหมือน "การปล่อยฝนตกหนักเฉพาะในหมู่บ้านที่ 3" โดยไม่ทำให้หมู่บ้านอื่นเดือดร้อน MockServer สามารถสั่งแกล้งพังเฉพาะ Endpoint เดียว (เช่น /recommendation) โดยไม่กระทบ Endpoint สำคัญอื่น (เช่น /login)
การทดสอบเฉพาะ Happy-Path Testing โดยสมมติว่าเซิร์ฟเวอร์ภายนอกจะทำงานได้สมบูรณ์แบบและรวดเร็วตลอดเวลา ทำให้มองไม่เห็น Bug ร้ายแรงในกลไก Timeout, Retry และ Fallback
เกิด Cascading Failure บน Production ซึ่งสร้างความเสียหายทางธุรกิจมหาศาล สูญเสียรายได้ และทำลายความเชื่อมั่นของลูกค้าอย่างรุนแรง
delay)
{
"httpRequest": {
"method": "GET",
"path": "/api/v4/slow-service"
},
"httpResponse": {
"statusCode": 200,
"delay": { // [Section 1] บล็อกกำหนดการหน่วงเวลาตอบกลับ
"timeUnit": "SECONDS", // ระบุหน่วยเวลา: SECONDS, MILLISECONDS, microseconds
"value": 4 // ระบุระยะเวลาหน่วง: 4 วินาที
},
"headers": { "Content-Type": ["application/json"] },
"body": "{\"status\": \"SUCCESS_BUT_SLOW\"}"
}
}
httpError -> dropConnection)
{
"httpRequest": {
"method": "GET",
"path": "/api/v4/unstable-socket"
},
"httpError": { // [Section 1] ใช้ Action แบบ httpError แทน httpResponse
"dropConnection": true // สั่งให้ MockServer ปิด TCP Socket ทิ้งทันทีที่รับ Request โดยไม่ส่ง HTTP Header/Body ใดๆ กลับไป
}
}
httpError -> responseBytes)
{
"httpRequest": {
"method": "GET",
"path": "/api/v4/corrupted-stream"
},
"httpError": {
"responseBytes": "S3VydXB0ZWQgR2FyYmFnZSBCeXRlcyE=" // [Line 6] ส่งข้อมูลขยะดิบ (Base64 Encoded) ที่ไม่ได้ตามมาตรฐาน HTTP Protocol คืนกลับไป
}
}
เปิดเว็บเบราว์เซอร์ไปที่: http://localhost:1080/mockserver/dashboard
========================================================================================
MOCKSERVER WEB DASHBOARD (PORT 1080)
========================================================================================
[ Active Expectations ] | [ Recorded Requests / Logs ] | [ Breakpoints ] | [ Reset All ]
----------------------------------------------------------------------------------------
Active Expectations Tab: - ตรวจสอบรายการ Mocks ที่มีการกำหนด delay หรือ httpError
- สังเกตสัญลักษณ์พายุความพัง (Latency/Drop Connection) บนหน้าแดชบอร์ด
Recorded Requests & Logs: - ตรวจดู Elapsed Time (เวลาที่ใช้ประมวลผล) ของแต่ละ Request
- ตรวจสอบสถานะการตัดสาย (Drop Connection) และประวัติทราฟฟิกที่ไม่ได้รับการตอบกลับ
Disable / Enable Chaos: - สามารถกดปุ่มสลับเปิด/ปิดพายุ Chaos ข้อนั้นๆ ได้ทันทีบนหน้าเว็บโดยไม่ต้องลบ JSON ทิ้ง
mockserver-module4-enterprise/
├── docker-compose.yml
├── config/
│ └── mockserver.properties
├── expectations/
│ ├── happy-path/ # Mocks การทำงานปกติ
│ │ └── user-service.json
│ └── chaos/ # Mocks แยกเฉพาะกรณีทำ Fault Injection
│ ├── latency-delay.json # หน่วงเวลาตอบกลับ
│ ├── rate-limit-429.json # จำลอง Rate Limit
│ ├── server-error-503.json # จำลองระบบล่ม 503
│ └── connection-drop.json # จำลองสายหลุด
├── scripts/
│ ├── inject_chaos.sh # สคริปต์สั่งฉีดพายุความพัง
│ └── clear_chaos.sh # สคริปต์สลับกลับมาใช้ Happy Path
└── tests/
└── ResilienceCircuitBreakerTest.java # Integration Test ตรวจสอบ Timeout & Fallback
Mocks คืนค่า 200 OK ทันทีเสมอ ไม่เคยทดสอบกรณีระบบปลายทางตอบช้า:
mockClient.when(request().withPath("/recommendation"))
.respond(response().withStatusCode(200).withBody("[]"));
จงใจใส่ Delay เพื่อดูว่า Client มี Timeout แล้วตัดไปใช้ Fallback ได้จริงไหม:
mockClient.when(request().withPath("/recommendation"))
.respond(response().withStatusCode(200)
.withDelay(TimeUnit.SECONDS, 4)
.withBody("[]"));
ก่อนเริ่มทำ ตรวจสอบว่าเปิด MockServer ไว้ที่ http://localhost:1080 เรียบร้อยแล้ว
Setup:
curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v4/slow-service" }, "httpResponse": { "statusCode": 200, "delay": { "timeUnit": "SECONDS", "value": 4 }, "headers": { "Content-Type": ["application/json"] }, "body": "{\"status\": \"SUCCESS_BUT_SLOW\"}" }}'
Test: ตั้ง Timeout 2 วินาที (หลุด Timeout): curl -i --max-time 2 http://localhost:1080/api/v4/slow-service (ได้ curl: (28) Operation timed out) | รอจนจบ 5 วินาที: curl -i --max-time 5 http://localhost:1080/api/v4/slow-service (ได้ 200 OK)
Setup:
curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v4/payment-gateway" }, "httpResponse": { "statusCode": 503, "headers": { "Content-Type": ["application/json"], "Retry-After": ["3"] }, "body": "{\"error\": \"SERVICE_OVERLOADED\"}" }}'
Test: curl -i -X POST http://localhost:1080/api/v4/payment-gateway (ได้ HTTP 503 พร้อม Header Retry-After: 3)
Setup:
curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v4/unstable-socket" }, "httpError": { "dropConnection": true }}'
Test: curl -i http://localhost:1080/api/v4/unstable-socket (ได้ curl: (52) Empty reply from server ทันที)
Setup:
curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v4/corrupted-stream" }, "httpError": { "responseBytes": "S3VydXB0ZWQgR2FyYmFnZSBCeXRlcyE=" }}'
Test: curl -i http://localhost:1080/api/v4/corrupted-stream (Client ฟ้องว่า Received corrupt data)
Setup:
curl -X PUT "http://localhost:1080/mockserver/expectation" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "POST", "path": "/api/v4/critical-tx" }, "httpResponse": { "statusCode": 500, "delay": { "timeUnit": "SECONDS", "value": 3 }, "headers": { "Content-Type": ["application/json"] }, "body": "{\"error\": \"INTERNAL_DATABASE_CRASH\"}" }}'
Test: curl -i -X POST http://localhost:1080/api/v4/critical-tx (รอนาน 3 วินาที แล้วได้ HTTP 500)
Verify Call:
curl -X PUT "http://localhost:1080/mockserver/verify" -H "Content-Type: application/json" -d '{"httpRequest": { "method": "GET", "path": "/api/v4/slow-service" }, "times": { "atLeast": 1 }}'
Expected: HTTP 202 Accepted (พบประวัติการยิงตรงตามเงื่อนไขอย่างน้อย 1 ครั้ง)
คลิกเลือกคำตอบข้อที่ถูกต้อง ระบบจะตรวจให้คะแนนและแสดงผลเฉลยพร้อมคำอธิบายทันที!
คะแนนของคุณคือ ...
เนื้อหาและแบบฝึกหัดทั้งหมดในวันนี้ ตอบและสะท้อนชื่อบท Day 4 / Module 4: Chaos Engineering & Fault Injection อย่างสมบูรณ์: การจงใจฉีด Latency Injection, HTTP 5xx Errors, Dropped Connections และการควบคุมแบบ Service-Scoped Chaos คือหัวใจสำคัญของการทดสอบเพื่อสร้างระบบที่ทนทานต่อความพังทลาย (Resilience) ช่วยให้เรามั่นใจว่าแอปพลิเคชันจะยังคงทำงานได้ราบรื่นแม้ในวันที่บริการภายนอกเกิดวิกฤตพังทลายครับ!