
ภาพหน้าปก: อัตราการไหลของข้อมูลถูกควบคุมด้วย Backpressure
เวลาอ่านโดยประมาณ 20 นาที — บทความนี้จะพาคุณเจาะลึกแนวคิด Reactive Streams Backpressure และวิธีนำไปใช้งานอย่างเป็นระบบใน Spring WebFlux (บน Project Reactor) เพื่อป้องกัน Out-Of-Memory, ปรับสมดุล workload และยกระดับ throughput ของแอป Reactive API ที่ใช้ Netty หรือ Undertow อยู่เบื้องหลัง
สารบัญ
- Backpressure คืออะไร & ทำไมต้องสนใจ?
- สัญญา Reactive Streams 4 ข้อ
- Operators รับมือ Backpressure ยอดนิยม
- ตัวอย่างโค้ด ครบวงจร
- เทคนิค Tuning ใน Spring Boot 3.5+
- ข้อผิดพลาดที่พบบ่อย
- Checklist สำหรับ Production
1. Backpressure คืออะไร & ทำไมต้องสนใจ?
ในระบบ Reactive Publisher กับ Subscriber จะสื่อสารกันด้วยสัญญาณ request(n) ผู้บริโภค (Consumer) จะบอกผู้ผลิต (Producer) ว่าต้องการข้อมูลทีละกี่ชิ้น หากผู้ผลิตส่งเร็วกว่าที่ร้องขอ เราเรียกว่า “overflow”. อาการที่พบบ่อย:
- คิว buffer โตไม่จำกัด → RAM พุ่งจน OOM
- Thread ถูกบล็อกรอ I/O เพราะ Storm ของ onNext()
- Latency กระโดดสูงใน P99 เพราะ GC ยิงถี่

การเปิด Backpressure จึงเหมือนใส่วาล์วควบคุมแรงดัน ช่วยรักษาอัตราการไหลให้สมดุล
2. สัญญา Reactive Streams 4 ข้อ
- Responsive – ต้องพร้อมตอบสนองตลอด
- Resilient – ต้องทนต่อความผิดพลาด
- Elastic – ขยายตามภาระงาน
- Message-Driven – ใช้ non-blocking message เป็นตัวควบคุม
ข้อสุดท้ายคือหัวใจ เพราะ Publisher จะส่ง onNext ได้ก็ต่อเมื่อ Subscriber ขอ
3. Operators รับมือ Backpressure ยอดนิยม

Operator | หลักการ | ข้อดี | ข้อควรระวัง |
---|---|---|---|
limitRate(int n) | ผ่อนส่งข้อมูลทีละก้อน | ควบคุมแรงดันได้แม่น, API อ่านง่าย | เพิ่ม latency เล็กน้อยหาก n ต่ำไป |
buffer(int size) | กักข้อมูลในหน่วยความจำ | ไม่มี data loss | เสี่ยง OOM ถ้า size ไม่จำกัด |
onBackpressureDrop() | ทิ้ง element เกิน demand | รักษา latency, ใช้ RAM น้อย | ข้อมูลหาย ไม่เหมาะกับ critical data |
4. ตัวอย่างโค้ด ครบวงจร
4.1 Controller แบบ Endpoint-Streaming
// src/main/java/com/example/controller/EventController.java @RestController @RequestMapping("/v1/events") public class EventController { private final EventService eventService; public EventController(EventService eventService) { this.eventService = eventService; } @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<Event> streamEvents() { return eventService.events() .limitRate(128) // ปรับแรงดัน .onBackpressureBuffer(256) // กักไม่เกิน 256 .publishOn(Schedulers.boundedElastic()); } }
4.2 Service จำลอง Producer เร็วกว่า Consumer
@Service public class EventService { public Flux<Event> events() { return Flux.interval(Duration.ofMillis(5)) // ยิงถี่มาก .map(this::toEvent); } private Event toEvent(Long seq) { return new Event(seq, Instant.now()); } }
4.3 ทดสอบ Backpressure ใน JUnit 5
@Test void backpressure_should_not_overflow() { StepVerifier.create(eventService.events() .limitRate(10) .take(100)) .expectNextCount(100) .verifyComplete(); }
5. เทคนิค Tuning ใน Spring Boot 3.5+
- ปรับ
reactor.netty.io-worker-count
เท่าจำนวน CPU logical core - ตั้ง
spring.codec.max-in-memory-size
จำกัด buffer แปลง JSON ใหญ่ ๆ - ใช้
@Scheduler
กำหนด thread pool เฉพาะสำหรับ blocking I/O - เปิด
BlockHound
ตรวจเจอ blocking call ขณะ ทำ load test

6. ข้อผิดพลาดที่พบบ่อย
⚠ ลืม Backpressure ที่ DB Layer
เช่น ดึง Flux ใหญ่จาก R2DBC แล้ว collectList() ใน Service → กลายเป็น blocking
- ทำ parallel() แต่ไม่
.runOn()
→ ยังคงทำงานบน main event-loop - ละเลย error signal → ต้อง
onErrorResume()
ก่อน retry - เชน operator ที่ขยายข้อมูล (flatMap) โดยไม่จำกัด
concurrency
7. Checklist ก่อนขึ้น Production
- เขียน
StepVerifier
กับทุก Flux/Mono ที่ Critical - ตั้งค่า limitRate ใน ทุก จุด ที่ Producer > Consumer
- กำหนดขนาด buffer ให้เหมาะกับ JVM Heap
- ใช้ SLA (P99) ตรวจ latency เสมอใน Grafana/Prometheus
- จำลอง Load Spike จริง ด้วย k6 หรือ Vegeta
สรุป
Backpressure ไม่ใช่แค่ตัวเลือก แต่คือหัวใจของสถาปัตยกรรม Reactive ทุกระดับ ตั้งแต่ function เล็ก ๆ จนถึง microservice ทั้งระบบ หากเข้าใจจังหวะ request-response ของ Reactive Streams และนำ operators มาใช้ถูกต้อง คุณจะได้แอปที่ เร็ว เสถียร และ รองรับ โหลดสูง อย่างมั่นใจ