quartz表达式cron_测试Quartz Cron表达式

quartz表达式cron_测试Quartz Cron表达式quartz表达式cron声明复杂的Cron表达式仍然让我有些头疼,尤其是在使用一些更高级的构造时。毕竟,您能否确定以下触发器何时触发’0017L-3W6-9?*”0017L-3W6-9?*’?由于触发器通常打算在将来运行,因此需要事先对其进行测试,并确保它们会在我们认为会触发时真正触发。QuartzScheduler(我正在测试版本2.1.6)不提…

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不仅显示标识递归的图标(请参见行号),而且在采用尾部调用优化时也使用不同的图标(!):

quartz表达式cron_测试Quartz Cron表达式

所以我认为这是最好的,当我想到另一个想法时。 首先,人为的max限制(默认为100)似乎很尴尬。 如果我们可以一次又一次地动态计算所有结果,又为什么还要累积所有结果呢? 当我意识到我不需要SeqList ,我需要一个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()

quartz表达式cron_测试Quartz Cron表达式

今天我们学到了什么?

  • 除非您确实有问题,否则不要对性能进行过多考虑。 力求最佳设计和最简单的实现。 想一想您要用来表示问题和解决方案的数据结构。 在这个(一见钟情)问题中, 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

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注