Module 03 — Caching, Async & Scheduling: tổng quan
Cache, chạy nền, lịch định kỳ và event đều đi qua cùng một AOP proxy, nên chia sẻ đúng một họ cạm bẫy: self-call, ranh giới thread, ranh giới transaction.
TL;DR: Module này dạy bốn cơ chế Spring dùng để đẩy việc ra khỏi luồng request chính: cache (@Cacheable), chạy nền (@Async), lịch định kỳ (@Scheduled), và Spring Events. Cả bốn, cùng @Transactional bạn đã học ở khoá trước, đi qua đúng một cơ chế nền: AOP proxy. Vì chia sẻ chung cơ chế, chúng chia sẻ chung một họ cạm bẫy — self-call bỏ qua proxy, ranh giới thread không tự mang theo ThreadLocal, ranh giới transaction không đợi việc phụ chạy xong. Module đi theo đúng thứ tự học một cơ chế rồi áp nó vào bốn bài toán khác nhau, chốt lại bằng một mini-challenge ráp cả năm mảnh vào một tính năng thật.
Vì sao module này tồn tại
TaskFlow đã có RestClient gọi API ngoài, có Actuator theo dõi sức khoẻ, có Micrometer đo latency. Nhưng ba vấn đề vẫn lặp lại mỗi tuần: endpoint báo cáo chậm vì tính lại từ đầu mỗi lần gọi dù dữ liệu bên dưới chỉ đổi vài lần một ngày, request tạo project bị chặn hai giây chờ SMTP trả lời trong khi người dùng chẳng cần biết email đã gửi xong chưa, và job gửi báo cáo đêm chạy trùng ba lần vì ứng dụng giờ chạy nhiều instance sau load balancer.
Bốn annotation của module này giải quyết đúng ba vấn đề đó, nhưng điểm đáng học không nằm ở cú pháp từng annotation. Nó nằm ở chỗ @Cacheable, @Async, @Scheduled và cơ chế publish/subscribe của Spring Events đều dựa trên đúng một nền tảng đã học ở bài AOP Proxy của spring-core: container bọc bean bằng một proxy, và proxy chỉ chèn được logic tại lời gọi đi qua nó. Hiểu đúng cơ chế đó một lần, bạn nhận ra ngay vì sao annotation lặng lẽ mất tác dụng khi gọi qua this, vì sao dữ liệu gắn với thread (MDC, SecurityContextHolder) biến mất khi việc chuyển sang thread khác, và vì sao transaction không đợi một việc phụ chạy xong trước khi commit.
flowchart TB
P["AOP Proxy - hoc o spring-core"] --> C["Bai 01: Cacheable"]
P --> A["Bai 03: Async"]
P --> S["Bai 04: Scheduled"]
P --> T["Transactional - hoc o spring-rest-data"]
C --> B02["Bai 02: chon Caffeine hay Redis"]
A --> B05["Bai 05: Spring Events"]
S --> B05
T --> B05
B02 --> M["Bai 06: Mini-challenge TaskFlow v6"]
B05 --> MSau module này bạn sẽ
- Implement
@Cacheablevà@CacheEvictvới key strategy đúng, tránh bẫy self-call của proxy. - Compare Caffeine local với Redis distributed theo trục invalidation, serialization và chi phí vận hành.
- Implement
@Asyncđúng executor và diagnose exception bị nuốt khi method trả vềvoid. - Choose fixed rate, fixed delay hay cron, và design chống chạy trùng khi chạy nhiều instance.
- Design luồng phụ bằng Spring Events và justify khi nào cần
@TransactionalEventListenerAFTER_COMMIT.
Lộ trình module
Thứ tự năm bài không ngẫu nhiên: mỗi bài dùng lại đúng cơ chế bài trước vừa dựng, chỉ đổi bài toán áp dụng.
- Cache abstraction:
@Cacheablechạy qua proxy như thế nào, và vì sao self-call làm nó lặng lẽ không hoạt động? - Caffeine hay Redis: cùng một annotation cho hai kết quả khác nhau tuỳ nơi đặt cache, vậy chọn theo trục nào khi TaskFlow chạy nhiều instance?
- Async và executor: trả
voidhayCompletableFuturequyết định exception đi đâu, và executor mặc định có đủ dùng cho mọi loại việc không? - Scheduled:
fixedRate,fixedDelayhaycronđếm mốc từ đâu, và job đêm chạy trùng ba lần khi có ba instance thì chặn bằng gì? - Spring Events:
@EventListenercó thật sự chạy bất đồng bộ như cái tên gợi ý không, và khi nào bắt buộc cần@TransactionalEventListenerAFTER_COMMIT? - Mini-challenge: TaskFlow v6: ráp cả năm mảnh vào một tính năng thật, báo cáo đêm có cache, chạy nền và phát qua event.
Yêu cầu trước khi bắt đầu
Module này giả định bạn đã nắm cơ chế proxy đứng sau annotation, không dạy lại từ đầu:
- AOP Proxy — JDK Dynamic Proxy so với CGLIB và pitfall self-call (spring-core): nền tảng của cả bốn annotation trong module này.
@Transactional& AOP proxy (spring-rest-data): ranh giới transaction quay lại nhiều lần ở bài 03 và bài 05.- Virtual threads trong Boot (module 01 course này): executor của
@Asyncvà@Scheduledcó thể chạy trên virtual thread, cùng ba cạm bẫy pinning/ThreadLocal/backpressure đã học ở đó.
Time budget
- Module: khoảng 1 giờ 33 phút tổng (1 giờ 3 phút đọc 5 bài concept, 30 phút mini-challenge).
| Bài | Chủ đề | Phút |
|---|---|---|
| 00 | Tổng quan | 8 |
| 01 | Cache abstraction | 13 |
| 02 | Caffeine hay Redis | 12 |
| 03 | Async và executor | 13 |
| 04 | Scheduled | 12 |
| 05 | Spring Events | 13 |
| 06 | Mini-challenge TaskFlow v6 | 30 |
| 07 | Tổng kết & cheat sheet | 6 |
Cách học module này hiệu quả
- Đừng học năm bài như năm annotation rời rạc. Mỗi lần gặp một pitfall mới (self-call, exception bị nuốt, chạy trùng nhiều instance), tự hỏi ngược lại: nó có họ hàng nào ở bài trước không?
- Bài 02 và bài 04 cùng dựng trên một tình huống: TaskFlow chạy ba instance sau load balancer. Nhớ lại đúng bối cảnh đó khi đọc bài 04 sẽ nhanh hơn đọc lại từ đầu.
- Đừng bỏ qua mini-challenge (bài 06). Đó là chỗ duy nhất năm mảnh kiến thức ráp lại thành một tính năng chạy được, và là bằng chứng cho cả năm learning outcome ở trên.
Bài tiếp theo: Cache abstraction
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên