07/09/2018, 09:30

Scheduler with Play

Trong các dự án có thể gặp những ví dụ lập lịch điển hình như : tự động gửi mail vào một thời gian nhất định hàng ngày cho khách hàng, tự động bật một popup nhắc nhở báo cáo cho nhân viên trước khi về,... Bài viết này mình sử dụng ngôn ngữ Scala với Play Framework version 2.x và dùng IDE IntelIj ...

Trong các dự án có thể gặp những ví dụ lập lịch điển hình như : tự động gửi mail vào một thời gian nhất định hàng ngày cho khách hàng, tự động bật một popup nhắc nhở báo cáo cho nhân viên trước khi về,...

Bài viết này mình sử dụng ngôn ngữ Scala với Play Framework version 2.x và dùng IDE IntelIj để tạo project. Trong ví dụ đơn giản này mình thiết lập là lúc 19h00 thì module mình muốn thực thi một nhiệm vụ sẽ được khởi chạy, và chu kỳ của nó là cứ 2 phút thì sẽ chạy lại nhiệm vụ đó. (Chu kỳ thực hiện bạn có thể tùy chỉnh trong class RecurrentTask). Phụ thuộc vào công việc bạn cần làm thì bạn tùy biến lại trong class SchedulerActor. Dưới đây là các bước thực hiện:

Bước 1 : Bạn tạo project Play, trong cấu trúc project vừa tạo ra bạn quan tâm đến các thư mục con gồm : app, conf, và file build.sbt

Bước 2 : Lập lịch Trong thư mục app, tạo ra package mới (đặt tên tùy theo ý bạn), mình đặt tên là services. Sau đó tạo ra hai class mới

  • Class thứ nhất là nội dung công việc bạn muốn thực thi
package services

import org.quartz.{Job, JobExecutionContext}

class SchedulerActor extends Job {
  override def execute(context: JobExecutionContext): Unit = {
    /*
     do something

    */
    println("My job executed.")
  }
}

  • Class thứ hai thiết lập lịch công việc bạn muốn thi hành vào thời gian nào
package services

import java.time.{LocalDateTime, ZoneId}
import java.util.{Calendar, Date}
import javax.inject.{Inject, Singleton}
t akka.actor.ActorSystem
import org.quartz.impl.StdSchedulerFactory
import org.quartz.{JobBuilder, TriggerBuilder}
import play.api.Logger
import play.api.inject.ApplicationLifecycle
import org.quartz.SimpleScheduleBuilder._

@Singleton
class RecurrentTask @Inject()(actorSystem: ActorSystem, lifecycle: ApplicationLifecycle) {
  try {
    Logger.info("------start RecurrentTask---at: "+Calendar.getInstance.getTime)
    var startDateTime: LocalDateTime = LocalDateTime.now()
    startDateTime = startDateTime.withHour(19).withMinute(0).withSecond(0)withNano(0)
    
    val startTime = Date.from(startDateTime.atZone(ZoneId.systemDefault()).toInstant)
    val myJob = JobBuilder.newJob(classOf[SchedulerActor]).withIdentity("myTask", "myGroup").build()
    val trigger = TriggerBuilder.newTrigger()
      .withIdentity("trigger", "group")
      .startAt(startTime)
      .withSchedule(simpleSchedule().withIntervalInMinutes(2).repeatForever())
      .build()
    
    val schedule = new StdSchedulerFactory().getScheduler
    schedule.start()
    schedule.scheduleJob(myJob, trigger)
  } catch {
    case e: Exception =>
      Logger.info("RecurrentTask--------------error", e)
  }
}


Bước 3 : Khai báo công việc trong một module, để module đó được tự động chạy khi ứng dụng chạy. Trong thư mục app tạo mới file class SchedulerModule.scala

import com.google.inject.AbstractModule
import play.api.Logger
import services.RecurrentTask

class SchedulerModule extends AbstractModule{
 override def configure(): Unit = {
   Logger.info("Module has started")
   bind(classOf[RecurrentTask]).asEagerSingleton()
 }
}


Bước 4 : Cấu hình file build.sbt Thêm dòng "org.quartz-scheduler" % "quartz" % "2.1.7" vào file build.sbt như bên dưới

name := "SchedulerExample"

version := "1.0"

lazy val schedulerexample = (project in file(".")).enablePlugins(PlayScala)

scalaVersion := "2.11.6"

libraryDependencies ++= Seq( jdbc , cache , ws   , specs2 % Test,
 "org.quartz-scheduler" % "quartz" % "2.1.7"
)

resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"


Bước 5 : Trong thư mục conf, cập nhập file application.conf để module vừa khai báo ở bước 3 được khởi chạy khi ứng dụng chạy. Thêm dòng dưới đây vào file application.conf

play.modules.enabled += "SchedulerModule"

Khi chạy chương trình sẽ được kết quả như hình dưới đây.

Bài viết này mình xin chia sẻ lại những gì mình đã tìm hiểu, còn một cách khác không dùng cách config qua module (các bạn có thể tìm hiểu thêm cách config thông qua một class kế thừa từ class GlobalSettings)

0