如何确保在某个特定时刻只有一个Play实例实际运行方法

时间:2015-08-15 20:44:57

标签: scala playframework

我有一个Play插件,每15分钟执行一次方法,如下所示:

import scala.concurrent.Future
import scala.concurrent.duration._
import play.api.libs.concurrent.Akka
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.{Application, Plugin}
import akka.actor.Cancellable

class AuthPlugin(app: Application) extends Plugin {

  private var cancellableDoSomething: Option[Cancellable] = None

  ...

  override def onStart = {
    cancellableDoSomething = Some(
      Akka.system.scheduler.scheduleOnce(0.seconds) {
        doSomething.foreach { _ =>
          Akka.system.scheduler.scheduleOnce(15.minutes)(doSomething)
        }
      }
    )
  }

  override def onStop = {
    cancellableDoSomething.foreach(_.cancel)
  }
}

object AuthPlugin {

  ...

  def doSomething: Future[Unit] = {
    // Access to shared DB here...
  }
}

假设我在两个不同的主机和cource上运行了Play个应用程序的两个实例,每个主机每15分钟调用一次doSomething

如何确保在某个特定时刻只有一个实例实际运行doSomething

修改

两个Play实例访问同一个数据库,doSomething处理状态pending中的记录。处理完状态pending的记录后,会将其更新为状态processed

如果第一个Play实例在数据库中查询待处理订单并在处理它们时第二个Play实例运行相同的查询,则它会获得一个记录集,该记录集还包含已由第一个Play实例但尚未处理。为了避免这种情况,当且仅当没有其他实例已经在执行时,Play实例应该能够调用doSomething

我可以实现一种基于数据库的信号量,其中获取它的第一个Play实例将字段的值设置为1或其他任何...但此解决方案不稳定,因为如果Play实例失败并且从未将字段设置为0,则其他Play实例将无法再获取信号并调用doSomething

1 个答案:

答案 0 :(得分:0)

使用为某些操作提供锁定机制的数据库,例如在UPDATE期间
示例:MySQL

  

" MySQL对InnoDB表使用行级锁定"

     

" MySQL对MyISAM,MEMORY和MERGE表使用表级锁定"


这意味着您可以使用上述任何表格获得锁定机制。


锁定程序

1.找到你的机器想要工作的工作。我们假设您使用jobId=123获得一个
2.尝试原子锁定。

UPDATE jobs SET state='started' and worker='worker1' WHERE jobId=123 AND state='pending';


3.检查获取的锁是否由机器'worker1'获得。

SELECT COUNT(*) FROM JOBS where jobId=123 AND state='started' AND worker='worker1';


4.如果上述查询返回1,则'worker1'已成功保留作业,排除所有其他计算机。如果查询返回0,则表示某个其他计算机(例如'worker2')首先获得了锁。

作业完成后

UPDATE jobs SET state='finished' WHERE jobId=123;<br>


如果预订机器出现故障怎么办?

尝试锁定(通过UPDATE)时,还要添加时间戳列。然后,让另一个后台工作定期清除比1小时更长的工作,但是状态仍然是“#34;”#34;