Akka(Scala)-如何正确重置计时器/调度器?

de90aj5v  于 2022-11-06  发布在  Scala
关注(0)|答案(1)|浏览(207)

我必须填写模板来实现购物车超时。到目前为止我有:

import akka.actor.{Actor, ActorRef, Cancellable, Props, Timers}
import akka.event.{Logging, LoggingReceive}

import scala.concurrent.duration._
import scala.language.postfixOps

object CartActor {
  sealed trait Command
  case class AddItem(item: Any) extends Command
  case class RemoveItem(item: Any) extends Command
  case object ExpireCart extends Command
  case object StartCheckout extends Command
  case object ConfirmCheckoutCancelled extends Command
  case object ConfirmCheckoutClosed extends Command

  sealed trait Event
  case class CheckoutStarted(checkoutRef: ActorRef) extends Event

  def props = Props(new CartActor())
}

class CartActor extends Actor with Timers {
  import context._
  import CartActor._

  private val log = Logging(context.system, this)
  val cartTimerDuration: FiniteDuration = 5 seconds

  var cart: Cart = Cart.empty

  private def scheduleTimer: Cancellable =
    system.scheduler.scheduleOnce(cartTimerDuration, self, ExpireCart)

  def receive: Receive = empty

  def empty: Receive = {
    case AddItem(item) =>
      this.cart = cart.addItem(item)
      scheduleTimer
      context become nonEmpty(cart, scheduleTimer)
    case _ =>
  }

  def nonEmpty(cart: Cart, timer: Cancellable): Receive = {
    case AddItem(item) =>
      this.cart = cart.addItem(item)
      timer.cancel()
      scheduleTimer
    case RemoveItem(item) =>
      this.cart = this.cart.removeItem(item)
      if (this.cart.size != 0) {
        timer.cancel()
        scheduleTimer
      }
      else
        context become empty
    case StartCheckout =>
      context become inCheckout(this.cart)
    case ExpireCart =>
      this.cart = Cart.empty
      println("Cart expired")
      context become empty
  }

  def inCheckout(cart: Cart): Receive = {
    case ConfirmCheckoutCancelled =>
      context become nonEmpty(cart, scheduleTimer)
    case ConfirmCheckoutClosed =>
      println("Cart closed after checkout")
      context become empty
    case _ =>
  }
}

提供了方法签名,因此,例如,我无法更改def nonEmpty(cart: Cart, timer: Cancellable)。在添加或删除项目时,计时器应该重置,因此用户有5秒时间来做一些事情。问题是,我不知道如何正确地做到这一点-上面的方法显然没有重置计时器,因为它总是在5秒后超时。我如何才能做到这一点?我应该使用计时器而不是调度程序吗?例如timers.startSingleTimer("ExpireCart", ExpireCart, cartTimerDuration)?我应该如何在方法之间传递它?计时器应该是CartActor的属性,而我应该忽略调度程序吗?顺便说一句,当我有def nonEmpty(cart: Cart, timer: Cancellable)时,计时器是在任何地方隐式调用,还是直接传递?

lg40wkob

lg40wkob1#

有两个问题。
首先,在empty中启动两个计时器:

scheduleTimer // Creates a timer, reference lost so can't be cancelled
  context become nonEmpty(cart, scheduleTimer) // Creates a second timer

更一般地说,您在receive方法中同时使用了mutable state * 和 * 参数。mutable state不是必需的,因此请删除以下行:

var cart: Cart = Cart.empty

现在修复nonEmpty,将更新后的状态传递给新的receive方法,而不是使用this

def nonEmpty(cart: Cart, timer: Cancellable): Receive = {
  case AddItem(item) =>
    timer.cancel()
    context become nonEmpty(cart.addItem(item), scheduleTimer)

  case RemoveItem(item) =>
    timer.cancel()
    if (this.cart.size > 1) {
      context become nonEmpty(cart.remoteItem(item), scheduleTimer)
    } else {
      context become empty
    }
  case StartCheckout =>
    timer.cancel()
    context become inCheckout(cart)
  case ExpireCart =>
    timer.cancel() // Just in case :)
    println("Cart expired")
    context become empty
}

相关问题