我有一个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
。
答案 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;