Chào các bạn!

Mở bài quen thuộc 🙂 Các bạn đã từng sử dụng hoặc từng nghe đến “cronjob” trên hosting chưa, hoặc là “crontab” trong linux? Nếu rồi thì các bạn sẽ rất dễ để tiếp cận bài viết này.

Cronjob trên hosting

Crontab trong linux

Nếu chưa từng nghe thì thử nhìn ảnh dưới đây.

Khá hài hước, đây là cách thức chung của nhiều bạn sinh viên, trong đó có mình. Nhưng giờ thì đỡ nhiều rồi, mình kệ khi nào tỉnh dậy thì đi chứ chả cần báo thức =)) Vâng, đây là chức năng hẹn giờ báo thức trên điện thoại. Dù bạn có dùng smartphone hay điện thoại cục gạch đi nữa thì chức năng Báo thức vẫn là thứ không thể thiếu trên điện thoại. Bấm vào chi tiết xem có gì.

Ồ, hẹn giờ theo thứ trong tuần. Cùng là tính năng hẹn giờ, nhưng ở mức độ cao cấp hơn, ví dụ như tạo email Meeting Request, bạn có thể đặt các lịch định kỳ theo ngày trong tháng, hoặc ngày trong năm, hoặc nhiều kiểu lặp lại khác.

Vậy câu hỏi là tính năng hẹn giờ hay đặt lịch đó hoạt động bằng cách nào, tại sao nó lại thực hiện yêu cầu của mình đúng theo thời điểm mình khai báo được!

Nếu tự xây dựng các bạn hoàn toàn có thể làm bằng cách làm 1 chương trình chạy vô tận, cứ mỗi giây thì lại kiểm tra thời gian hiện tại 1 phát, xem hiện tại là ngày bao nhiêu, thứ mấy, tháng mấy, năm nào,… vân vân mây mây. Sau đó đem so sánh với 1 loại các cấu hình hẹn giờ đã khai báo xem trùng khớp với cái nào thì chạy nghiệp vụ của cái đó. Nghe thì thế nhưng cũng khá mất công đấy, quản lý thread không tốt thì nó tiêu tốn tài nguyên hệ thống không ít, không có chức năng giám sát hay cảnh báo thì nó treo/chết lúc nào chả biết.

Nhưng rất may là Spring nó đã đẻ ra 1 thằng hỗ trợ tận răng vụ này. Mình xin giới thiệu cho các bạn nào chưa biết đến Spring Schedule. Spring Schedule xử lý hết việc đọc cấu hình đặt lịch, quét lịch và chạy nghiệp vụ khi đến lịch, quản lý tiến trình. Công việc của bạn bây giờ chỉ còn là khai báo lịch muốn chạy, và viết hàm main chạy nghiệp vụ đơn giản như nhập môn lập trình 😀

Dài dòng đủ rồi, giờ cụ tỷ cách thực hiện như sau

Bước 1: Đầu tiên với project sử dụng Maven thì bạn cần add dependency spring schedule vào file pom.xml. Spring Schedule nằm trong gói spring-context, nếu pom.xml của bạn chưa có thì add thêm vào

Bước 2: Tạo một class hoặc một method nghiệp vụ cần chạy ngầm

Bước 3: Tạo file cấu hình đặt lịch

Bước 4: Chạy thử chương trình và đợi kết quả.

Easy game 😀

Nhưng đây mới chỉ là cách tạo tiến trình cơ bản nhất theo đúng cách học của sinh viên, chưa có tính linh động về cấu hình và cũng chưa có tính mở rộng cao.

Để thuận tiện cho cấu hình đặt lịch, thì ta phải chuyển cái cấu hình đó ra file application.properties của project. Thêm một cái cấu hình để cho phép Enable hay Disable module tiến trình trong trường hợp cần thiết.

Để tăng tính mở rộng, giảm thiểu khối lượng duplicate code thì ta phải viết khung tiến trình theo kiểu abstract, các tiến trình nghiệp vụ chỉ cần extend abstract đó và chỉ xử lý liên quan đến nghiệp vụ thôi, không cần duplicate toàn bộ khung nữa. Đồng thời các tham số nghiệp vụ cho tiến trình (ví dụ xóa log đã lưu trữ quá 6 tháng chẳng hạn) thì nên khai báo trong Cơ sở dữ liệu để tiện cho việc sửa tiến trình mà không cần deploy lại hệ thống… vân vân mây mây.

Như vậy cần tạo một abstract class khung tiến trình cơ bản như sau

Tiếp đó tạo class nghiệp vụ như sau

Vậy là các bạn đã dựng được một module tiến trình tương đối và sử dụng tốt trong phần lớn các trường hợp, áp dụng được từ hệ thống nhỏ đến hệ thống to. Mà độ rủi ro lại thấp, quản lý được log từ log4j trên server luôn.

Phần cuối cùng, quay lại chủ đề ban đầu. Giờ muốn đặt lịch cho nghiệp vụ kia chạy theo thứ trong tuần, ngày trong tháng, theo giờ phút giây, hoặc bất kỳ loại định kỳ khác thì như nào?

Schedule Annotation của Spring hỗ trợ 3 loại đặt lịch chính

  • cron: Đặt lịch theo cấu hình chuỗi tương tự cronjob/crontab
  • fixedDelay: Cấu hình thời gian chờ giữa thời điểm hoàn thành của lần chạy trước với thời điểm bắt đầu của lần chạy sau. Nghĩa là sau khi hoàn thành lần chạy 1 thì phải chờ fixedDelay (tính theo mili giây) thì mới start lần tiếp theo
  • fixedRate: Cấu hình thời gian chờ giữa các thời điểm bắt đầu. Nghĩa là sau khi start lần chạy 1 thì phải chờ fixedRate (tính theo mili giây) thì mới start lần tiếp theo. Không quan trọng lần 1 đã chạy xong hay chưa.

Các bạn có thể xem ảnh dưới để hình dung dễ hơn về fixedDelayfixedRate

Đối với fixedDelayfixedRate thì đơn giản dễ hiểu, còn đối với cron thì hơi phức tạp chút, ví dụ như báo thức của mình trên đầu bài viết là 7h sáng các ngày từ thứ 2 đến thứ 6 thì cấu hình cron như sau

Nghĩa là 0 giờ, 0 phút, 7 giờ, các ngày trong tháng, các tháng trong năm, từ thứ MON đến FRI

Các bạn có thể xem ảnh mô tả dưới đây để dễ hình dung hơn

Cấu trúc của 1 chuỗi cron gồm 7 dấu tương ứng với 7 tham số, riêng dấu cuối cùng (Year) có thể có hoặc không. Trường hợp cần đặt lịch theo Year nhưng lại không cần theo Day of Week thì dấu * thứ 6 chuyển thành dấu ? nghĩa là không có giá trị

Một số ví dụ khác

Chạy 12h trưa hàng ngày

Chạy vào 1h sáng và 12h trưa hàng ngày

Chạy 10s 1 lần (0s, 10s, 20s, …. )

Chạy 30 phút 1 lần

Mỗi ngày 1 lần vào 6h tối, chỉ chạy trong năm 2019

Các bạn có thể xem thêm nhiều ví dụ chi tiết hơn tại đây:

https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm

Hi vọng các bạn áp dụng được vào các trường hợp thực tế, hoặc ít nhất là các bài tập lớn, thay vì việc phải dùng new Thread() hay cái gì đó để tạo ra cái mà bạn gọi là tiến trình 🙂

Bình luận