Phần 1: Giới thiệu tổng quan Apache Flink embedded trong Spring Boot

Ở phần này, chúng ta sẽ đi qua các khái niệm cốt lõi, bối cảnh kinh doanh/dự án, và lý do tại sao lại lựa chọn giải pháp Apache Flink embedded trong Spring Boot. Mục tiêu là để bạn có cái nhìn toàn diện về vấn đề lẫn giải pháp, đồng thời nhận thức được những ưu, nhược điểm khi triển khai theo mô hình này.
1. Bối cảnh & yêu cầu
1.1 Bối cảnh thực tế E-commerce
Giả sử bạn có một nền tảng thương mại điện tử (e-commerce) đang vận hành. Hệ thống này phát sinh rất nhiều dữ liệu theo dạng streaming từ các nguồn khác nhau. Điển hình trong hệ thống của chúng ta:
Kafka đang được dùng làm “message bus” để thu thập sự kiện liên quan đến:
Đơn hàng (Order) của khách (Topic A).
Thông tin vận chuyển (ShippingDetail) cho đơn hàng đó (Topic B).
Mục tiêu là kết hợp (join) hai nguồn dữ liệu này để có cái nhìn đầy đủ về mỗi đơn hàng. Ví dụ:
Topic A: “order_events” – phát sinh sự kiện khi khách đặt hàng, huỷ đơn, cập nhật trạng thái thanh toán, v.v.
Giả sử một record sẽ có:
order_id (mã đơn hàng)
user_id (mã khách hàng)
order_total (tổng giá trị đơn hàng)
order_status (trạng thái: “CREATED”, “PAID”, “CANCELLED”, …)
timestamp
Topic B: “shipping_events” – phát sinh khi đơn hàng được tạo phiếu gửi, chuyển cho đơn vị vận chuyển, cập nhật thông tin giao hàng, v.v.
Giả sử một record sẽ có:
shipping_id (mã giao hàng)
order_id (mã đơn hàng – liên kết với bên A)
carrier (đơn vị vận chuyển, ví dụ “DHL”, “FedEx”, …)
shipping_status (trạng thái: “PREPARED”, “IN_TRANSIT”, “DELIVERED”, …)
timestamp
Vì hai sự kiện (order tạo trước, shipping có thể đến sau) không đồng bộ về thời gian, ta cần một pipeline streaming để join dựa trên order_id. Sau đó, khi đã có đủ thông tin, ta muốn lưu kết quả cuối cùng vào MongoDB dưới dạng upsert (lần đầu insert, về sau update nếu có thay đổi).
1.2 Lựa chọn giải pháp Flink + Spring Boot
Mặc định, Flink có thể chạy theo mô hình cluster (cài đặt JobManager, TaskManagers). Nhưng để đơn giản hoá, ta quyết định “nhúng” (embed) Flink bên trong Spring Boot. Lý do:
Dễ phát triển, dễ triển khai: Chỉ cần một file JAR “spring-boot” duy nhất.
Tận dụng Dependency Injection, quản lý cấu hình (application.properties / application.yml) của Spring Boot.
Nếu muốn, ta có thể mở REST API cho việc giám sát, điều khiển job (bật/tắt, kiểm tra trạng thái, …).
2. Tại sao “chạy embedded” thay vì triển khai Flink cluster?
2.1 Ưu điểm
Đơn giản hóa quy trình phát triển & triển khai
Không phải cài đặt hay quản trị thêm cụm Flink.
Mọi thứ gói gọn trong ứng dụng Spring Boot, dễ đưa lên môi trường dev, test, hoặc staging.
Tích hợp dễ dàng với hệ sinh thái Spring
Dùng
@Bean
,@Configuration
,@Service
để injectStreamExecutionEnvironment
.Dùng chung cấu hình
spring.kafka.bootstrap-servers
,spring.data
.mongodb.uri
trongapplication.yml
.
Thuận tiện khi bạn muốn cung cấp REST API
Spring Boot hỗ trợ sẵn: Actuator, Healthcheck, metrics, …
Có thể viết thêm Controller để “bật/tắt” pipeline, theo dõi logs,...
2.2 Nhược điểm
Khó mở rộng (scale) khi khối lượng dữ liệu lớn
Embedded Flink chạy kiểu “Local MiniCluster” trong cùng JVM.
Muốn scale thường phải bật thêm nhiều instance Spring Boot, mỗi instance có một pipeline riêng → phức tạp trong quản lý state và đồng bộ.
Truy cập tài nguyên hạn chế
- Không thể dễ dàng mở rộng sang nhiều máy như mô hình Flink cluster (JobManager + nhiều TaskManager).
Quản lý state & checkpoint khó
Bạn phải tự lo cách checkpoint, savepoint.
Không có “JobManager” tập trung quản lý job, checkpoint, high availability như Flink cluster.
Tóm lại, chạy embedded phù hợp với luồng dữ liệu vừa hoặc nhỏ. Nếu bài toán e-commerce của bạn chưa cần xử lý hàng triệu sự kiện mỗi giây, mô hình này có thể đủ. Khi nào nhu cầu cao hơn (scale lớn, nhiều logic phức tạp), bạn nên chuyển sang Flink cluster.
3. Kiến trúc tổng thể
Dưới đây là sơ đồ tổng quát về cách chạy Flink embedded trong Spring Boot cho ví dụ e-commerce:
+--------------------------------+
| Spring Boot (Embedded Flink) |
| |
| +---------------------------+ |
| | StreamExecutionEnv | |
| | StreamTableEnv | |
| | ... Pipeline (Join) | |
| +---------------------------+ |
+--------------^-------------------+
|
Kafka A: "order_events" (Topic A)
|
| Consumer
|
(Join dựa trên order_id)
|
| Consumer
Kafka B: "shipping_events" (Topic B)
|
v
Ghi xuống MongoDB
Spring Boot:
Khởi chạy Flink
StreamExecutionEnvironment
.Đọc 2 topic Kafka (“order_events” và “shipping_events”).
Join trên
order_id
.Ghi kết quả (có thể là thông tin đơn hàng + vận chuyển) xuống MongoDB.
MongoDB:
Lưu dữ liệu ở chế độ upsert.
Mỗi record trong Mongo có thể “đủ” thông tin bao gồm:
- order_id, user_id, order_total, order_status, shipping_id, carrier, shipping_status, ...
4. Yêu cầu & bước triển khai cơ bản
4.1 Yêu cầu về dữ liệu
Topic A: order_events
Ví dụ một record JSON:
{ "order_id": "ORD123", "user_id": "U001", "order_total": 99.5, "order_status": "CREATED", "timestamp": 1679999999999 }
Topic B: shipping_events
Ví dụ một record JSON:
{ "shipping_id": "SHIP987", "order_id": "ORD123", "carrier": "DHL", "shipping_status": "IN_TRANSIT", "timestamp": 1680000000999 }
Để join, ta dùng trường order_id
(có thể xuất hiện sớm trong topic A, muộn hơn trong topic B hoặc ngược lại).
4.2 Bước triển khai tổng quát
Tạo project Spring Boot: thêm dependencies Flink, Kafka, MongoDB connector,…
Cấu hình:
application.yml
để khai báospring.kafka.bootstrap-servers
,spring.data
.mongodb.uri
, …
Khởi tạo Bean:
StreamExecutionEnvironment
,StreamTableEnvironment
, …Cài đặt checkpoint (nếu cần), watermark, parallelism, …
Xây dựng pipeline:
Tạo
KafkaSource
choorder_events
,shipping_events
.Chuyển JSON →
Row
.Tạo bảng Flink từ các DataStream, thiết lập “changelog stream” nếu upsert.
JOIN hai bảng (hoặc DataStream) dựa trên
order_id
, có thể dùng window/interval join tùy logic.
Tạo sink MongoDB:
Dùng Flink SQL DDL:
CREATE TABLE ... WITH ( 'connector'='mongodb', ... )
.INSERT INTO <mongo_table> SELECT ... FROM <join_result>
.
Chạy job:
Gọi
env.execute()
hoặcstmtSet.execute()
trong Spring Boot.Xác minh dữ liệu đã ghi vào MongoDB (insert hay update khi shipping_status thay đổi, v.v.).
5. Hướng dẫn đọc & mong đợi ở các phần sau
Chuỗi bài này sẽ được chia thành nhiều phần:
Phần 2: Cấu hình dự án Spring Boot & nhúng Flink
Tạo project, thêm dependencies, khai báo Bean cho Flink.
Thiết lập checkpoint, watermark,…
Phần 3: Đọc dữ liệu từ Kafka, parse, và tạo bảng Flink
Tạo
KafkaSource
cho “order_events” và “shipping_events”.Cách chuyển JSON →
Row
(hoặc Table).tableEnv.fromChangelogStream(...)
vớiChangelogMode.upsert()
(hoặc insertOnly).
Phần 4: Thực hiện Join & ghi kết quả sang MongoDB
Join dựa trên
order_id
.Tạo sink MongoDB bằng DDL, dùng upsert logic.
INSERT INTO ... SELECT ...
để ghi kết quả.
Phần 5: Vận hành & scale mô hình embedded
Cách chạy, cấu hình Docker/Kubernetes.
Hạn chế khi scale.
Cách quản lý state & checkpoint.
Phần 6: Tổng kết & mở rộng
Khi nào nên chuyển sang Flink cluster.
Tối ưu performance, monitoring, savepoint nâng cao.
Bạn đọc nên nắm rõ Phần 1 (nội dung này) để hiểu được bức tranh tổng thể. Ở Phần 2, chúng ta sẽ bắt đầu với việc cấu hình dự án và nhúng Flink vào Spring Boot như thế nào. Hy vọng ví dụ e-commerce này sẽ giúp bạn dễ hình dung hơn về cách đọc hai topic Kafka rồi join & upsert xuống MongoDB.
Tổng kết phần 1
Bối cảnh e-commerce: Có hai topic Kafka (
order_events
,shipping_events
) cần join dựa trênorder_id
.Mục tiêu: Tích hợp Flink vào Spring Boot, đọc streaming từ Kafka, xử lý/transform, rồi ghi dữ liệu sang MongoDB.
Ưu điểm: Nhanh gọn, thuận tiện dev/test, tận dụng Spring Boot.
Nhược điểm: Hạn chế về scale, khó quản lý stateful streaming so với Flink cluster.
Trong phần tiếp theo, chúng ta sẽ chuẩn bị dự án Spring Boot và cho Flink chạy dưới dạng “embedded” trong đó. Hẹn gặp lại ở Phần 2!
Subscribe to my newsletter
Read articles from Henry Collins directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
