Task-scheduler สำหรับ Java ที่ได้รับแรงบันดาลใจจากความต้องการ java.util.concurrent.ScheduledExecutorService
แบบคลัสเตอร์ที่ง่ายกว่า Quartz
ผู้ใช้ก็ชื่นชมเช่นกัน (cbarbosa2, rafaelhofmann, BukhariH):
lib ของคุณสุดยอด! ฉันดีใจมากที่ได้กำจัด Quartz และเปลี่ยนเป็นของคุณแทน ซึ่งง่ายกว่ามากในการจัดการ!
ซีบาร์โบซา2
ดูเพิ่มเติมว่าทำไมไม่ควอตซ์?
< dependency >
< groupId >com.github.kagkarlsson</ groupId >
< artifactId >db-scheduler</ artifactId >
< version >15.0.0</ version >
</ dependency >
สร้างตาราง scheduled_tasks
ในฐานข้อมูลสคีมาของคุณ ดูคำจำกัดความของตารางสำหรับ postgresql, oracle, mssql หรือ mysql
สร้างอินสแตนซ์และเริ่มตัวกำหนดตารางเวลา ซึ่งจะเริ่มงานที่เกิดซ้ำที่กำหนดไว้
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 ();
หากต้องการตัวอย่างเพิ่มเติม โปรดอ่านต่อ สำหรับรายละเอียดเกี่ยวกับการทำงานภายใน โปรดดูวิธีการทำงาน หากคุณมีแอปพลิเคชัน Spring Boot โปรดดูที่การใช้งาน Spring Boot
รายชื่อองค์กรที่ทราบว่าใช้งาน db-scheduler ในการใช้งานจริง:
บริษัท | คำอธิบาย |
---|---|
ดิจิโพสต์ | ผู้ให้บริการกล่องจดหมายดิจิทัลในนอร์เวย์ |
วี กรุ๊ป | หนึ่งในกลุ่มการขนส่งที่ใหญ่ที่สุดในกลุ่มประเทศนอร์ดิก |
ฉลาด | วิธีส่งเงินไปต่างประเทศที่ประหยัดและรวดเร็ว |
การศึกษาวิชาชีพเบกเกอร์ | |
มอนิเตอร์ | บริการตรวจสอบเว็บไซต์ |
รถตัก | การทดสอบโหลดสำหรับแอปพลิเคชันเว็บ |
สเตเทน เวกเวเซ่น | การบริหารถนนสาธารณะของนอร์เวย์ |
ปีแสง | วิธีที่ง่ายและเข้าถึงได้เพื่อลงทุนเงินของคุณทั่วโลก |
มูลค่าทรัพย์สินสุทธิ | ฝ่ายบริหารแรงงานและสวัสดิการนอร์เวย์ |
โมเดิร์นลูป | ปรับขนาดตามความต้องการในการจ้างงานของบริษัทของคุณโดยใช้ ModernLoop เพื่อเพิ่มประสิทธิภาพในการกำหนดเวลาการสัมภาษณ์ การสื่อสาร และการประสานงาน |
ดิฟเฟีย | บริษัท eHealth ของนอร์เวย์ |
หงส์ | Swan ช่วยให้นักพัฒนาฝังบริการทางธนาคารลงในผลิตภัณฑ์ของตนได้อย่างง่ายดาย |
ทอมร่า | TOMRA เป็นบริษัทข้ามชาติของนอร์เวย์ที่ออกแบบและผลิตเครื่องจำหน่ายสินค้าอัตโนมัติแบบย้อนกลับเพื่อการรีไซเคิล |
อย่าลังเลที่จะเปิด PR เพื่อเพิ่มองค์กรของคุณลงในรายการ
ดูตัวอย่างที่รันได้ด้วย
กำหนดงาน ที่เกิดซ้ำ และกำหนดเวลาการดำเนินการครั้งแรกของงานเมื่อเริ่มต้นใช้งานโดยใช้วิธีสร้าง startTasks
เมื่อเสร็จสิ้น งานจะถูกจัดกำหนดการใหม่ตามกำหนดการที่กำหนดไว้ (ดูประเภทกำหนดการที่กำหนดไว้ล่วงหน้า)
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 ();
สำหรับงานที่เกิดซ้ำซึ่งมีหลายอินสแตนซ์และกำหนดเวลา โปรดดูตัวอย่าง RecurringTaskWithPersistentScheduleMain.java
อินสแตนซ์ของงานที่ ทำครั้งเดียว จะมีเวลาดำเนินการเพียงครั้งเดียวในอนาคต (เช่น ไม่เกิดซ้ำ) instance-id จะต้องไม่ซ้ำกันภายในงานนี้ และอาจใช้เพื่อเข้ารหัสข้อมูลเมตาบางส่วน (เช่น id) สำหรับสถานะที่ซับซ้อนมากขึ้น รองรับอ็อบเจ็กต์ Java ที่ทำให้ซีเรียลไลซ์แบบกำหนดเองได้ (ตามที่ใช้ในตัวอย่าง)
กำหนดงานที่ทำ ครั้งเดียว และเริ่มตัวกำหนดเวลา:
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 ();
... และเมื่อถึงจุดหนึ่ง (ที่รันไทม์) การดำเนินการจะถูกกำหนดเวลาโดยใช้ 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 )));
ตัวอย่าง | คำอธิบาย |
---|---|
EnableImmediateExecutionMain.java | เมื่อกำหนดเวลาการดำเนินการให้ทำงาน now() หรือก่อนหน้า ตัว Scheduler ในเครื่องจะได้รับคำแนะนำเกี่ยวกับเรื่องนี้ และ "ปลุก" เพื่อตรวจสอบการดำเนินการใหม่เร็วกว่าปกติ (ตามที่กำหนดค่าโดย pollingInterval |
MaxRetriesMain.java | วิธีกำหนดขีดจำกัดจำนวนครั้งในการลองดำเนินการใหม่ |
ExponentialBackoffMain.java | วิธีใช้ Exponential Backoff เป็นกลยุทธ์การลองใหม่แทนการหน่วงเวลาคงที่ตามค่าเริ่มต้น |
ExponentialBackoffWithMaxRetriesMain.java | วิธีใช้ Exponential Backoff เป็นกลยุทธ์การลองใหม่ และ การจำกัดจำนวนครั้งสูงสุดในการลองใหม่ |
การติดตามความคืบหน้าRecurringTaskMain.java | งานที่เกิดซ้ำอาจจัดเก็บ task_data ไว้เป็นวิธีการคงสถานะไว้ตลอดการดำเนินการ ตัวอย่างนี้แสดงให้เห็นว่า |
การวางไข่OtherTasksMain.java | สาธิตอินสแตนซ์การกำหนดเวลางานของอินสแตนซ์อื่นโดยใช้ executionContext.getSchedulerClient() |
SchedulerClientMain.java | แสดงให้เห็นถึงความสามารถบางอย่างของ SchedulerClient การกำหนดเวลา การดึงข้อมูลการดำเนินการตามกำหนดเวลา ฯลฯ |
RecurringTaskWithPersistentScheduleMain.java | งานที่เกิดซ้ำหลายอินสแตนซ์โดยที่ Schedule ถูกจัดเก็บไว้เป็นส่วนหนึ่งของ task_data ตัวอย่างเช่น เหมาะสำหรับแอปพลิเคชันที่มีผู้เช่าหลายราย โดยแต่ละผู้เช่าควรมีงานที่เกิดซ้ำ |
StatefulRecurringTaskWithPersistentScheduleMain.java | |
JsonSerializerMain.java | แทนที่การทำให้เป็นอนุกรมของ task_data จาก Java-serialization (ค่าเริ่มต้น) เป็น JSON |
JobChainingUsingTaskDataMain.java | การเชื่อมโยงงาน เช่น "เมื่ออินสแตนซ์นี้ดำเนินการเสร็จสิ้น ให้กำหนดเวลางานอื่น |
JobChainingUsingSeparateTasksMain.java | การผูกมัดงานดังที่กล่าวข้างต้น |
InterceptorMain.java | การใช้ ExecutionInterceptor เพื่อฉีดตรรกะก่อนและหลังการดำเนินการสำหรับ ExecutionHandler ทั้งหมด |
ตัวอย่าง | คำอธิบาย |
---|---|
ตัวอย่างพื้นฐาน | งานพื้นฐานแบบครั้งเดียวและงานที่เกิดซ้ำ |
TransactionallyStagedJob | ตัวอย่างของการจัดเตรียมงานตามทรานแซคชัน เช่น ตรวจสอบให้แน่ใจว่างานเบื้องหลังรัน หาก ธุรกรรมมีการดำเนินการ (พร้อมกับการแก้ไข db อื่นๆ) |
งานวิ่งระยะยาว | งานที่ใช้เวลานานจำเป็นต้อง รีสตาร์ทแอปพลิเคชัน และหลีกเลี่ยงการรีสตาร์ทตั้งแต่ต้น ตัวอย่างนี้แสดงให้เห็นถึงวิธี การคงความคืบหน้า ในการปิดระบบและเทคนิคเพิ่มเติมในการจำกัดงานให้รันทุกคืน |
การติดตามสถานะที่เกิดซ้ำ | งานที่เกิดซ้ำโดยมีสถานะที่สามารถแก้ไขได้หลังการรันแต่ละครั้ง |
ParallelJobSpawner | สาธิตวิธีใช้งานที่เกิดซ้ำเพื่อวางงานแบบครั้งเดียว เช่น การทำงานแบบขนาน |
JobChaining | งานครั้งเดียวที่มี หลายขั้นตอน ขั้นตอนถัดไปจะถูกกำหนดเวลาหลังจากขั้นตอนก่อนหน้าเสร็จสมบูรณ์ |
หลายอินสแตนซ์ที่เกิดซ้ำ | สาธิตวิธีการบรรลุ งานประเภทเดียวกันที่เกิดซ้ำหลายครั้ง แต่อาจมีกำหนดการและข้อมูลที่แตกต่างกัน |
ตัวกำหนดเวลาถูกสร้างขึ้นโดยใช้ตัวสร้าง Scheduler.create(...)
ตัวสร้างมีค่าเริ่มต้นที่สมเหตุสมผล แต่สามารถกำหนดค่าตัวเลือกต่อไปนี้ได้
.threads(int)
จำนวนเธรด ค่าเริ่มต้น 10
.pollingInterval(Duration)
ความถี่ที่ตัวกำหนดตารางเวลาตรวจสอบฐานข้อมูลว่ามีการดำเนินการครบกำหนดหรือไม่ ค่าเริ่มต้น 10s
.alwaysPersistTimestampInUTC()
ตัวกำหนดเวลาถือว่าคอลัมน์สำหรับการประทับเวลาที่มีอยู่ยังคงมีอยู่ใน Instant
s ไม่ใช่ LocalDateTime
s กล่าวคือผูกการประทับเวลากับโซนอย่างใด อย่างไรก็ตาม ฐานข้อมูลบางแห่งมีการรองรับประเภทดังกล่าวอย่างจำกัด (ซึ่งไม่มีข้อมูลโซน) หรือลักษณะเฉพาะอื่นๆ ทำให้ "จัดเก็บใน UTC เสมอ" เป็นทางเลือกที่ดีกว่า ในกรณีดังกล่าว ให้ใช้การตั้งค่านี้เพื่อจัดเก็บ Instants ในรูปแบบ UTC เสมอ PostgreSQL และ Oracle-schema ได้รับการทดสอบเพื่อรักษาข้อมูลโซน MySQL และ MariaDB -schema ไม่ใช้ และควรใช้การตั้งค่านี้ หมายเหตุ: สำหรับความเข้ากันได้แบบย้อนหลัง ลักษณะการทำงานเริ่มต้นสำหรับฐานข้อมูล "ไม่รู้จัก" คือถือว่าฐานข้อมูลรักษาโซนเวลาไว้ สำหรับฐานข้อมูล "ที่รู้จัก" โปรดดูที่คลาส AutodetectJdbcCustomization
.enableImmediateExecution()
หากเปิดใช้งานสิ่งนี้ ตัวกำหนดเวลาจะพยายามบอกใบ้ให้ Scheduler
ในเครื่องทราบว่ามีการดำเนินการที่จะดำเนินการหลังจากที่กำหนดเวลาให้ทำงาน now()
หรือเวลาในอดีต หมายเหตุ: หากการเรียกเพื่อ schedule(..)
/ reschedule(..)
เกิดขึ้นจากภายในธุรกรรม ตัวกำหนดเวลาอาจพยายามเรียกใช้ก่อนที่การอัปเดตจะมองเห็นได้ (ธุรกรรมไม่มีข้อผูกมัด) แม้ว่าจะยังคงมีอยู่ ดังนั้นแม้ว่าจะพลาดไปก็ตาม มันก็จะทำงานก่อน polling-interval
ถัดไป นอกจากนี้คุณยังอาจทริกเกอร์การตรวจสอบล่วงหน้าสำหรับการดำเนินการครบกำหนดทางโปรแกรมโดยใช้วิธี Scheduler scheduler.triggerCheckForDueExecutions()
) ค่าเริ่มต้น false
.registerShutdownHook()
ลงทะเบียน hook-Shutdown ที่จะเรียก Scheduler.stop()
เมื่อปิดเครื่อง ควรเรียก Stop เสมอเพื่อการปิดระบบอย่างค่อยเป็นค่อยไป และเพื่อหลีกเลี่ยงการประหารชีวิตที่ผิดพลาด
.shutdownMaxWait(Duration)
ระยะเวลาที่ตัวกำหนดตารางเวลาจะรอก่อนที่จะขัดจังหวะเธรดบริการของตัวดำเนินการ หากคุณพบว่าตัวเองใช้สิ่งนี้ ให้พิจารณาว่าเป็นไปได้หรือไม่ที่จะตรวจสอบ executionContext.getSchedulerState().isShuttingDown()
ใน ExecutionHandler เป็นประจำแทน และยกเลิกงานที่ใช้เวลานาน ค่าเริ่มต้น 30min
.enablePriority()
เป็นไปได้ที่จะกำหนดลำดับความสำคัญสำหรับการดำเนินการซึ่งกำหนดลำดับของการดึงการดำเนินการที่ครบกำหนดจากฐานข้อมูล การดำเนินการที่มีค่าสูงกว่าสำหรับลำดับความสำคัญจะทำงานก่อนการดำเนินการที่มีค่าต่ำกว่า (ในทางเทคนิค การจัดลำดับจะ order by priority desc, execution_time asc
) พิจารณาใช้ลำดับความสำคัญในช่วง 0-32000 เนื่องจากฟิลด์ถูกกำหนดเป็น SMALLINT
หากคุณต้องการค่าที่มากขึ้น ให้แก้ไขสคีมา ในตอนนี้ คุณลักษณะนี้เป็น แบบเลือกใช้ และ priority
ของคอลัมน์จำเป็นสำหรับผู้ใช้ที่เลือกเปิดใช้งานลำดับความสำคัญผ่านการตั้งค่าการกำหนดค่านี้เท่านั้น
ตั้งค่าลำดับความสำคัญต่ออินสแตนซ์โดยใช้ TaskInstance.Builder
:
scheduler . schedule (
MY_TASK
. instance ( "1" )
. priority ( 100 )
. scheduledTo ( Instant . now ()));
บันทึก:
(execution_time asc, priority desc)
(แทนที่ execution_time asc
แบบเก่า) อาจเป็นประโยชน์null
สำหรับลำดับความสำคัญอาจถูกตีความแตกต่างกันไปขึ้นอยู่กับฐานข้อมูล (ต่ำหรือสูง) หากคุณใช้งาน >1,000 การดำเนินการ/วินาที คุณอาจต้องการใช้กลยุทธ์การโพล lock-and-fetch
เพื่อลดค่าใช้จ่ายและปริมาณงานที่สูงขึ้น (อ่านเพิ่มเติม) ถ้าไม่เช่นนั้น fetch-and-lock-on-execute
ค่าเริ่มต้นจะถือว่าใช้ได้
.pollUsingFetchAndLockOnExecute(double, double)
ใช้กลยุทธ์การโพลเริ่มต้น fetch-and-lock-on-execute
หากการดึงข้อมูลครั้งล่าสุดจากฐานข้อมูลเป็นแบตช์เต็ม ( executionsPerBatchFractionOfThreads
) การดึงข้อมูลใหม่จะถูกทริกเกอร์เมื่อจำนวนการดำเนินการที่เหลือน้อยกว่าหรือเท่ากับ lowerLimitFractionOfThreads * nr-of-threads
การดำเนินการที่ดึงข้อมูลมาจะไม่ถูกล็อก/เลือก ดังนั้นตัวกำหนดเวลาจะแข่งขันกับอินสแตนซ์อื่นๆ สำหรับการล็อกเมื่อมีการดำเนินการ รองรับทุกฐานข้อมูล
ค่าเริ่มต้น: 0,5, 3.0
.pollUsingLockAndFetch(double, double)
ใช้ lock-and-fetch
กลยุทธ์การโพลซึ่งใช้ select for update .. skip locked
เพื่อค่าใช้จ่ายที่น้อยลง
หากการดึงข้อมูลครั้งล่าสุดจากฐานข้อมูลเป็นชุดเต็ม การดึงข้อมูลใหม่จะถูกทริกเกอร์เมื่อจำนวนการดำเนินการที่เหลือน้อยกว่าหรือเท่ากับ lowerLimitFractionOfThreads * nr-of-threads
จำนวนการดำเนินการที่ดึงมาในแต่ละครั้งจะเท่ากับ (upperLimitFractionOfThreads * nr-of-threads) - nr-executions-left
การดำเนินการที่ดึงข้อมูลมาถูกล็อค/เลือกแล้วสำหรับอินสแตนซ์ตัวกำหนดตารางเวลานี้ ดังนั้น จึงบันทึกคำสั่ง UPDATE
หนึ่งคำสั่ง
สำหรับการใช้งานปกติ ให้ตั้งค่าเป็น 0.5, 1.0
เป็นต้น
สำหรับปริมาณงานสูง (เช่น ทำให้เธรดไม่ว่าง) ให้ตั้งค่าเป็นเช่น 1.0, 4.0
ขณะนี้ฮาร์ทบีทไม่ได้รับการอัพเดตสำหรับการดำเนินการที่เลือกในคิว (ใช้ได้หาก upperLimitFractionOfThreads > 1.0
) หากพวกเขาอยู่ที่นั่นนานกว่า 4 * heartbeat-interval
(ค่าเริ่มต้น 20m
) ไม่เริ่มดำเนินการ พวกเขาจะถูกตรวจพบว่า ตาย และมีแนวโน้มที่จะถูกปลดล็อคอีกครั้ง (กำหนดโดย DeadExecutionHandler
) ปัจจุบันได้รับการสนับสนุนโดย postgres sql-server ยังรองรับสิ่งนี้ด้วย แต่การทดสอบแสดงให้เห็นว่าสิ่งนี้มีแนวโน้มที่จะเกิดการหยุดชะงัก ดังนั้นจึงไม่แนะนำจนกว่าจะเข้าใจหรือแก้ไขได้
.heartbeatInterval(Duration)
ความถี่ในการอัปเดตการประทับเวลาฮาร์ทบีทสำหรับการเรียกใช้การดำเนินการ ค่าเริ่มต้น 5m
.missedHeartbeatsLimit(int)
อาจพลาดการเต้นของหัวใจไปกี่ครั้งจึงจะถือว่าการประหารชีวิต ค่าเริ่มต้น 6
.addExecutionInterceptor(ExecutionInterceptor)
เพิ่ม ExecutionInterceptor
ซึ่งอาจแทรกตรรกะรอบการดำเนินการ สำหรับ Spring Boot เพียงลงทะเบียน Bean ประเภท ExecutionInterceptor
.addSchedulerListener(SchedulerListener)
เพิ่ม SchedulerListener
ซึ่งจะได้รับเหตุการณ์ที่เกี่ยวข้องกับ Scheduler และการดำเนินการ สำหรับ Spring Boot เพียงลงทะเบียน Bean ประเภท SchedulerListener
.schedulerName(SchedulerName)
ชื่อของอินสแตนซ์ตัวกำหนดเวลานี้ ชื่อจะถูกเก็บไว้ในฐานข้อมูลเมื่อมีการเลือกการดำเนินการโดยตัวกำหนดตารางเวลา <hostname>
เริ่มต้น
.tableName(String)
ชื่อของตารางที่ใช้ในการติดตามการดำเนินการงาน เปลี่ยนชื่อในคำจำกัดความของตารางตามเมื่อสร้างตาราง ค่าเริ่มต้น scheduled_tasks
.serializer(Serializer)
การใช้งาน Serializer เพื่อใช้เมื่อซีเรียลไลซ์ข้อมูลงาน ค่าเริ่มต้นคือการใช้การทำให้เป็นอนุกรม Java มาตรฐาน แต่ db-scheduler ยังรวม GsonSerializer
และ JacksonSerializer
ไว้ด้วย ดูตัวอย่างสำหรับ KotlinSerializer ดูเอกสารเพิ่มเติมภายใต้ Serializers
.executorService(ExecutorService)
หากระบุ ให้ใช้บริการตัวดำเนินการที่ได้รับการจัดการจากภายนอกเพื่อเรียกใช้การดำเนินการ ตามหลักการแล้ว ควรระบุจำนวนเธรดที่จะใช้ (สำหรับการเพิ่มประสิทธิภาพการโพลของตัวกำหนดตารางเวลา) ค่าเริ่มต้น null
.deleteUnresolvedAfter(Duration)
เวลาที่การดำเนินการกับงานที่ไม่รู้จักจะถูกลบโดยอัตโนมัติ โดยทั่วไปสิ่งเหล่านี้อาจเป็นงานเก่าๆ ที่ไม่ได้ใช้งานอีกต่อไป ค่านี้ไม่เป็นศูนย์เพื่อป้องกันการลบงานโดยไม่ตั้งใจเนื่องจากข้อผิดพลาดในการกำหนดค่า (งานที่ทราบหายไป) และปัญหาระหว่างการอัพเกรด ค่าเริ่มต้น 14d
.jdbcCustomization(JdbcCustomization)
db-scheduler พยายามตรวจจับฐานข้อมูลอัตโนมัติที่ใช้เพื่อดูว่าจำเป็นต้องปรับแต่งการโต้ตอบ jdbc ใด ๆ หรือไม่ วิธีนี้เป็น Escape-hatch เพื่ออนุญาตให้ตั้งค่า JdbcCustomizations
ได้อย่างชัดเจน การตรวจจับอัตโนมัติเริ่มต้น
.commitWhenAutocommitDisabled(boolean)
ตามค่าเริ่มต้น ไม่มีการคอมมิตบน DataSource Connections หากปิดใช้งานการยืนยันอัตโนมัติ จะถือว่าธุรกรรมได้รับการจัดการโดยผู้จัดการธุรกรรมภายนอก ตั้งค่าคุณสมบัตินี้เป็น true
เพื่อแทนที่ลักษณะการทำงานนี้ และให้ตัวจัดกำหนดการออกคอมมิตเสมอ ค่าเริ่มต้น false
.failureLogging(Level, boolean)
กำหนดค่าวิธีการบันทึกความล้มเหลวของงาน เช่น Throwable
ที่ถูกโยนจากตัวจัดการการดำเนินการงาน ใช้ระดับการบันทึก OFF
เพื่อปิดใช้งานการบันทึกประเภทนี้โดยสมบูรณ์ WARN, true
งานถูกสร้างขึ้นโดยใช้หนึ่งในคลาสตัวสร้างใน Tasks
ตัวสร้างมีค่าเริ่มต้นที่สมเหตุสมผล แต่ตัวเลือกต่อไปนี้สามารถแทนที่ได้
ตัวเลือก | ค่าเริ่มต้น | คำอธิบาย |
---|---|---|
.onFailure(FailureHandler) | ดูคำอธิบาย | จะทำอย่างไรเมื่อ ExecutionHandler ส่งข้อยกเว้น ตามค่าเริ่มต้น งานที่เกิดซ้ำ จะถูกจัดกำหนดการใหม่ตาม Schedule งานแบบครั้งเดียว ที่จะพยายามอีกครั้งใน 5 นาที |
.onDeadExecution(DeadExecutionHandler) | ReviveDeadExecution | จะทำอย่างไรเมื่อตรวจพบ การดำเนินการที่ไม่ทำงาน เช่น การดำเนินการที่มีการประทับเวลาการเต้นของหัวใจเก่า โดยค่าเริ่มต้น การดำเนินการที่ไม่ทำงานจะถูกกำหนดเวลาใหม่เป็น now() |
.initialData(T initialData) | null | ข้อมูลที่จะใช้ในครั้งแรกที่มีการกำหนด เวลางานที่เกิดซ้ำ |
ห้องสมุดประกอบด้วยการดำเนินการตามกำหนดการจำนวนหนึ่งสำหรับงานที่เกิดซ้ำ ดู Schedules
เรียน
กำหนดการ | คำอธิบาย |
---|---|
.daily(LocalTime ...) | วิ่งทุกวันตามเวลาที่กำหนด สามารถเลือกระบุโซนเวลาได้ |
.fixedDelay(Duration) | เวลาดำเนินการครั้งถัดไปคือ Duration หลังจากการดำเนินการเสร็จสิ้นครั้งล่าสุด หมายเหตุ: Schedule นี้กำหนดเวลาการดำเนินการเริ่มต้นเป็น Instant.now() เมื่อใช้ใน startTasks(...) |
.cron(String) | cron-expression แบบสปริง (v5.3+) รูปแบบ - ถูกตีความว่าเป็นกำหนดการที่ถูกปิดใช้งาน |
ตัวเลือกอื่นในการกำหนดค่ากำหนดการคือการอ่านรูปแบบสตริงด้วย Schedules.parse(String)
รูปแบบที่มีอยู่ในปัจจุบันคือ:
ลวดลาย | คำอธิบาย |
---|---|
FIXED_DELAY|Ns | เช่นเดียวกับ .fixedDelay(Duration) โดยตั้งระยะเวลาเป็น N วินาที |
DAILY|12:30,15:30...(|time_zone) | เช่นเดียวกับ .daily(LocalTime) พร้อมเขตเวลาเพิ่มเติม (เช่น ยุโรป/โรม, UTC) |
- | กำหนดการที่ปิดใช้งาน |
ดูรายละเอียดเพิ่มเติมเกี่ยวกับรูปแบบเขตเวลาได้ที่นี่
Schedule
สามารถทำเครื่องหมายว่าปิดใช้งานได้ ตัวกำหนดเวลาจะไม่กำหนดเวลาการดำเนินการเริ่มต้นสำหรับงานที่มีกำหนดเวลาที่ถูกปิดใช้งาน และจะลบการดำเนินการที่มีอยู่สำหรับงานนั้นออก
อินสแตนซ์งานอาจมีข้อมูลที่เกี่ยวข้องบางส่วนในฟิลด์ task_data
ตัวกำหนดเวลาใช้ Serializer
เพื่ออ่านและเขียนข้อมูลนี้ไปยังฐานข้อมูล ตามค่าเริ่มต้น จะใช้การทำให้เป็นอนุกรม Java มาตรฐาน แต่มีตัวเลือกจำนวนหนึ่งให้ไว้:
GsonSerializer
JacksonSerializer
สำหรับการทำให้เป็นอนุกรมของ Java ขอแนะนำให้ระบุ serialVersionUID
เพื่อให้สามารถพัฒนาคลาสที่แสดงถึงข้อมูลได้ หากไม่ได้ระบุ และคลาสเปลี่ยนแปลง การดีซีเรียลไลซ์อาจล้มเหลวด้วย InvalidClassException
หากสิ่งนี้เกิดขึ้น ให้ค้นหาและตั้งค่า serialVersionUID
ที่สร้างขึ้นโดยอัตโนมัติในปัจจุบันอย่างชัดเจน จากนั้นจะเป็นไปได้ที่จะทำการเปลี่ยนแปลงแบบไม่ทำลายชั้นเรียน
หากคุณต้องการย้ายจากการทำให้เป็นอนุกรมของ Java ไปยัง GsonSerializer
ให้กำหนดค่าตัวกำหนดเวลาเพื่อใช้ SerializerWithFallbackDeserializers
:
. serializer ( new SerializerWithFallbackDeserializers ( new GsonSerializer (), new JavaSerializer ()))
สำหรับแอปพลิเคชัน Spring Boot จะมีสตาร์ทเตอร์ db-scheduler-spring-boot-starter
ทำให้การเดินสายตัวกำหนดตารางเวลาง่ายมาก (ดูตัวอย่างโครงการฉบับเต็ม)
DataSource
ที่ใช้งานได้พร้อมสคีมาเริ่มต้นแล้ว (ในตัวอย่าง HSQLDB ถูกใช้และสคีมาจะถูกนำไปใช้โดยอัตโนมัติ)< dependency >
< groupId >com.github.kagkarlsson</ groupId >
< artifactId >db-scheduler-spring-boot-starter</ artifactId >
< version >15.0.0</ version >
</ dependency >
Task
ของคุณเป็น Spring beans หากเกิดขึ้นเป็นประจำ พวกเขาจะถูกรับและเริ่มต้นโดยอัตโนมัติScheduler
ในข้อมูลความสมบูรณ์ของตัวกระตุ้น คุณต้องเปิดใช้งานตัวบ่งชี้ความสมบูรณ์ของ db-scheduler
ข้อมูลสุขภาพฤดูใบไม้ผลิ การกำหนดค่าส่วนใหญ่ทำผ่าน application.properties
การกำหนดค่าชื่อตัวกำหนดตารางเวลา ตัวซีเรียลไลเซอร์ และตัวดำเนินการบริการทำได้โดยการเพิ่ม bean ประเภท DbSchedulerCustomizer
ให้กับบริบท Spring ของคุณ
# 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
คุณสามารถใช้ Scheduler
เพื่อโต้ตอบกับการดำเนินการในอนาคตที่ยังคงมีอยู่ได้ สำหรับสถานการณ์ที่ไม่จำเป็นต้องใช้ Scheduler
แบบเต็ม คุณสามารถสร้าง SchedulerClient ที่ง่ายกว่าได้โดยใช้ตัวสร้าง:
SchedulerClient . Builder . create ( dataSource , taskDefinitions ). build ()
จะช่วยให้สามารถดำเนินการเช่น:
ตารางฐานข้อมูลเดียวใช้เพื่อติดตามการดำเนินการงานในอนาคต เมื่อถึงกำหนดการดำเนินการงาน db-scheduler จะเลือกและดำเนินการ เมื่อดำเนินการเสร็จสิ้นจะมีการปรึกษาหารือเกี่ยวกับ Task
เพื่อดูว่าควรทำอะไร ตัวอย่างเช่น โดยทั่วไปงาน RecurringTask
จะมีการจัดกำหนดการใหม่ในอนาคตตาม Schedule
ตัวกำหนดเวลาใช้การล็อกในแง่ดีหรือเลือกเพื่ออัปเดต (ขึ้นอยู่กับกลยุทธ์การโพล) เพื่อรับประกันว่าอินสแตนซ์ตัวกำหนดเวลาเพียงตัวเดียวเท่านั้นที่จะเลือกและรันการดำเนินการงานได้
คำว่า งานที่เกิดซ้ำ ใช้สำหรับงานที่ควรดำเนินการเป็นประจำตามกำหนดการบางอย่าง
เมื่อการดำเนินการของงานที่เกิดซ้ำเสร็จสิ้น จะมีการพิจารณา Schedule
เพื่อกำหนดว่าครั้งต่อไปสำหรับการดำเนินการควรเป็นอย่างไร และการดำเนินการงานในอนาคตจะถูกสร้างขึ้นสำหรับเวลานั้น (เช่น กำหนดเวลาใหม่ ) เวลาที่เลือกจะเป็นเวลาที่ใกล้ที่สุดตาม Schedule
แต่ยังเป็นเวลาในอนาคต
งานที่เกิดซ้ำมีสองประเภท ได้แก่ งานที่เกิดซ้ำ แบบคง ที่ปกติ โดยที่ Schedule
ถูกกำหนดแบบคงที่ในโค้ด และงานที่เกิดซ้ำ แบบไดนามิก โดยที่ Schedule
ถูกกำหนด ณ รันไทม์และคงอยู่ในฐานข้อมูล (ยังคงต้องการเพียงตารางเดียว) .
งานที่เกิดซ้ำ แบบคง ที่เป็นงานที่พบบ่อยที่สุดและเหมาะสำหรับงานพื้นหลังปกติ เนื่องจากตัวกำหนดเวลาจะจัดกำหนดการอินสแตนซ์ของงานโดยอัตโนมัติหากไม่มีอยู่ และยังอัปเดตเวลาดำเนินการครั้งถัดไปหากมีการอัปเดต Schedule
ในการสร้างการดำเนินการเริ่มต้นสำหรับงานที่เกิดซ้ำแบบคงที่ ตัวกำหนดเวลามีเมธอด startTasks(...)
ที่จะรับรายการงานที่ควรจะ "เริ่มต้น" หากยังไม่มีการดำเนินการที่มีอยู่ เวลาดำเนินการเริ่มต้นถูกกำหนดโดย Schedule
หากงานนั้นมีการดำเนินการในอนาคตอยู่แล้ว (เช่น ได้เริ่มต้นแล้วอย่างน้อยหนึ่งครั้งก่อนหน้านี้) แต่ Schedule
ที่อัปเดตในขณะนี้บ่งชี้เวลาดำเนินการอื่น การดำเนินการที่มีอยู่จะถูกจัดกำหนดการใหม่เป็นเวลาดำเนินการใหม่ (ยกเว้นเวลา ที่ไม่ได้กำหนดไว้ กำหนดการเช่น FixedDelay
ซึ่งเวลาดำเนินการใหม่จะอยู่ต่อไปในอนาคต)
สร้างโดยใช้ Tasks.recurring(..)
งานที่เกิดซ้ำ แบบไดนามิก เป็นส่วนเพิ่มเติมในภายหลังของ db-scheduler และถูกเพิ่มเพื่อรองรับกรณีการใช้งานที่จำเป็นต้องมีอินสแตนซ์หลายรายการของงานประเภทเดียวกัน (เช่น การใช้งานเดียวกัน) ที่มีกำหนดเวลาต่างกัน Schedule
จะยังคงอยู่ใน task_data
ควบคู่ไปกับข้อมูลปกติใดๆ แตกต่างจากงานที่เกิดซ้ำ แบบคง ที่ งานไดนามิกจะไม่กำหนดเวลาอินสแตนซ์ของงานโดยอัตโนมัติ ขึ้นอยู่กับผู้ใช้ที่จะสร้างอินสแตนซ์และอัปเดตกำหนดการสำหรับอินสแตนซ์ที่มีอยู่หากจำเป็น (โดยใช้อินเทอร์เฟซ SchedulerClient
) ดูตัวอย่าง RecurringTaskWithPersistentScheduleMain.java สำหรับรายละเอียดเพิ่มเติม
สร้างโดยใช้ Tasks.recurringWithPersistentSchedule(..)
คำว่า งานครั้งเดียว ใช้สำหรับงานที่มีเวลาดำเนินการครั้งเดียว นอกเหนือจากการเข้ารหัสข้อมูลลงใน instanceId
ของการดำเนินการงานแล้ว ยังสามารถจัดเก็บข้อมูลไบนารี่ที่กำหนดเองในฟิลด์แยกต่างหากเพื่อใช้ในเวลาดำเนินการได้อีกด้วย ตามค่าเริ่มต้น การทำให้เป็นอนุกรมของ Java จะถูกใช้เพื่อจัดเรียง/ยกเลิกการจัดเรียงข้อมูล
สร้างโดยใช้ Tasks.oneTime(..)
สำหรับงานที่ไม่เหมาะสมกับหมวดหมู่ข้างต้น คุณสามารถปรับแต่งพฤติกรรมของงานได้อย่างเต็มที่โดยใช้ Tasks.custom(..)
กรณีการใช้งานอาจเป็น:
ในระหว่างการดำเนินการ ตัวกำหนดเวลาจะอัปเดตเวลาฮาร์ทบีทสำหรับการดำเนินการเป็นประจำ หากการดำเนินการถูกทำเครื่องหมายว่ากำลังดำเนินการ แต่ไม่ได้รับการอัปเดตสำหรับฮาร์ทบีทไทม์ จะถือว่าเป็นการ ดำเนินการที่ไม่ทำงาน หลังจากเวลา X ซึ่งอาจเกิดขึ้นได้ เช่น หาก JVM ที่รันตัวกำหนดตารางเวลาออกอย่างกะทันหัน
เมื่อพบการประหารชีวิตที่ตาย จะมีการพิจารณา Task
เพื่อดูว่าควรทำอย่างไร โดยทั่วไป RecurringTask
ที่ไม่ทำงานจะถูกจัดกำหนดการใหม่เป็น now()
แม้ว่าในตอนแรก db-scheduler ได้รับการกำหนดเป้าหมายไปที่กรณีการใช้งานปริมาณงานต่ำถึงปานกลาง แต่ก็จัดการกรณีการใช้งานที่มีปริมาณงานสูง (การดำเนินการมากกว่า 1,000 ครั้งต่อวินาที) ค่อนข้างดีเนื่องจากโมเดลข้อมูลนั้นง่ายมาก ประกอบด้วย ตารางการประหารชีวิตเพียงตารางเดียว เพื่อให้เข้าใจถึงวิธีการทำงาน จะเป็นประโยชน์ในการพิจารณาคำสั่ง SQL ที่รันต่อชุดการประมวลผล
กลยุทธ์การโพลแบบเดิมและแบบดีฟอลต์ fetch-and-lock-on-execute
จะทำสิ่งต่อไปนี้:
select
ชุดของการดำเนินการที่ครบกำหนดupdate
การดำเนินการเป็น picked=true
สำหรับอินสแตนซ์ตัวกำหนดตารางเวลานี้ อาจพลาดเนื่องจากตารางการแข่งขันที่แข่งขันกันupdate
หรือ delete
คอร์ดตามตัวจัดการจำนวนรวมต่อชุด: เลือก 1 รายการ, 2 * การอัปเดตขนาดชุดงาน (ไม่รวมการพลาด)
ใน v10 มีการเพิ่มกลยุทธ์การโพลใหม่ ( lock-and-fetch
) ใช้ข้อเท็จจริงที่ว่าขณะนี้ฐานข้อมูลส่วนใหญ่รองรับ SKIP LOCKED
ในคำสั่ง SELECT FOR UPDATE
(ดูบล็อก 2ndquadrant) การใช้กลยุทธ์ดังกล่าวทำให้สามารถดึงข้อมูลการดำเนินการที่ล็อคไว้ล่วงหน้าได้ และทำให้ได้รับคำสั่งน้อยลงหนึ่งคำสั่ง:
select for update .. skip locked
ชุดการดำเนินการที่ครบกำหนด สิ่งเหล่านี้จะถูกเลือกโดยอินสแตนซ์ตัวกำหนดเวลาupdate
หรือ delete
บันทึกตามตัวจัดการผลรวมต่อชุด: เลือกและอัปเดต 1 รายการ, อัปเดตขนาดชุดงาน 1 รายการ (ไม่พลาด)
หากต้องการทราบสิ่งที่คาดหวังจาก db-scheduler โปรดดูผลลัพธ์จากการทดสอบที่ทำงานใน GCP ด้านล่าง การทดสอบดำเนินการโดยใช้การกำหนดค่าที่แตกต่างกันเล็กน้อย แต่แต่ละรายการใช้อินสแตนซ์ตัวกำหนดตารางเวลาที่แข่งขันกัน 4 รายการที่ทำงานบน VM ที่แยกกัน TPS คือประมาณ. ธุรกรรมต่อวินาทีตามที่แสดงใน GCP
การดึงข้อมูลปริมาณงาน (อดีต) | การดึงข้อมูล TPS (ประมาณการ) | การล็อกและดึงข้อมูลปริมาณงาน (อดีต) | ล็อคและดึงข้อมูล TPS (ประมาณการ) | |
---|---|---|---|---|
Postgres 4core 25gb ram, 4xVMs(2-คอร์) | ||||
20 เธรด, ต่ำกว่า 4.0, บน 20.0 | 2000 | 9000 | 10600 | 11500 |
100 เธรด, ล่าง 2.0, บน 6.0 | 2560 | 11000 | 11200 | 11200 |
Postgres 8core 50gb ram, 4xVMs(4 คอร์) | ||||
50 เธรด ล่าง: 0.5 บน: 4.0 | 4000 | 22000 | 11840 | 10300 |
ข้อสังเกตสำหรับการทดสอบเหล่านี้:
fetch-and-lock-on-execute
lock-and-fetch
ปัจจุบัน lock-and-fetch
กลยุทธ์การโพลถูกนำมาใช้กับ Postgres เท่านั้น ยินดีสนับสนุนการเพิ่มการรองรับฐานข้อมูลเพิ่มเติม
มีผู้ใช้จำนวนหนึ่งที่ใช้ db-scheduler สำหรับกรณีการใช้งานที่มีปริมาณการประมวลผลสูง ดูตัวอย่าง:
ไม่มีการรับประกันว่า Instant ทั้งหมดในกำหนดการสำหรับ RecurringTask
จะถูกดำเนินการ Schedule
จะได้รับการปรึกษาหลังจากการดำเนินการงานก่อนหน้าเสร็จสิ้น และเวลาที่ใกล้ที่สุดในอนาคตจะถูกเลือกสำหรับเวลาดำเนินการครั้งถัดไป อาจมีการเพิ่มงานประเภทใหม่ในอนาคตเพื่อให้มีฟังก์ชันดังกล่าว
วิธีการบน SchedulerClient
( schedule
, cancel
, reschedule
) จะทำงานโดยใช้ Connection
ใหม่จาก DataSource
ที่ให้ไว้ หากต้องการให้การดำเนินการเป็นส่วนหนึ่งของธุรกรรม จะต้องได้รับการดูแลโดย DataSource
ที่ให้ไว้ เช่น การใช้บางอย่างเช่น TransactionAwareDataSourceProxy
ของ Spring
ปัจจุบันความแม่นยำของ db-scheduler ขึ้นอยู่กับ pollingInterval
(ค่าเริ่มต้น 10 วินาที) ซึ่งระบุความถี่ที่จะดูในตารางเพื่อดำเนินการตามกำหนด หากคุณรู้ว่าคุณกำลังทำอะไรอยู่ ตัวกำหนดเวลาอาจได้รับคำสั่งขณะรันไทม์ให้ "ดูล่วงหน้า" ผ่านทาง scheduler.triggerCheckForDueExecutions()
(ดูเพิ่มเติมที่ enableImmediateExecution()
บน Builder
)
ดูการเผยแพร่สำหรับบันทึกประจำรุ่น
อัปเกรดเป็น 15.x
priority
ของคอลัมน์และดัชนี priority_execution_time_idx
ลงในสคีมาฐานข้อมูล ดูคำจำกัดความของตารางสำหรับ postgresql, oracle หรือ mysql เมื่อถึงจุดหนึ่ง คอลัมน์นี้จะถูกบังคับใช้ สิ่งนี้จะมีความชัดเจนในบันทึกการเปิดตัว/การอัปเกรดในอนาคตอัปเกรดเป็น 8.x
boolean isDeterministic()
เพื่อระบุว่าจะสร้าง Instant เดียวกันหรือไม่อัปเกรดเป็น 4.x
consecutive_failures
ให้กับสคีมาฐานข้อมูล ดูคำจำกัดความของตารางสำหรับ postgresql, oracle หรือ mysql null
จะถูกจัดการเป็น 0 ดังนั้นจึงไม่จำเป็นต้องอัปเดตบันทึกที่มีอยู่อัปเกรดเป็น 3.x
Tasks
อัปเกรดเป็น 2.x
task_data
ให้กับสคีมาฐานข้อมูล ดูคำจำกัดความของตารางสำหรับ postgresql, oracle หรือ mysql ข้อกำหนดเบื้องต้น
ทำตามขั้นตอนเหล่านี้:
โคลนพื้นที่เก็บข้อมูล
git clone https://github.com/kagkarlsson/db-scheduler
cd db-scheduler
สร้างโดยใช้ Maven (ข้ามการทดสอบโดยเพิ่ม -DskipTests=true
)
mvn package
ข้อมูลจำเพาะที่แนะนำ
ผู้ใช้บางรายประสบปัญหาการทดสอบล้มเหลวเป็นระยะๆ เมื่อทำงานบน VM แบบคอร์เดียว ดังนั้นจึงแนะนำให้ใช้อย่างน้อย:
db-scheduler
เมื่อมี Quartz
? เป้าหมายของ db-scheduler
คือการไม่รุกรานและใช้งานง่าย แต่ยังคงแก้ปัญหาการคงอยู่และปัญหาการประสานงานของคลัสเตอร์ เดิมทีมีการกำหนดเป้าหมายไปที่แอปพลิเคชันที่มีสคีมาฐานข้อมูลเล็กน้อย ซึ่งการเพิ่ม 11 ตารางอาจทำให้รู้สึกว่าเกินกำลังไปหน่อย อัปเดต: นอกจากนี้ ณ ตอนนี้ (2024) ดูเหมือนว่า Quartz จะไม่ได้รับการบำรุงรักษาอย่างแข็งขันเช่นกัน
จูบ. เป็นประเภทแอปพลิเคชันสถานะที่ใช้ร่วมกันที่พบบ่อยที่สุด
โปรดสร้างปัญหากับคำขอคุณลักษณะ แล้วเราจะหารือเกี่ยวกับเรื่องนี้ที่นั่น หากคุณใจร้อน (หรือรู้สึกอยากมีส่วนร่วม) เรายินดีเป็นอย่างยิ่งที่จะขอดึงคำขอ :)
ใช่. มีการใช้ในการผลิตในบริษัทหลายแห่ง และจนถึงขณะนี้ดำเนินไปอย่างราบรื่น