【问题标题】:Akka (Scala) - how to properly reset timer / scheduler?Akka (Scala) - 如何正确重置计时器/调度程序?
【发布时间】:2020-10-24 23:38:45
【问题描述】:

我必须填写模板才能在购物车上实现超时。到目前为止,我有:

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) 时,计时器是在任何地方隐式调用,还是刚刚通过?

【问题讨论】:

    标签: scala timer akka scheduler


    【解决方案1】:

    有两个问题。

    首先,在empty 中,您正在启动两个计时器:

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

    更一般地说,您在接收方法中同时使用可变状态 参数。可变状态不是必需的,所以删除这一行:

    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
    }
    

    【讨论】:

    • 谢谢,这教会了我很多!
    猜你喜欢
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多