假设我有一个允许在DB中更新某些日期的方法:
def updateLastConsultationDate(userId: String): Unit = ???
如何轻松地对该方法进行限制/去抖动,以使其每小时每小时运行不超过一次。
我想要最简单的解决方案,而不是基于任何事件总线,actor lib或持久层。我想要一个内存解决方案(我知道风险)。
我已经看到了基于Akka Throttler的Scala限制解决方案,但这真的让我觉得开始使用actor只是为了限制方法调用。是不是有一种非常简单的方法呢?
编辑:因为它似乎不够清晰,这里是我想要的visual representation,用JS实现。正如您所看到的,限制可能不仅仅是过滤后续调用,还可以推迟调用(在js / lodash / underscore中也称为trailing events
)。我正在寻找的解决方案不能仅基于纯同步代码。
答案 0 :(得分:5)
对于基于ReactiveX的解决方案来说,这听起来很棒。在Scala上,Monix是我最喜欢的。以下是说明它的Ammonite REPL会话:
import $ivy.`io.monix::monix:2.1.0` // I'm using Ammonite's magic imports, it's equivalent to adding "io.monix" %% "monix" % "2.1.0" into your libraryImports in SBT
import scala.concurrent.duration.DurationInt
import monix.reactive.subjects.ConcurrentSubject
import monix.reactive.Consumer
import monix.execution.Scheduler.Implicits.global
import monix.eval.Task
class DbUpdater {
val publish = ConcurrentSubject.publish[String]
val throttled = publish.throttleFirst(1 hour)
val cancelHandle = throttled.consumeWith(
Consumer.foreach(userId =>
println(s"update your database with $userId here")))
.runAsync
def updateLastConsultationDate(userId: String): Unit = {
publish.onNext(userId)
}
def stop(): Unit = cancelHandle.cancel()
}
是的,使用Scala.js,如果这对您来说很重要,那么此代码也适用于浏览器。
答案 1 :(得分:1)
由于您要求最简单的解决方案,您可以存储val lastUpdateByUser: Map[String, Long]
,在允许更新之前您会参考
if (lastUpdateByUser.getOrElse(userName, 0)+60*60*1000 < System.currentTimeMillis) updateLastConsultationDate(...)
并在用户实际执行更新时更新
lastUpdateByUser(userName) = System.currentTimeMillis
答案 2 :(得分:0)
节流的一种方法是在redis实例中维护一个计数。这样做可以确保数据库不会被更新,无论您运行多少个scala进程,因为状态存储在进程外部。