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

Henry CollinsHenry Collins
7 min read

Ở 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:

    1. Đơn hàng (Order) của khách (Topic A).

    2. 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).

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.1 Ưu điểm

  1. Đơ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.

  2. Tích hợp dễ dàng với hệ sinh thái Spring

    • Dùng @Bean, @Configuration, @Service để inject StreamExecutionEnvironment.

    • Dùng chung cấu hình spring.kafka.bootstrap-servers, spring.data.mongodb.uri trong application.yml.

  3. 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

  1. 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ộ.

  2. 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).
  3. 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

  1. 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
        }
      
  2. 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

  1. Tạo project Spring Boot: thêm dependencies Flink, Kafka, MongoDB connector,…

  2. Cấu hình:

    • application.yml để khai báo spring.kafka.bootstrap-servers, spring.data.mongodb.uri, …
  3. Khởi tạo Bean:

    • StreamExecutionEnvironment, StreamTableEnvironment, …

    • Cài đặt checkpoint (nếu cần), watermark, parallelism, …

  4. Xây dựng pipeline:

    • Tạo KafkaSource cho order_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.

  5. Tạo sink MongoDB:

    • Dùng Flink SQL DDL: CREATE TABLE ... WITH ( 'connector'='mongodb', ... ).

    • INSERT INTO <mongo_table> SELECT ... FROM <join_result>.

  6. Chạy job:

    • Gọi env.execute() hoặc stmtSet.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:

  1. 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,…

  2. 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ới ChangelogMode.upsert() (hoặc insertOnly).

  3. 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ả.

  4. 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.

  5. 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ự ánnhú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ên order_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!

0
Subscribe to my newsletter

Read articles from Henry Collins directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Henry Collins
Henry Collins