Sử dụng đối tượng lập lịch trong EzyPlatform
Back To BlogsThiết kế của Scheduler
Sẽ có hai thiết kế chính trong Scheduler:
- Thiết kế thành phần.
- Thiết kế luồng.
Thiết kế thành phần
Bên trong Scheduler cũng chỉ có hai thành phần chính là các task và các luồng.
Các task này trong suốt với các nhà phát triển, vì khi các nhà phát triển sử dụng họ chỉ truyền vào command
ở dạng Runnable
, các command này chỉ làm nhiệm vụ thực thi. Khi truyền vào các hàm của Scheduler
thì command
sẽ được bọc lại bởi một Task
để tính toán logic về thời gian thực thi và số lần thực thi, đây là một trong những ứng dụng quan trọng của proxy design pattern. Mã nguồn của lớp Task
cũng chỉ đơn giản như sau:
private static class Task { final Runnable command; final boolean runForever; final long periodMillis; final AtomicLong nexRunTime = new AtomicLong(); Task( Runnable command, boolean runForever, long initialDelay, long period, TimeUnit unit ) { this.command = command; this.runForever = runForever; this.periodMillis = unit.toMillis(period); this.nexRunTime.set(System.currentTimeMillis() + unit.toMillis(initialDelay)); } void calculateNextRunTime() { this.nexRunTime.addAndGet(periodMillis); } }
Hàm calculateNextRunTime
tương đối quan trọng, nó sẽ tính thời gian tiếp theo mà task được chạy nếu như task này là chạy mãi mãi.
Thiết kế luồng
Scheduler sẽ tổ chức thành 2 loại luồng:
- Inspector thread: Là một luồng duy nhất chạy định kỳ, nghỉ tối đa 5 milli giây. Luồng này có tranh nhiệm thanh toàn bộ các task xem đã có task nào đến thời điểm được chạy chưa, nếu đến rồi thì nó sẽ lấy một trong các worker thread ra để thực thi
command
trong task. - Các worker thread: Là các luồng để thực thi
command
được bọc trong task, các worker thread này được quản lý bởi thread pool.
Việc tổ chức 1 luồng thanh tra như thế này để tránh gây ra tình trạng race condition
trong việc kiểm tra các task đã đến thời điểm được chạy chưa.
Các hàm trong Scheduler
Có 3 hàm mà bạn sẽ hay sử dụng nhất đó là:
scheduleOneTime
: Chạy mộtcommand
duy nhất một lần.scheduleAtFixRate
: Chạy định kỳ mộtcommand
.cancelSchedule
: Dừng dạy mộtcommand
.
Hàm scheduleOneTime
Hàm này có các tham số như sau:
public void scheduleOneTime( Runnable command, long delayTime, TimeUnit unit )
command
: Là công việc mà bạn cần thực hiện một lần.delayTime
: Là thời gian trì hoãn cho đến khicommand
được thực thi.unit
: Là đơn vị thời gian cho việc trì hoãn, ví dụ milli giây, giây, phút, giờ, ngày, ...
Hàm scheduleAtFixRate
Đây là hàm mà bạn sẽ hay sử dụng nhất và nó có các tham số như sau:
public void scheduleAtFixRate( Runnable command, long initialDelay, long period, TimeUnit unit )
command
: Là công việc mà bạn cần thực hiện một lần.initialDelay
: Là thời gian trì hoãn cho đến khicommand
được thực thi lần đầu.period
: Là khoảng thời gian định kỳ màcommand
sẽ được chạy sau lần đầu tiên.unit
: Là đơn vị thời gian cho việc trì hoãn và thời gian định kỳ, ví dụ milli giây, giây, phút, giờ, ngày, ...
Hàm cancelSchedule
Hàm này sẽ bao gồm 1 tham số duy nhất là command
mà bạn muốn dừng chạy định kỳ.
public void cancelSchedule(Runnable command)
Sử dụng
Hãy nói bạn cần phải backup dữ liệu định kỳ 24 giờ một lần bạn có thể sử dụng Scheduler
với mã nguồn như sau:
scheduler.scheduleAtFixRate( ezyPlatformBackupDeletionWorker, 30, 24 * 60 * 60, TimeUnit.SECONDS );
Hay bạn cần phải thực thi một công việc định kỳ theo một mốc thời gian nào đó trong ngày bạn có thể sử dụng mã nguồn:
long initDelayTime = calculateInitDelayTime( schedulerSetting.getRunTime() ); long period = calculatePeriodTime( schedulerSetting.getPeriodInDay() ); logger.info( "start data backup scheduler, " + "initDelayTime: {} seconds " + "period: {} day", initDelayTime / 1000, period / (24 * 60 * 60 * 1000) ); Runnable command = this::runWorkers; scheduler.scheduleAtFixRate( command, initDelayTime, period, TimeUnit.MILLISECONDS ); runningSchedulerFuture.set(future);
Và đến khi cần dừng lại bạn có thể gọi hàm:
scheduler.cancelSchedule(command);
Tổng kết
Lập lịch là tính năng rất quan trọng trong EzyPlatform, vì có rất nhiều các công việc cần thực hiện định kỳ như cache dữ liệu, backup dữ liệu, xoá log, ... Để hạn chế sinh ra quá nhiều thread và tối ưu hiệu năng, chúng tôi đề xuất bạn sử dụng lớp Scheduler
này cho các công việc cần lập lịch nhé.