我最近开始在 Scala 和 Play Framework 中工作,并且刚刚将我一直致力于的服务升级到 Play 2.4.3。我的最终目标是创建一个夜间进程,在我的 Play 应用程序中启动一个服务方法,目的是通过方法调用来安排事件,我目前正在使用一个 Actor 调用该方法。
我通过一个覆盖 onStart 的 Global.scala 文件有了这个工作的基本想法,但后来我看到了关于远离 GlobalSettings ( https://www.playframework.com/documentation/2.4.x/GlobalSettings ) 的使用的 Play 文档,并一直试图将其移动到注入(inject)依赖项的方法。
这是我到目前为止拼凑的内容:
模块代码:
import javax.inject._
import com.myOrganization.myPackage.Actors.ScheduleActor
import play.api.libs.concurrent.AkkaGuiceSupport
import play.libs.Akka
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import akka.actor.{ActorRef, ActorSystem}
import scala.concurrent.duration._
import play.Application
import com.google.inject.AbstractModule
@Singleton
class NightlyEvalSchedulerStartup @Inject()(system: ActorSystem, @Named("ScheduleActor") scheduleActor: ActorRef) {
Akka.system.scheduler.schedule(10.seconds, 20.seconds, scheduleActor, "ScheduleActor")
}
class ScheduleModule extends AbstractModule with AkkaGuiceSupport {
def configure() = {
bindActor[ScheduleActor]("ScheduleActor")
bind(classOf[NightlyEvalSchedulerStartup]).asEagerSingleton
}
}
Actor 等级:
import akka.actor.{Actor, Props}
import com.myOrganization.myPackage.services.MySchedulingService
object ScheduleActor {
def props = Props[ScheduleActor]
class updateSchedules
}
class ScheduleActor extends Actor {
val MySchedulingService: MySchedulingService = new MySchedulingService
def receive = {
case "runScheduler" => MySchedulingService.nightlyScheduledUpdate()
}
}
应用程序配置文件
play.modules.enabled += "com.myOrganization.myPackage.modules.ScheduleModule"
该服务正在调用一种主要基于 Scala 逻辑代码和通过 Anorm 进行数据库交互的方法。
每次我尝试使用激活器启动(或运行,一旦收到 Http 请求)启动服务时,我都会收到以下错误:
Oops, cannot start the server.
com.google.inject.CreationException: Unable to create injector, see the following errors:
1) Error injecting constructor, java.lang.RuntimeException: There is no started application
我已经尝试通过用一个简单的 println() 替换 Aka.system.scheduler... 来运行相同的代码,一切似乎都运行良好,这意味着服务启动了,我在控制台上看到了我的消息。所以我猜测 Akka 调度程序缺少一些导致它爆炸的依赖项。你能提供的任何建议都会很棒,我整天都在反对这个。
编辑(每个请求的已解决代码):
模块代码,添加了一些用于粗略估计第二天凌晨 3 点的代码。这可能会改变,但它现在有效:
package com.myOrganization.performanceManagement.modules
import com.myOrganization.performanceManagement.Actors.ScheduleActor
import com.myOrganization.performanceManagement.Actors.ScheduleActor.nightlySchedule
import org.joda.time.{Seconds, LocalDate, LocalTime, LocalDateTime}
import play.api.libs.concurrent.AkkaGuiceSupport
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import akka.actor.{ActorRef, ActorSystem}
import scala.concurrent.duration.{FiniteDuration, SECONDS, HOURS }
import org.joda.time._
import com.google.inject.{Inject, Singleton, AbstractModule}
import com.google.inject.name.Named
class ScheduleModule extends AbstractModule with AkkaGuiceSupport {
override def configure() = {
bindActor[ScheduleActor]("ScheduleActor")
bind(classOf[NightlyEvalSchedulerStartup]).asEagerSingleton()
}
}
@Singleton
class NightlyEvalSchedulerStartup @Inject()(system: ActorSystem, @Named("ScheduleActor") scheduleActor: ActorRef) {
//Calculate initial delay to 3am the next day.
val currentTime: DateTime = DateTime.now
val targetDateTime = currentTime.plusDays(1).withTimeAtStartOfDay()
//Account for Daylight savings to an extent, not mandatory that it starts at 3am, just after midnight.
val initialDelaySeconds = targetDateTime.getHourOfDay match {
case 0 => new Duration(currentTime, targetDateTime.plusHours(3)).getStandardSeconds
case 1 => new Duration(currentTime, targetDateTime.plusHours(2)).getStandardSeconds
}
//Schedule first actor firing to occur at calculated delay and then every 24 hours.
system.scheduler.schedule(FiniteDuration(initialDelaySeconds, SECONDS), FiniteDuration(24, HOURS), scheduleActor, nightlySchedule)
}
Actor :
package com.myOrganization.performanceManagement.Actors
import akka.actor.{ActorSystem, Actor}
import com.google.inject.Inject
import com.myOrganization.performanceManagement.services.PMEvalSchedulingService
object ScheduleActor {
case object nightlySchedule
}
class ScheduleActor @Inject() (actorSystem: ActorSystem) extends Actor {
val pMEvalSchedulingService: PMEvalSchedulingService = new PMEvalSchedulingService
override def receive: Receive = {
case nightlySchedule =>
println("Called the scheduler")
pMEvalSchedulingService.nightlyScheduledEvaluationsUpdate()
}
}
最佳答案
好吧,就我而言,最大的问题最终是在 NightlyEvalSchedulerStartup() 中安排 actor 调用时,我正在调用 Akka.system...这导致系统在应用程序存在之前实例化一个新的 AkkaSystem。通过删除 akk 系统来表示已准备就绪的注入(inject)依赖项。希望这对 future 的人有所帮助!
关于scala - 在系统启动时调度一个重复的 actor Play 2.4.3,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33031333/