Hướng dẫn tạo lịch (Task, Scheduler) với @Schedule trong Spring.
(Xem thêm: Code ví dụ Spring Boot tạo lịch với annotation @Scheduled)
Trong bài này mình sẽ giới thiệu chức năng tạo lịch (Schedule) với Spring.
Đôi khi viết chương trình chúng ta sẽ gặp những tình hướng thực hiện các chức năng chạy theo 1 lịch cố định: ví dụ cứ 5 phút gửi request 1 lần, cứ vào 23h đêm hàng ngày thì thực hiện chạy chức năng backup data, cứ 7h sáng chủ nhật hàng tuần thì tính toán và gửi báo cáo…
Chúng ta có nhiều giải pháp như:
- Làm bằng tay (cách này không ổn, nhỡ quên thì chết toi, và không thể thực hiện khi thời gian giữa các lần quá ngắn)
- Tạo Thread chạy ko ngừng nghỉ, và kiểm tra thời gian nếu khớp thì chạy method.
- Sử dụng TimerTask của Java, các thư viện như Quartz…
Cách thứ 3 là ổn nhất, và trong spring nó tích hợp sẵn cho chúng ta cách đó. Cùng tìm hiểu xem cách cấu hình như nào nhé.
Cấu hình Scheduling Tasks trong Spring
Bật chức năng schedule trong Spring:
Với trường hợp cấu hình bằng annotation hay Spring Boot ta sử dụng annotation @EnableScheduling
@Configuration @EnableScheduling public class SpringConfig { ... }
@SpringBootApplication @EnableScheduling public class Application { ... }
Với trường hợp cấu hình bằng xml ta sử dụng thẻ sau:
<task:annotation-driven>
Tạo task/job chạy theo lịch với annotation @Schedule
Tạo schedule task với fixedDelay
@Scheduled(fixedDelay = 1000) public void scheduleFixedDelayTask() throws InterruptedException { System.out.println("Task1 - " + new Date()); }
- Cứ sau khoảng thời gian fixedDelay thì nó lại chạy một lần, ví dụ ở trên thì cứ sau 1000ms (1 giây) thì nó lại chạy method
scheduleFixedDelayTask
một lần. - Với
fixedDelay
thì chỉ khi nào task trước đó thực hiện xong thì nó mới chạy tiếp task đó lại lần nữa. Ví dụ sau 1 giây mà methodscheduleFixedDelayTask
chưa chạy xong thì nó sẽ chờ cho tới khi nào xong mới chạy lại lần tiếp theo
Tạo schedule task với fixedRate
@Scheduled(fixedRate = 1000) public void scheduleFixedRateTask() throws InterruptedException { System.out.println("Task2 - " + new Date()); }
fixedRate
thì giống vớifixedDelay
, tuy nhiên cứ sau khoảng thời gianfixedRate
thì nó chạy tiếp 1 lần nữa mà không cần quan tâm lần chạy trước đã hoàn thành chưa.- Ví dụ sau 1s mà method
scheduleFixedRateTask
chưa thực hiện xong thì nó vẫn chạy lần tiếp theo.
Tạo schedule với cron
Với cron ta sẽ sử dụng cron expression để định nghĩa lịch chạy.
(Xem lại: Cron expression là gì? Hướng dẫn cú pháp cron expression)
Bằng cron ta có thể định nghĩa thời gian chạy theo giờ, phút, giây, ngày tháng năm, trong khoảng thời gian nào… do đó việc đặt lịch linh hoạt hơn so với fixedDelay
và fixedRate
rất nhiều
Ví dụ: từ giây thứ 5 đến giây thứ 10 trong khoảng thời gian 12h-14h các ngày từ thứ 2 đến thứ 6, cứ 1 giây lặp lại một lần
@Scheduled(cron = "5-10/1 * 12-14 * * MON-FRI") public void scheduleTaskUsingCronExpression() throws InterruptedException { System.out.println("Task3 - " + new Date()); }
Ngoài việc sử dụng annotation @Scheduled bạn cũng có thể cấu hình trong file xml như sau:
<beans> <bean name="beanA" class="your_class"/> <bean name="beanB" class="your_class"/> <bean name="beanC" class="your_class"/> </beans> <!-- Configure parameters --> <task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="scheduleFixedDelayTask" fixed-delay="1000" initial-delay="1000" /> <task:scheduled ref="beanB" method="scheduleFixedRateTask" fixed-rate="1000" /> <task:scheduled ref="beanC" method="scheduleTaskUsingCronExpression" cron="*/5 * * * * MON-FRI" /> </task:scheduled-tasks>
TaskScheduler, thread pool cho schedule task
Mặc định thread pool cho schedule task có giá trị là 1. Tức là hệ thống chỉ tạo ra duy nhất 1 thread để chạy các schedule task. Do đó bạn sẽ gặp trường hợp đến thời gian chỉ định mà task vẫn không được thực hiện vì có 1 task trước đó chưa hoàn thành kể cả với fixedRate
,fixedDelay
hay cron
Ví dụ method scheduleFixedRateTask
ở trên đang để là 1s chạy 1 lần, nhưng method đó mất tới 5s mới hoàn thành thì phải 5s sau method đó mới được thực hiện một lần nữa.
Để giải quyết vấn đề này ta tăng pool size lên để mỗi task chạy với một thread độc lập:
Tăng pool size cho TaskScheduler bằng cách tạo bean:
@Bean public TaskScheduler taskScheduler() { final ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setPoolSize(10); return scheduler; }
Hoặc cấu hình trong file .xml
<task:scheduler id="myScheduler" pool-size="10" />
Okay, Done!
Trong bài tiếp theo mình sẽ thực hiện code ví dụ Spring Boot đặt lịch với @Scheduled
References: