Penjadwal tugas untuk Java yang terinspirasi oleh kebutuhan akan java.util.concurrent.ScheduledExecutorService
yang lebih sederhana daripada Quartz.
Oleh karena itu, juga diapresiasi oleh pengguna (cbarbosa2, rafaelhofmann, BukhariH):
Libmu keren! Saya sangat senang saya menyingkirkan Quartz dan menggantinya dengan milik Anda yang lebih mudah ditangani!
cbarbosa2
Lihat juga mengapa tidak Kuarsa?
< dependency >
< groupId >com.github.kagkarlsson</ groupId >
< artifactId >db-scheduler</ artifactId >
< version >15.0.0</ version >
</ dependency >
Buat tabel scheduled_tasks
di skema database Anda. Lihat definisi tabel untuk postgresql, Oracle, mssql atau mysql.
Buat instance dan mulai penjadwal, yang kemudian akan memulai tugas berulang yang ditentukan.
RecurringTask < Void > hourlyTask = Tasks . recurring ( "my-hourly-task" , FixedDelay . ofHours ( 1 ))
. execute (( inst , ctx ) -> {
System . out . println ( "Executed!" );
});
final Scheduler scheduler = Scheduler
. create ( dataSource )
. startTasks ( hourlyTask )
. threads ( 5 )
. build ();
// hourlyTask is automatically scheduled on startup if not already started (i.e. exists in the db)
scheduler . start ();
Untuk contoh lainnya, lanjutkan membaca. Untuk detail tentang cara kerja bagian dalam, lihat Cara kerjanya. Jika Anda memiliki aplikasi Spring Boot, lihat Penggunaan Spring Boot.
Daftar organisasi yang diketahui menjalankan db-scheduler dalam produksi:
Perusahaan | Keterangan |
---|---|
Digipost | Penyedia kotak surat digital di Norwegia |
Grup Vy | Salah satu grup transportasi terbesar di negara-negara Nordik. |
Bijak | Cara murah dan cepat untuk mengirim uang ke luar negeri. |
Pendidikan Profesional Becker | |
Monitoria | Layanan pemantauan situs web. |
Pemuat | Memuat pengujian untuk aplikasi web. |
Staten vegvesen | Administrasi Jalan Umum Norwegia |
Tahun Cahaya | Cara sederhana dan mudah didekati untuk menginvestasikan uang Anda secara global. |
NAV | Administrasi Perburuhan dan Kesejahteraan Norwegia |
Lingkaran Modern | Sesuaikan kebutuhan perekrutan perusahaan Anda dengan menggunakan ModernLoop untuk meningkatkan efisiensi dalam penjadwalan wawancara, komunikasi, dan koordinasi. |
Diffia | Perusahaan eHealth Norwegia |
Angsa | Swan membantu pengembang untuk menanamkan layanan perbankan dengan mudah ke dalam produk mereka. |
TOMRA | TOMRA adalah perusahaan multinasional Norwegia yang merancang dan memproduksi mesin penjual otomatis terbalik untuk didaur ulang. |
Jangan ragu untuk membuka PR untuk menambahkan organisasi Anda ke daftar.
Lihat juga contoh yang dapat dijalankan.
Tentukan tugas berulang dan jadwalkan eksekusi pertama tugas saat permulaan menggunakan metode pembuat startTasks
. Setelah selesai, tugas akan dijadwalkan ulang sesuai dengan jadwal yang ditentukan (lihat jenis jadwal yang telah ditentukan sebelumnya).
RecurringTask < Void > hourlyTask = Tasks . recurring ( "my-hourly-task" , FixedDelay . ofHours ( 1 ))
. execute (( inst , ctx ) -> {
System . out . println ( "Executed!" );
});
final Scheduler scheduler = Scheduler
. create ( dataSource )
. startTasks ( hourlyTask )
. registerShutdownHook ()
. build ();
// hourlyTask is automatically scheduled on startup if not already started (i.e. exists in the db)
scheduler . start ();
Untuk tugas berulang dengan beberapa instans dan jadwal, lihat contoh RecurringTaskWithPersistentScheduleMain.java.
Sebuah instance dari tugas satu kali mempunyai waktu eksekusi tunggal pada suatu waktu di masa depan (yaitu tidak berulang). Id instans harus unik dalam tugas ini, dan dapat digunakan untuk mengkodekan beberapa metadata (misalnya id). Untuk keadaan yang lebih kompleks, objek java serialisasi khusus didukung (seperti yang digunakan dalam contoh).
Tentukan tugas satu kali dan mulai penjadwal:
TaskDescriptor < MyTaskData > MY_TASK =
TaskDescriptor . of ( "my-onetime-task" , MyTaskData . class );
OneTimeTask < MyTaskData > myTaskImplementation =
Tasks . oneTime ( MY_TASK )
. execute (( inst , ctx ) -> {
System . out . println ( "Executed! Custom data, Id: " + inst . getData (). id );
});
final Scheduler scheduler = Scheduler
. create ( dataSource , myTaskImplementation )
. registerShutdownHook ()
. build ();
scheduler . start ();
... dan kemudian pada titik tertentu (saat runtime), eksekusi dijadwalkan menggunakan SchedulerClient
:
// Schedule the task for execution a certain time in the future and optionally provide custom data for the execution
scheduler . schedule (
MY_TASK
. instanceWithId ( "1045" )
. data ( new MyTaskData ( 1001L ))
. scheduledTo ( Instant . now (). plusSeconds ( 5 )));
Contoh | Keterangan |
---|---|
AktifkanImmediateExecutionMain.java | Saat menjadwalkan eksekusi untuk dijalankan now() atau lebih awal, Scheduler lokal akan diberi petunjuk tentang hal ini, dan "bangun" untuk memeriksa eksekusi baru lebih awal dari biasanya (seperti yang dikonfigurasi oleh pollingInterval . |
MaxRetriesMain.java | Cara menetapkan batas jumlah percobaan ulang yang dapat dilakukan suatu eksekusi. |
EksponensialBackoffMain.java | Cara menggunakan kemunduran eksponensial sebagai strategi percobaan ulang alih-alih penundaan tetap seperti default. |
ExponentialBackoffWithMaxRetriesMain.java | Cara menggunakan backoff eksponensial sebagai strategi percobaan ulang dan batasan tegas pada jumlah percobaan ulang maksimum. |
TrackingProgressRecurringTaskMain.java | Pekerjaan berulang mungkin menyimpan task_data sebagai cara untuk mempertahankan status di seluruh eksekusi. Contoh ini menunjukkan caranya. |
PemijahanOtherTasksMain.java | Mendemonstrasikan contoh penjadwalan tugas lain dengan menggunakan executionContext.getSchedulerClient() . |
PenjadwalClientMain.java | Mendemonstrasikan beberapa kemampuan SchedulerClient . Menjadwalkan, mengambil eksekusi terjadwal, dll. |
Tugas BerulangDenganPersistentScheduleMain.java | Pekerjaan berulang multi-instans tempat Schedule disimpan sebagai bagian dari task_data . Misalnya cocok untuk aplikasi multi-penyewa di mana setiap penyewa harus memiliki tugas berulang. |
StatefulRecurringTaskWithPersistentScheduleMain.java | |
JsonSerializerMain.java | Mengganti serialisasi task_data dari serialisasi Java (default) ke JSON. |
JobChainingUsingTaskDataMain.java | Rangkaian pekerjaan, yaitu "ketika instance ini selesai dijalankan, jadwalkan tugas lain. |
JobChainingUsingSeparateTasksMain.java | Rangkaian pekerjaan, seperti di atas. |
InterceptorMain.java | Menggunakan ExecutionInterceptor untuk memasukkan logika sebelum dan sesudah eksekusi untuk semua ExecutionHandler . |
Contoh | Keterangan |
---|---|
Contoh Dasar | Tugas dasar satu kali dan tugas berulang |
Pekerjaan yang Dipentaskan Secara Transaksional | Contoh pementasan pekerjaan secara transaksional, yaitu memastikan pekerjaan latar belakang berjalan jika transaksi dilakukan (bersama dengan modifikasi db lainnya). |
Pekerjaan Berlari Panjang | Pekerjaan yang berjalan lama harus bertahan saat aplikasi dimulai ulang dan menghindari memulai ulang dari awal. Contoh ini menunjukkan bagaimana mempertahankan kemajuan saat shutdown dan juga teknik untuk membatasi pekerjaan agar berjalan setiap malam. |
Pelacakan Status Berulang | Tugas berulang dengan status yang dapat diubah setelah setiap kali dijalankan. |
ParallelJobSpawner | Mendemonstrasikan cara menggunakan pekerjaan berulang untuk menghasilkan pekerjaan satu kali, misalnya untuk paralelisasi. |
Rantai Pekerjaan | Pekerjaan satu kali dengan banyak langkah . Langkah selanjutnya dijadwalkan setelah langkah sebelumnya selesai. |
MultiInstanceBerulang | Menunjukkan cara mencapai beberapa pekerjaan berulang dengan jenis yang sama, namun kemungkinan jadwal dan datanya berbeda. |
Penjadwal dibuat menggunakan pembuat Scheduler.create(...)
. Pembuatnya memiliki default yang masuk akal, tetapi opsi berikut dapat dikonfigurasi.
.threads(int)
Jumlah utas. Bawaan 10
.
.pollingInterval(Duration)
Seberapa sering penjadwal memeriksa database untuk eksekusi yang jatuh tempo. Standarnya 10s
.
.alwaysPersistTimestampInUTC()
Penjadwal mengasumsikan bahwa kolom untuk stempel waktu yang bertahan tetap ada Instant
s, bukan LocalDateTime
s, yaitu entah bagaimana mengikat stempel waktu ke suatu zona. Namun, beberapa database memiliki dukungan terbatas untuk tipe tersebut (yang tidak memiliki informasi zona) atau kebiasaan lainnya, sehingga "selalu menyimpan dalam UTC" merupakan alternatif yang lebih baik. Untuk kasus seperti itu, gunakan pengaturan ini untuk selalu menyimpan Instan dalam UTC. Skema PostgreSQL dan Oracle diuji untuk menjaga informasi zona. MySQL dan MariaDB -schemas tidak dan harus menggunakan pengaturan ini. NB: Untuk kompatibilitas mundur, perilaku default untuk database "tidak diketahui" adalah mengasumsikan database mempertahankan zona waktu. Untuk database yang "dikenal", lihat kelas AutodetectJdbcCustomization
.
.enableImmediateExecution()
Jika ini diaktifkan, penjadwal akan mencoba memberi petunjuk kepada Scheduler
lokal bahwa ada eksekusi yang akan dieksekusi setelah dijadwalkan untuk dijalankan now()
, atau waktu yang lalu. NB: Jika panggilan ke schedule(..)
/ reschedule(..)
terjadi dari dalam suatu transaksi, penjadwal mungkin mencoba menjalankannya sebelum pembaruan terlihat (transaksi belum dilakukan). Namun hal ini masih dipertahankan, sehingga meskipun gagal, hal ini akan dijalankan sebelum polling-interval
berikutnya. Anda juga dapat secara terprogram memicu pemeriksaan awal untuk eksekusi yang jatuh tempo menggunakan metode Penjadwal scheduler.triggerCheckForDueExecutions()
). Bawaan false
.
.registerShutdownHook()
Mendaftarkan kait pematian yang akan memanggil Scheduler.stop()
saat pematian. Berhenti harus selalu dipanggil untuk penghentian yang baik dan untuk menghindari eksekusi mati.
.shutdownMaxWait(Duration)
Berapa lama penjadwal akan menunggu sebelum mengganggu thread layanan pelaksana. Jika Anda menemukan diri Anda menggunakan ini, pertimbangkan apakah mungkin untuk secara teratur memeriksa executionContext.getSchedulerState().isShuttingDown()
di ExecutionHandler dan membatalkan tugas yang sudah berjalan lama. Standarnya 30min
.
.enablePriority()
Dimungkinkan untuk menentukan prioritas eksekusi yang menentukan urutan pengambilan eksekusi dari database. Eksekusi dengan nilai prioritas lebih tinggi akan dijalankan sebelum eksekusi dengan nilai lebih rendah (secara teknis, pengurutan akan order by priority desc, execution_time asc
). Pertimbangkan untuk menggunakan prioritas dalam rentang 0-32000 karena bidang tersebut didefinisikan sebagai SMALLINT
. Jika Anda memerlukan nilai yang lebih besar, ubah skemanya. Untuk saat ini, fitur ini bersifat opt-in , dan priority
kolom hanya diperlukan oleh pengguna yang memilih untuk mengaktifkan prioritas melalui pengaturan konfigurasi ini.
Tetapkan prioritas per instance menggunakan TaskInstance.Builder
:
scheduler . schedule (
MY_TASK
. instance ( "1" )
. priority ( 100 )
. scheduledTo ( Instant . now ()));
Catatan:
(execution_time asc, priority desc)
(mengganti yang execution_time asc
).null
untuk prioritas dapat diinterpretasikan berbeda tergantung pada database (rendah atau tinggi). Jika Anda menjalankan >1000 eksekusi/dtk, Anda mungkin ingin menggunakan strategi polling lock-and-fetch
untuk overhead yang lebih rendah dan throughput yang lebih tinggi (baca selengkapnya). Jika tidak, fetch-and-lock-on-execute
default akan baik-baik saja.
.pollUsingFetchAndLockOnExecute(double, double)
Gunakan strategi polling default fetch-and-lock-on-execute
.
Jika pengambilan terakhir dari database adalah batch penuh ( executionsPerBatchFractionOfThreads
), pengambilan baru akan dipicu ketika jumlah eksekusi yang tersisa kurang dari atau sama dengan lowerLimitFractionOfThreads * nr-of-threads
. Eksekusi yang diambil tidak dikunci/dipilih, sehingga penjadwal akan bersaing dengan instance lain untuk mendapatkan kunci ketika dieksekusi. Didukung oleh semua database.
Default: 0,5, 3.0
.pollUsingLockAndFetch(double, double)
Gunakan strategi polling lock-and-fetch
yang menggunakan select for update .. skip locked
untuk mengurangi overhead.
Jika pengambilan terakhir dari database adalah kumpulan penuh, pengambilan baru akan dipicu ketika jumlah eksekusi yang tersisa kurang dari atau sama dengan lowerLimitFractionOfThreads * nr-of-threads
. Jumlah eksekusi yang diambil setiap kali sama dengan (upperLimitFractionOfThreads * nr-of-threads) - nr-executions-left
. Eksekusi yang diambil sudah dikunci/dipilih untuk instance penjadwal ini sehingga menyimpan satu pernyataan UPDATE
.
Untuk penggunaan normal, atur ke misalnya 0.5, 1.0
.
Untuk throughput tinggi (yaitu membuat thread sibuk), atur ke misalnya 1.0, 4.0
. Saat ini detak jantung tidak diperbarui untuk eksekusi yang dipilih dalam antrean (berlaku jika upperLimitFractionOfThreads > 1.0
). Jika mereka tetap di sana selama lebih dari 4 * heartbeat-interval
(default 20m
), tidak memulai eksekusi, mereka akan terdeteksi mati dan kemungkinan besar akan dibuka kembali (ditentukan oleh DeadExecutionHandler
). Saat ini didukung oleh postgres . sql-server juga mendukung hal ini, tetapi pengujian menunjukkan hal ini rentan terhadap kebuntuan dan karenanya tidak disarankan sampai dipahami/diselesaikan.
.heartbeatInterval(Duration)
Seberapa sering memperbarui stempel waktu detak jantung untuk menjalankan eksekusi. Standarnya 5m
.
.missedHeartbeatsLimit(int)
Berapa detak jantung yang mungkin terlewat sebelum eksekusi dianggap mati. Bawaan 6
.
.addExecutionInterceptor(ExecutionInterceptor)
Menambahkan ExecutionInterceptor
yang mungkin memasukkan logika di sekitar eksekusi. Untuk Spring Boot, cukup daftarkan Bean bertipe ExecutionInterceptor
.
.addSchedulerListener(SchedulerListener)
Menambahkan SchedulerListener
yang akan menerima kejadian terkait Penjadwal dan Eksekusi. Untuk Spring Boot, cukup daftarkan Bean bertipe SchedulerListener
.
.schedulerName(SchedulerName)
Nama contoh penjadwal ini. Nama tersebut disimpan dalam database ketika eksekusi dipilih oleh penjadwal. <hostname>
bawaan.
.tableName(String)
Nama tabel yang digunakan untuk melacak pelaksanaan tugas. Ubah nama dalam definisi tabel sesuai saat membuat tabel. scheduled_tasks
bawaan.
.serializer(Serializer)
Implementasi serializer untuk digunakan saat membuat serial data tugas. Defaultnya menggunakan serialisasi Java standar, tetapi db-scheduler juga menggabungkan GsonSerializer
dan JacksonSerializer
. Lihat contoh untuk KotlinSerializer. Lihat juga dokumentasi tambahan di bawah Serializers.
.executorService(ExecutorService)
Jika ditentukan, gunakan layanan eksekutor yang dikelola secara eksternal ini untuk menjalankan eksekusi. Idealnya jumlah thread yang akan digunakan tetap disediakan (untuk optimasi polling penjadwal). Defaultnya null
.
.deleteUnresolvedAfter(Duration)
Waktu setelah eksekusi dengan tugas yang tidak diketahui dihapus secara otomatis. Ini biasanya merupakan tugas lama yang berulang dan tidak digunakan lagi. Ini bukan nol untuk mencegah penghapusan tugas yang tidak disengaja karena kesalahan konfigurasi (tugas yang diketahui hilang) dan masalah selama peningkatan berkelanjutan. Bawaan 14d
.
.jdbcCustomization(JdbcCustomization)
db-scheduler mencoba mendeteksi secara otomatis database yang digunakan untuk melihat apakah ada interaksi jdbc yang perlu disesuaikan. Metode ini adalah jalan keluar untuk memungkinkan pengaturan JdbcCustomizations
secara eksplisit. Deteksi otomatis default.
.commitWhenAutocommitDisabled(boolean)
Secara default, tidak ada penerapan yang dikeluarkan pada DataSource Connections. Jika komitmen otomatis dinonaktifkan, diasumsikan bahwa transaksi ditangani oleh manajer transaksi eksternal. Setel properti ini ke true
untuk mengesampingkan perilaku ini dan buat Penjadwal selalu mengeluarkan komitmen. Bawaan false
.
.failureLogging(Level, boolean)
Mengonfigurasi cara mencatat kegagalan tugas, yaitu Throwable
yang dilempar dari pengendali eksekusi tugas. Gunakan log level OFF
untuk menonaktifkan logging semacam ini sepenuhnya. WARN, true
.
Tugas dibuat menggunakan salah satu kelas pembangun di Tasks
. Pembuatnya memiliki standar yang masuk akal, tetapi opsi berikut dapat dikesampingkan.
Pilihan | Bawaan | Keterangan |
---|---|---|
.onFailure(FailureHandler) | lihat deskripsi | Apa yang harus dilakukan ketika ExecutionHandler memunculkan pengecualian. Secara default, Tugas berulang akan dijadwal ulang sesuai dengan Schedule Tugas satu kali akan dicoba lagi dalam 5 menit. |
.onDeadExecution(DeadExecutionHandler) | ReviveDeadExecution | Apa yang harus dilakukan ketika eksekusi mati terdeteksi, yaitu eksekusi dengan stempel waktu detak jantung basi. Secara default, eksekusi mati dijadwal ulang ke now() . |
.initialData(T initialData) | null | Data yang akan digunakan saat tugas berulang dijadwalkan untuk pertama kalinya. |
Perpustakaan berisi sejumlah implementasi Jadwal untuk tugas berulang. Lihat Schedules
kelas.
Jadwal | Keterangan |
---|---|
.daily(LocalTime ...) | Berjalan setiap hari pada waktu tertentu. Secara opsional, zona waktu dapat ditentukan. |
.fixedDelay(Duration) | Waktu eksekusi berikutnya adalah Duration setelah eksekusi terakhir selesai. Catatan: Schedule ini menjadwalkan eksekusi awal ke Instant.now() saat digunakan di startTasks(...) |
.cron(String) | Ekspresi cron gaya pegas (v5.3+). Pola - diartikan sebagai jadwal yang dinonaktifkan. |
Opsi lain untuk mengonfigurasi jadwal adalah membaca pola string dengan Schedules.parse(String)
.
Pola yang tersedia saat ini adalah:
Pola | Keterangan |
---|---|
FIXED_DELAY|Ns | Sama seperti .fixedDelay(Duration) dengan durasi diatur ke N detik. |
DAILY|12:30,15:30...(|time_zone) | Sama seperti .daily(LocalTime) dengan zona waktu opsional (misalnya Eropa/Roma, UTC) |
- | Jadwal dinonaktifkan |
Detail selengkapnya tentang format zona waktu dapat ditemukan di sini.
Schedule
dapat ditandai sebagai dinonaktifkan. Penjadwal tidak akan menjadwalkan eksekusi awal untuk tugas dengan jadwal yang dinonaktifkan, dan akan menghapus semua eksekusi yang ada untuk tugas tersebut.
Contoh tugas mungkin memiliki beberapa data terkait di bidang task_data
. Penjadwal menggunakan Serializer
untuk membaca dan menulis data ini ke database. Secara default, serialisasi Java standar digunakan, tetapi sejumlah opsi disediakan:
GsonSerializer
JacksonSerializer
Untuk serialisasi Java disarankan untuk menentukan serialVersionUID
agar dapat mengembangkan kelas yang mewakili data. Jika tidak ditentukan, dan kelas berubah, deserialisasi kemungkinan akan gagal dengan InvalidClassException
. Jika ini terjadi, temukan dan atur serialVersionUID
yang dibuat secara otomatis saat ini secara eksplisit. Kemudian dimungkinkan untuk melakukan perubahan yang tidak dapat dihentikan pada kelas.
Jika Anda perlu bermigrasi dari serialisasi Java ke GsonSerializer
, konfigurasikan penjadwal untuk menggunakan SerializerWithFallbackDeserializers
:
. serializer ( new SerializerWithFallbackDeserializers ( new GsonSerializer (), new JavaSerializer ()))
Untuk aplikasi Spring Boot, ada starter db-scheduler-spring-boot-starter
yang membuat pengkabelan penjadwal menjadi sangat sederhana. (Lihat contoh proyek lengkap).
DataSource
yang berfungsi dengan skema diinisialisasi. (Dalam contoh HSQLDB digunakan dan skema diterapkan secara otomatis.)< dependency >
< groupId >com.github.kagkarlsson</ groupId >
< artifactId >db-scheduler-spring-boot-starter</ artifactId >
< version >15.0.0</ version >
</ dependency >
Task
Anda sebagai kacang Spring. Jika berulang, maka secara otomatis akan diambil dan dimulai.Scheduler
ke dalam informasi kesehatan aktuator, Anda perlu mengaktifkan indikator kesehatan db-scheduler
. Informasi Kesehatan Musim Semi. Konfigurasi terutama dilakukan melalui application.properties
. Konfigurasi nama penjadwal, serializer, dan layanan pelaksana dilakukan dengan menambahkan kacang bertipe DbSchedulerCustomizer
ke konteks Spring Anda.
# application.properties example showing default values
db-scheduler.enabled=true
db-scheduler.heartbeat-interval=5m
db-scheduler.polling-interval=10s
db-scheduler.polling-limit=
db-scheduler.table-name=scheduled_tasks
db-scheduler.immediate-execution-enabled=false
db-scheduler.scheduler-name=
db-scheduler.threads=10
db-scheduler.priority-enabled=false
# Ignored if a custom DbSchedulerStarter bean is defined
db-scheduler.delay-startup-until-context-ready=false
db-scheduler.polling-strategy=fetch
db-scheduler.polling-strategy-lower-limit-fraction-of-threads=0.5
db-scheduler.polling-strategy-upper-limit-fraction-of-threads=3.0
db-scheduler.shutdown-max-wait=30m
Dimungkinkan untuk menggunakan Scheduler
untuk berinteraksi dengan eksekusi masa depan yang bertahan. Untuk situasi di mana instance Scheduler
lengkap tidak diperlukan, SchedulerClient yang lebih sederhana dapat dibuat menggunakan pembuatnya:
SchedulerClient . Builder . create ( dataSource , taskDefinitions ). build ()
Ini akan memungkinkan operasi seperti:
Tabel database tunggal digunakan untuk melacak eksekusi tugas di masa depan. Ketika eksekusi tugas jatuh tempo, db-scheduler mengambilnya dan mengeksekusinya. Ketika eksekusi selesai, Task
dikonsultasikan untuk melihat apa yang harus dilakukan. Misalnya, RecurringTask
biasanya dijadwal ulang di masa mendatang berdasarkan Schedule
nya.
Penjadwal menggunakan penguncian optimis atau pilih untuk pembaruan (bergantung pada strategi polling) untuk menjamin bahwa satu dan hanya satu instance penjadwal yang dapat memilih dan menjalankan eksekusi tugas.
Istilah tugas berulang digunakan untuk tugas-tugas yang harus dijalankan secara teratur, menurut jadwal tertentu.
Ketika pelaksanaan tugas berulang telah selesai, Schedule
dikonsultasikan untuk menentukan waktu pelaksanaan selanjutnya, dan pelaksanaan tugas di masa depan dibuat untuk waktu tersebut (yaitu dijadwal ulang ). Waktu yang dipilih adalah waktu terdekat sesuai Schedule
, namun tetap di masa yang akan datang.
Ada dua jenis tugas berulang, tugas berulang statis reguler, yang Schedule
ditentukan secara statis dalam kode, dan tugas berulang dinamis , yang Schedule
ditentukan saat runtime dan disimpan dalam database (masih hanya memerlukan satu tabel) .
Tugas berulang statis adalah yang paling umum dan cocok untuk pekerjaan latar belakang reguler karena penjadwal secara otomatis menjadwalkan tugas jika tidak ada dan juga memperbarui waktu eksekusi berikutnya jika Schedule
diperbarui.
Untuk membuat eksekusi awal untuk tugas berulang statis, penjadwal memiliki metode startTasks(...)
yang mengambil daftar tugas yang harus "dimulai" jika belum ada eksekusinya. Waktu pelaksanaan awal ditentukan oleh Schedule
. Jika tugas sudah memiliki eksekusi di masa depan (yaitu telah dimulai setidaknya sekali sebelumnya), namun Schedule
yang diperbarui sekarang menunjukkan waktu eksekusi lain, eksekusi yang ada akan dijadwal ulang ke waktu eksekusi baru (dengan pengecualian non-deterministik jadwal seperti FixedDelay
di mana waktu eksekusi baru jauh di masa depan).
Buat menggunakan Tasks.recurring(..)
.
Tugas berulang dinamis adalah tambahan selanjutnya pada db-scheduler dan ditambahkan untuk mendukung kasus penggunaan di mana diperlukan beberapa contoh dari jenis tugas yang sama (yaitu implementasi yang sama) dengan jadwal berbeda. Schedule
disimpan di task_data
bersama dengan data reguler lainnya. Berbeda dengan tugas berulang statis , tugas dinamis tidak akan secara otomatis menjadwalkan pelaksanaan tugas. Terserah pengguna untuk membuat instance dan memperbarui jadwal yang sudah ada jika perlu (menggunakan antarmuka SchedulerClient
). Lihat contoh RecurringTaskWithPersistentScheduleMain.java untuk detail selengkapnya.
Buat menggunakan Tasks.recurringWithPersistentSchedule(..)
.
Istilah tugas satu kali digunakan untuk tugas yang memiliki waktu eksekusi tunggal. Selain menyandikan data ke dalam instanceId
dari eksekusi tugas, dimungkinkan untuk menyimpan data biner arbitrer di bidang terpisah untuk digunakan pada waktu eksekusi. Secara default, serialisasi Java digunakan untuk menyusun/menghapus data.
Buat menggunakan Tasks.oneTime(..)
.
Untuk tugas yang tidak sesuai dengan kategori di atas, dimungkinkan untuk sepenuhnya menyesuaikan perilaku tugas menggunakan Tasks.custom(..)
.
Kasus penggunaan mungkin:
Selama eksekusi, penjadwal secara teratur memperbarui waktu detak jantung untuk eksekusi tugas. Jika suatu eksekusi ditandai sebagai sedang dieksekusi, namun tidak menerima pembaruan pada waktu detak jantung, eksekusi tersebut akan dianggap sebagai eksekusi mati setelah waktu X. Misalnya, hal ini dapat terjadi jika JVM yang menjalankan penjadwal tiba-tiba keluar.
Ketika eksekusi mati ditemukan, Task
dikonsultasikan untuk melihat apa yang harus dilakukan. RecurringTask
yang mati biasanya dijadwal ulang ke now()
.
Meskipun db-scheduler awalnya ditargetkan pada kasus penggunaan throughput rendah hingga menengah, db-scheduler menangani kasus penggunaan throughput tinggi (1000+ eksekusi/detik) dengan cukup baik karena fakta bahwa model datanya sangat sederhana, terdiri dari satu meja eksekusi. Untuk memahami bagaimana kinerjanya, ada baiknya untuk mempertimbangkan pernyataan SQL yang dijalankannya per batch eksekusi.
Strategi polling asli dan default, fetch-and-lock-on-execute
, akan melakukan hal berikut:
select
kumpulan eksekusi yang jatuh tempoupdate
eksekusi ke picked=true
untuk instance penjadwal ini. Mungkin ketinggalan karena jadwal yang bersaing.update
atau delete
catatan sesuai dengan penangannya.Total per batch: 1 pilihan, 2 * pembaruan ukuran batch (tidak termasuk kesalahan)
Di v10, strategi polling baru ( lock-and-fetch
) telah ditambahkan. Ini memanfaatkan fakta bahwa sebagian besar database sekarang memiliki dukungan untuk SKIP LOCKED
dalam pernyataan SELECT FOR UPDATE
(lihat blog kuadran ke-2). Dengan menggunakan strategi seperti itu, dimungkinkan untuk mengambil eksekusi yang sudah dikunci sebelumnya, sehingga mengurangi satu pernyataan:
select for update .. skip locked
kumpulan eksekusi yang jatuh tempo. Ini sudah dipilih oleh instance penjadwal.update
atau delete
catatan sesuai dengan penangannya.Total per batch: 1 pilih-dan-perbarui, 1 * pembaruan ukuran batch (tidak ada yang terlewat)
Untuk mendapatkan gambaran tentang apa yang diharapkan dari db-scheduler, lihat hasil pengujian yang dijalankan di GCP di bawah. Pengujian dijalankan dengan beberapa konfigurasi berbeda, namun masing-masing menggunakan 4 instance penjadwal bersaing yang berjalan pada VM terpisah. TPS adalah sekitar. transaksi per detik seperti yang ditunjukkan di GCP.
Pengambilan throughput (ex/s) | Pengambilan TPS (perkiraan) | Penguncian dan pengambilan throughput (ex/s) | Penguncian dan pengambilan TPS (perkiraan) | |
---|---|---|---|---|
Postgres 4core 25gb ram, 4xVMs(2-core) | ||||
20 utas, bawah 4.0, atas 20.0 | 2000 | 9000 | 10600 | 11500 |
100 utas, bawah 2.0, atas 6.0 | 2560 | 11000 | 11200 | 11200 |
Postgres 8core 50gb ram, 4xVMs(4-core) | ||||
50 utas, bawah: 0,5, atas: 4.0 | 4000 | 22000 | 11840 | 10300 |
Pengamatan untuk tes ini:
fetch-and-lock-on-execute
lock-and-fetch
Saat ini, strategi lock-and-fetch
polling diterapkan hanya untuk Postgres. Kontribusi yang menambahkan dukungan untuk lebih banyak database dipersilakan.
Ada sejumlah pengguna yang menggunakan db-scheduler untuk kasus penggunaan throughput tinggi. Lihat misalnya:
Tidak ada jaminan bahwa semua instans dalam jadwal untuk RecurringTask
akan dieksekusi. Schedule
dikonsultasikan setelah pelaksanaan tugas sebelumnya selesai, dan waktu terdekat di masa depan akan dipilih untuk waktu pelaksanaan berikutnya. Jenis tugas baru mungkin ditambahkan di masa mendatang untuk menyediakan fungsionalitas tersebut.
Metode pada SchedulerClient
( schedule
, cancel
, reschedule
) akan dijalankan menggunakan Connection
baru dari DataSource
yang disediakan. Agar tindakan menjadi bagian dari transaksi, tindakan tersebut harus ditangani oleh DataSource
yang disediakan, misalnya menggunakan sesuatu seperti TransactionAwareDataSourceProxy
dari Spring.
Saat ini, ketepatan db-scheduler bergantung pada pollingInterval
(default 10 detik) yang menentukan seberapa sering mencari eksekusi yang jatuh tempo di tabel. Jika Anda tahu apa yang Anda lakukan, penjadwal mungkin diinstruksikan pada waktu proses untuk "melihat lebih awal" melalui scheduler.triggerCheckForDueExecutions()
. (Lihat juga enableImmediateExecution()
pada Builder
)
Lihat rilis untuk catatan rilis.
Meningkatkan ke 15.x
priority
dan indeks priority_execution_time_idx
harus ditambahkan ke skema database. Lihat definisi tabel untuk postgresql, Oracle atau mysql. Suatu saat kolom ini akan diwajibkan. Hal ini akan dijelaskan pada catatan rilis/peningkatan mendatang.Meningkatkan ke 8.x
boolean isDeterministic()
untuk menunjukkan apakah jadwal tersebut akan selalu menghasilkan instans yang sama atau tidak.Meningkatkan ke 4.x
consecutive_failures
ke skema database. Lihat definisi tabel untuk postgresql, Oracle atau mysql. null
ditangani sebagai 0, jadi tidak perlu memperbarui catatan yang ada.Meningkatkan ke 3.x
Tasks
Meningkatkan ke 2.x
task_data
ke skema database. Lihat definisi tabel untuk postgresql, Oracle atau mysql. Prasyarat
Ikuti langkah-langkah berikut:
Kloning repositori.
git clone https://github.com/kagkarlsson/db-scheduler
cd db-scheduler
Bangun menggunakan Maven (lewati tes dengan menambahkan -DskipTests=true
)
mvn package
Spesifikasi yang direkomendasikan
Beberapa pengguna mengalami kegagalan pengujian intermiten saat menjalankan VM inti tunggal. Oleh karena itu, disarankan untuk menggunakan minimal:
db-scheduler
padahal ada Quartz
? Tujuan dari db-scheduler
adalah menjadi non-invasif dan mudah digunakan, namun tetap memecahkan masalah persistensi, dan masalah koordinasi cluster. Awalnya ditargetkan pada aplikasi dengan skema database sederhana, yang menambahkan 11 tabel akan terasa sedikit berlebihan.. Pembaruan: Selain itu, hingga sekarang (2024), Quartz juga tampaknya tidak dipelihara secara aktif.
CIUMAN. Ini adalah jenis aplikasi negara bersama yang paling umum dimiliki.
Silakan buat masalah dengan permintaan fitur dan kita dapat mendiskusikannya di sana. Jika Anda tidak sabar (atau ingin berkontribusi), permintaan penarikan dipersilakan :)
Ya. Ini digunakan dalam produksi di sejumlah perusahaan, dan sejauh ini berjalan lancar.