Spring WebFlux: พัฒนา Web Application แบบ Reactive ตั้งแต่เริ่มจน Deployment

Sharing is caring!

Spring WebFlux คืออะไร?

Spring WebFlux เป็น framework สำหรับการพัฒนา web application ที่ใช้แนวคิด Reactive Programming ซึ่งออกแบบมาสำหรับระบบที่ต้องการ high throughput, low latency และรองรับการประมวลผล asynchronous แบบ non-blocking โดยใช้ Project Reactor (Mono/Flux) เป็นแกนหลัก

แตกต่างจาก Spring MVC ที่ใช้ servlet blocking I/O, WebFlux รองรับทั้ง Netty และ servlet container (เช่น Tomcat) แต่จะทำงานแบบ non-blocking ซึ่งเหมาะสำหรับ workload ที่เน้น concurrent สูง เช่น streaming, chat, IoT, และ API ที่มี response time สั้น


ข้อดีของ Spring WebFlux

  • รองรับ high concurrency ด้วย memory footprint ที่ต่ำ
  • ประมวลผลแบบ asynchronous non-blocking โดยใช้ Mono และ Flux
  • รองรับ Server-Sent Events (SSE), WebSocket ได้ดี
  • สามารถใช้กับ Netty, Undertow หรือ Tomcat ได้
  • สามารถ scale ได้ดีในระบบ cloud หรือ microservice

ข้อเสียของ Spring WebFlux

  • เรียนรู้ยากกว่า Spring MVC
  • Debugging stack trace ซับซ้อน
  • ต้องออกแบบระบบให้เข้าใจ reactive flow (backpressure, flatMap, etc.)
  • บาง library/classic library ไม่รองรับ reactive (ต้องใช้ adapter)

เริ่มต้นโปรเจกต์ Spring WebFlux

1. ใช้ Spring Initializr

  • ไปที่ https://start.spring.io
  • เลือก Spring Boot >= 3.0
  • เลือก dependency: Spring Reactive Web, Lombok

2. ตัวอย่างโครงสร้างโปรเจกต์

src/
└── main/
    ├── java/
    │   └── com.example.webflux/
    │       ├── controller/
    │       ├── model/
    │       ├── repository/
    │       ├── service/
    │       └── WebFluxApp.java
    └── resources/
        └── application.yml

ตัวอย่าง: สร้าง REST API แบบ Reactive

Model

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(collection = "products")
public class Product {
    @Id
    private String id;
    private String name;
    private Double price;
}

Repository

public interface ProductRepository extends ReactiveMongoRepository<Product, String> {
    Flux<Product> findByPriceBetween(double min, double max);
}

Controller

@RestController
@RequestMapping("/products")
public class ProductController {

    @Autowired
    private ProductRepository repo;

    @GetMapping
    public Flux<Product> getAll() {
        return repo.findAll();
    }

    @GetMapping("/{id}")
    public Mono<Product> getById(@PathVariable String id) {
        return repo.findById(id);
    }

    @PostMapping
    public Mono<Product> create(@RequestBody Product p) {
        return repo.save(p);
    }

    @DeleteMapping("/{id}")
    public Mono<Void> delete(@PathVariable String id) {
        return repo.deleteById(id);
    }
}

Reactive Type: Mono และ Flux

  • Mono<T>: ส่งค่าเดียว หรือ error หรือ empty
  • Flux<T>: ส่งหลายค่า (stream) หรือ empty/error

ตัวอย่าง:

Mono.just("Hello").subscribe(System.out::println);
Flux.range(1, 5).subscribe(System.out::println);

Server-Sent Events (SSE) ใน WebFlux

@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
    return Flux.interval(Duration.ofSeconds(1))
               .map(i -> "Message #" + i);
}

Spring WebFlux กับ Functional Endpoint

นอกจากใช้ @RestController แล้ว WebFlux ยังรองรับ “Functional Style Routing”

Handler Function

public class ProductHandler {

    public Mono<ServerResponse> hello(ServerRequest req) {
        return ServerResponse.ok().bodyValue("Hello from functional route");
    }
}

Router Config

@Configuration
public class RouterConfig {
    @Bean
    public RouterFunction<ServerResponse> route(ProductHandler handler) {
        return RouterFunctions
            .route(GET("/hello"), handler::hello);
    }
}

การทำ Unit Test ใน WebFlux

@WebFluxTest(ProductController.class)
public class ProductControllerTest {

    @Autowired
    private WebTestClient webClient;

    @Test
    void testGetAll() {
        webClient.get().uri("/products")
            .exchange()
            .expectStatus().isOk()
            .expectBodyList(Product.class);
    }
}

การ Deploy Spring WebFlux

1. สร้าง Jar

./mvnw clean package

2. Run บน Docker

FROM eclipse-temurin:17
WORKDIR /app
COPY target/webflux-demo.jar app.jar
ENTRYPOINT ["java","-jar","app.jar"]

3. สร้าง image

docker build -t webflux-demo .
docker run -p 8080:8080 webflux-demo

4. Deploy บน Kubernetes / Cloud

  • สามารถใช้ Helm, YAML หรือ Spring Cloud Kubernetes
  • เหมาะกับ workload ที่มีการ connect หลาย client พร้อมกัน

Use Cases ที่เหมาะกับ Spring WebFlux

  • Streaming, Notification, Live data feed
  • ระบบ IoT ที่มี sensor จำนวนมาก
  • ระบบที่ต้องการ scale ตาม request concurrency
  • WebSocket และ SSE

บทสรุป

Spring WebFlux คือคำตอบสำหรับการพัฒนาแอปพลิเคชันแบบ reactive ที่สามารถ scale ได้ดีในระบบ cloud-native โดยเฉพาะ use case ที่มี request concurrency สูง หรือระบบที่เน้น streaming

แม้ว่า WebFlux จะมีความซับซ้อนในการเรียนรู้ แต่เมื่อเข้าใจและใช้อย่างถูกต้องจะช่วยให้ระบบมีประสิทธิภาพสูงมาก ทั้งในเรื่องของ memory, throughput และ response time ที่ดีขึ้นอย่างชัดเจน

Leave a Reply

อีเมลของคุณจะไม่แสดงให้คนอื่นเห็น ช่องข้อมูลจำเป็นถูกทำเครื่องหมาย *