quartz表达式cron
声明复杂的Cron表达式仍然让我有些头疼,尤其是在使用一些更高级的构造时。 毕竟,您能否确定以下触发器何时触发'0 0 17 L-3W 6-9 ? *'
'0 0 17 L-3W 6-9 ? *'
? 由于触发器通常打算在将来运行,因此需要事先对其进行测试,并确保它们会在我们认为会触发时真正触发。
Quartz Scheduler (我正在测试版本2.1.6)不提供对此的直接支持,但是基于现有的API(即CronExpression.getNextValidTimeAfter()
方法CronExpression.getNextValidTimeAfter()
一些简单的函数很容易。 我们的目标是定义一个方法,该方法将为给定的Cron表达式返回下一个N
计划的执行。 我们无法要求全部,因为某些触发器(包括上述触发器)没有结束日期,可以无限重复。 我们只能依靠前面提到的getNextValidTimeAfter()
,该方法将日期作为参数并返回该日期之后的最近点火时间T 1
。 因此,如果要查找第二个计划执行,则必须询问第一个执行之后的下一个执行( T 1
)。 等等。 让我们将其放入代码中:
def findTriggerTimesIterative(expr: CronExpression, from: Date = new Date, max: Int = 100): Seq[Date] = {
val times = mutable.Buffer[Date]()
var next = expr getNextValidTimeAfter from
while (next != null && times.size < max) {
times += next
next = expr getNextValidTimeAfter next
}
times
}
如果没有下一次触发时间(例如,假设触发器仅在2012年运行,而我们询问2013年1月1日之后的触发时间),则返回null
。 一点崩溃测试:
findTriggerTimesRecursive(new CronExpression('0 0 17 L-3W 6-9 ? *')) foreach println
产量:
Thu Jun 27 17:00:00 CEST 2013
Mon Jul 29 17:00:00 CEST 2013
Wed Aug 28 17:00:00 CEST 2013
Fri Sep 27 17:00:00 CEST 2013
Fri Jun 27 17:00:00 CEST 2014
Mon Jul 28 17:00:00 CEST 2014
Thu Aug 28 17:00:00 CEST 2014
Fri Sep 26 17:00:00 CEST 2014
Fri Jun 26 17:00:00 CEST 2015
Tue Jul 28 17:00:00 CEST 2015
Fri Aug 28 17:00:00 CEST 2015
Mon Sep 28 17:00:00 CEST 2015
Mon Jun 27 17:00:00 CEST 2016
...
希望我们复杂的Cron表达式的含义现在更清楚: 六月至九月( 6-9
)的月份结束( L-3
)前三天的最近一周( W
)在17:00:00( 0 0 17
) 。 现在,我开始尝试不同的实现方式,以找到最优雅,最适合此相当简单的问题的方法。 首先,我注意到问题不是迭代的,而是递归的:找到下一个100次执行时间等同于找到第一个执行并在第一个执行之后找到99个剩余执行:
def findTriggerTimesRecursive(expr: CronExpression, from: Date = new Date, max: Int = 100): List[Date] =
expr getNextValidTimeAfter from match {
case null => Nil
case next =>
if (max > 0)
next :: findTriggerTimesRecursive(expr, next, max - 1)
else
Nil
}
似乎实现起来要简单得多:没有匹配项–返回空列表( Nil
)。 找到匹配项-除非我们已经收集了足够的日期,否则将其返回到下一个匹配项。 但是,此实现存在一个问题,它不是尾递归的 。 通常,可以通过引入第二个函数并在参数中累加中间结果来更改此设置:
def findTriggerTimesTailRecursive(expr: CronExpression, from: Date = new Date, max: Int = 100) = {
@tailrec def accum(curFrom: Date, curMax: Int, acc: List[Date]): List[Date] = {
expr getNextValidTimeAfter curFrom match {
case null => acc
case next =>
if (curMax > 0)
accum(next, curMax - 1, next :: acc)
else
acc
}
}
accum(from, max, Nil)
}
稍微复杂一点,但是至少StackOverflowError
不会在深夜唤醒我们。 顺便说一句,我刚刚注意到IntelliJ IDEA不仅显示标识递归的图标(请参见行号),而且在采用尾部调用优化时也使用不同的图标(!):
所以我认为这是最好的,当我想到另一个想法时。 首先,人为的max
限制(默认为100)似乎很尴尬。 如果我们可以一次又一次地动态计算所有结果,又为什么还要累积所有结果呢? 当我意识到我不需要Seq
或List
,我需要一个Iterator[Date]
!
class TimeIterator(expr: CronExpression, from: Date = new Date) extends Iterator[Date] {
private var cur = expr getNextValidTimeAfter from
def hasNext = cur != null
def next() = if (hasNext) {
val toReturn = cur
cur = expr getNextValidTimeAfter cur
toReturn
} else {
throw new NoSuchElementException
}
}
我已经做了一些尝试,将if
true分支toReturn
为单行代码,并避免使用中间的toReturn
变量。 这是可能的,但是为了清楚起见(并且不遗余力),我不会透露它* 。 但是,为什么迭代器不那么灵活且使用起来却令人愉悦呢? 好吧,首先,它使我们可以延迟生成下一个触发时间,因此我们无需为不使用的东西付费。 此外,中间结果不会存储在任何地方,因此我们也可以节省内存。 而且由于适用于序列的所有内容也适用于迭代器,因此我们可以轻松地在Scala中使用迭代器,例如打印( 获取 )前10个日期:
new TimeIterator(expr) take 10 foreach println
尝试比较不同的实现方式做一点基准测试(在这里使用caliper ):
object FindTriggerTimesBenchmark extends App {
Runner.main(classOf[FindTriggerTimesBenchmark], Array('--trials', '1'))
}
class FindTriggerTimesBenchmark extends SimpleBenchmark {
val expr = new CronExpression('0 0 17 L-3W 6-9 ? *')
def timeIterative(reps: Int) {
for (i <- 1 to reps) {
findTriggerTimesIterative(expr)
}
}
def timeRecursive(reps: Int) {
for (i <- 1 to reps) {
findTriggerTimesRecursive(expr)
}
}
def timeTailRecursive(reps: Int) {
for (i <- 1 to reps) {
findTriggerTimesTailRecursive(expr)
}
}
def timeUsedIterator(reps: Int) {
for (i <- 1 to reps) {
(new TimeIterator(expr) take 100).toList
}
}
def timeNotUsedIterator(reps: Int) {
for (i <- 1 to reps) {
new TimeIterator(expr)
}
}
}
似乎实现更改对时间的影响可以忽略不计,因为大多数CPU大概都在内部烧毁了。
getNextValidTimeAfter()
。
今天我们学到了什么?
- 除非您确实有问题,否则不要对性能进行过多考虑。 力求最佳设计和最简单的实现。 想一想您要用来表示问题和解决方案的数据结构。 在这个(一见钟情)问题中,
Iterator
(懒惰地评估,可能是无限的项目流)被证明是最好的方法
*好的,这是方法。 提示:分配具有Unit
类型,这里涉及(Date, Unit)
元组:
def next() = if (hasNext)
(cur, cur = expr getNextValidTimeAfter cur)._1
else
throw new NoSuchElementException
参考:在Java和社区博客上,从我们的JCG合作伙伴 Tomasz Nurkiewicz 测试Quartz Cron表达式 。
翻译自: https://www.javacodegeeks.com/2012/10/testing-quartz-cron-expressions.html
quartz表达式cron
今天的文章quartz表达式cron_测试Quartz Cron表达式分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/31666.html