<個人備忘錄> quartz在Play中的簡單使用

###1.簡單介紹html

Quartz的功能十分強大,我這裏只是簡單記錄我在項目中使用的過程,便於之後查缺補漏. Quartz的基本知識請看這裏Quartz教程.因爲我使用的版本爲最新版(Version:2.2.2),所以和此篇文章的方法實現仍是有小部分差別.java

###2.需求說明redis

因爲我須要實現一個定時任務,須要隨意的更改定時時間或者取消定時任務.而且在程序崩潰重啓後可以從新運行那些還未結束的定時任務. Akka自帶的Scheduler並不能徹底知足需求.同時QuartzjobName式的key->value異步實現方式配合PlayFrameWork來使用可謂事半功倍.api

Quartz的持久化存儲,還在研究....異步

本次的key值我暫時存儲在redis中,每次新增一個定時任務將jobName 放在redis 中,完成定時任務移除redis中的jobName.系統重啓前從redis中分別取出未完成的JobName 進行從新計算.ide

###3.功能實現工具

  1. 導入sbt文件
libraryDependencies += "org.quartz-scheduler" % "quartz" % "2.2.2"

2.QuartzManager工具類測試

package utils

/**
  * Created by jiang on 16/1/18.
  */

import models.basic._
import org.quartz.DateBuilder._
import org.quartz._
import org.quartz.impl.StdSchedulerFactory
import org.quartz.JobBuilder._
import org.quartz.CronScheduleBuilder._
import org.quartz.TriggerBuilder._
import java.util.{HashMap, Date}
import play.api.Logger
import scala.collection.JavaConverters._
import org.quartz.impl.matchers.GroupMatcher

/**
  * Created by jiang on 16/1/18.
  */
object QuartzManager {
    val scheduler = new StdSchedulerFactory().getScheduler()
    val jobGroupName = "jobs"
    val triggerGroupName = "triggers"
    // 在Global.onStart是提早初始化運行
    def initialize() = {  
        scheduler.clear()
        scheduler.start()
    }

    def getJobKey(jobName: String): JobKey = {
	    val group = GroupMatcher.groupEquals[JobKey](jobGroupName)
        val keys = scheduler.getJobKeys(group).asScala.filter(_.getName == jobName)
        if (keys.nonEmpty) keys.head else null
    }

    def jobExists(jobName: String): Boolean = {
        getJobKey(jobName) != null
    }
// 新增定時任務,只執行一次.同時將wid存入JobDataMap中.
// stage 參數爲不一樣階段業務需求,存放在redis hash 不一樣的key關鍵字
    def addJob(wid:Long,stage:String,time:Long,jobs:Job) = {
        var ret:Option[Int] = None
        val jobName,triggerName = wid+":"+stage
        val data = new JobDataMap()
        data.put("wid", wid)
        if(jobExists(jobName)) {
            Logger.info(s"$wid :後臺已經有一個定時任務,請先取消再從新設置")
            ret = Option(0)
        }else{
            val job: JobDetail = newJob(jobs.getClass)
                .withIdentity(jobName,jobGroupName)
                .usingJobData(data)
                .build()
            val  trigger =  newTrigger
                .withIdentity(triggerName,triggerGroupName)
                .startAt(new Date(time)) 
                .forJob(jobName, jobGroupName)                 
                .build()
            scheduler.scheduleJob(job, trigger)

            // 將key 值存入redis 中
            val key = "WorkTask:"+stage
            RedisUtils.getJedis.hset(key,wid.toString,time.toString)

            ret = Option(1)
        }
        // O-> 已存在,1->設置成功,none->未知錯誤
        ret

    }

    def deleteJob(wid:Long,stage:String) ={
        val jobName = wid+":"+stage
        scheduler.deleteJob(getJobKey(jobName))
        // redis 中一樣刪除數據
        val key = "WorkTask:"+stage
        RedisUtils.getJedis.hdel(key,wid.toString)

    }

    // 系統忽然down 掉,從新恢復定時任務
    def recoverJob() = {
        lazy val time = System.currentTimeMillis()  // 當前時間,redis 中時間若小於當前時間則當即執行.
        val jedis = RedisUtils.getJedis
        //1.恢復準備中的定時任務
        val readyWidKeyMap = jedis.hgetAll("WorkTask:ready").asScala
        if(readyWidKeyMap.nonEmpty){
            readyWidKeyMap.foreach(p=>{
                val wid:Long = p._1.toLong
                val recoverReadyTime:Long = if(p._2.toLong <= time ) time else p._2.toLong
                addJob(wid,"ready",recoverReadyTime,new readyJob())
            })

        }

        //2. 恢復進行中的定時任務
        val ongoingWidKeyMap = jedis.hgetAll("WorkTask:ongoing").asScala
        if(ongoingWidKeyMap.nonEmpty){
            ongoingWidKeyMap.foreach(p=>{
                val wid:Long = p._1.toLong
                val recoverOnGoingTime:Long = if(p._2.toLong <= time ) time+5000 else p._2.toLong
                addJob(wid,"ongoing",recoverOnGoingTime,new ongoingJob())
            })

        }

        //3.恢復結束定時任務
        val endWidKeyMap = jedis.hgetAll("WorkTask:end").asScala
        if(endWidKeyMap.nonEmpty){
            endWidKeyMap.foreach(p=>{
                val wid:Long = p._1.toLong
                val recoverEndTime:Long = if(p._2.toLong <= time ) time+10000 else p._2.toLong
                addJob(wid,"end",recoverEndTime,new endJob())
            })
        }
    }
}

3.定時任務實現功能ui

class TaskJob extends Job{
    val DateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    val d = new Date()
    val returnStr = DateFormat.format(d)
    override  def execute(jobExecutionContext: JobExecutionContext): Unit ={
        val wid = jobExecutionContext.getJobDetail.getJobDataMap.get("wid")

        println(returnStr +"★★★★★★★★★★★:"+wid)

    }

}

4.測試scala

// 當前時間後的10執行
val startTime = System.currentTimeMillis() + 10000
QuartzManager.addJob(2,"ready",startTime,new TaskJob() )

//10s 後,在控制檯能夠看到
2016-01-19 11:01:53★★★★★★★★★★★:2

總結

我都寫了啥啊?

相關文章
相關標籤/搜索