【问题标题】:Akka Actor how to only process the latest messageAkka Actor 如何只处理最新消息
【发布时间】:2014-01-30 11:48:34
【问题描述】:

假设我正在向一个 Actor 发送消息,当它正在处理一条消息时,可能会出现更多消息。现在,当它准备好处理下一条消息时,我希望它只处理最新消息,因为以前的消息已经过时。我怎样才能最好地做到这一点?

使用 scala Actors 库,我可以通过首先从我的发件人处进行如下检查来实现此目的:

if (myActor.getState != Runnable)
  myActor ! message     

但是我觉得我在Akka系统中做不到这样的测试

【问题讨论】:

  • 您试图确保哪些消息处理保证?
  • 我相信 Akka 也有优先收件箱。这可能会做你想要的,但这会导致问题:如何处理旧消息?因此,您可以按照建议使用自定义收件箱,或者让您的参与者存储处理的最后一条消息的时间戳(必须存储在消息中),然后删除之前的所有消息。

标签: scala akka


【解决方案1】:

无需实现自己的邮箱。完全没有。

删除了很多文字,让这段代码自己说话:

// Either implement "equals" so that every job is unique (by default) or do another comparison in the match.
class Work 
case class DoWork(work: Work)

class WorkerActor extends Actor {
  // Left as an exercise for the reader, it clearly should do some work.
  def perform(work: Work): Unit = ()

  def lookingForWork: Receive = {
    case w: Work =>
      self forward DoWork(w)
      context become prepareToDoWork(w)
  }

  def prepareToDoWork(work: Work): Receive = {
    case DoWork(`work`) =>
      // No new work, so perform this one
      perform(work)
      // Now we're ready to look for new work
      context become lookingForWork
    case DoWork(_) =>
      // Discard work that we don't need to do anymore
    case w2: Work =>
      // Prepare to do this newer work instead
      context become prepareToDoWork(w2) 
  }

  //We start out as looking for work
  def receive = lookingForWork
}

这意味着只有在邮箱中没有更新的工作时才会执行工作。

【讨论】:

  • 这是个好主意,但在你的实现中存在一个错误:你不应该在work平等上中继:假设我们的邮箱中有2个works:a from s1和@ 987654325@ => a from s2, DoWork(a) from s1 => DoWork(a) from s1, DoWork(a) from s2。所以我们将处理a from s1 而不是a from s2。因此,您应该删除有关发件人的信息或修复您的实施。如果{a from s1b from s1a from s2},这可能很重要。
  • 是的,我假设实施 equals 的人考虑到工作项是唯一的。这是除了一般的点之外。我将添加免责声明。
  • 我是不是弄错了,或者你不需要在成为新的prepareToDoWork之前自己转发DoWork(w2)吗? “留给读者作为练习”......你没有在 LTH 学过数学吗?
【解决方案2】:

您可以实现自己的邮箱,这种方法不会影响您的actor实现。请参阅this answer 以获取更改参与者实现而不是自定义邮箱实现的解决方案。

enqueue 上丢弃旧邮件的邮箱的实现:

package akka.actor.test 

import akka.actor.{ ActorRef, ActorSystem }
import com.typesafe.config.Config
import akka.dispatch.{Envelope, MessageQueue}

class SingleMessageMailbox extends akka.dispatch.MailboxType {

  // This constructor signature must exist, it will be called by Akka
  def this(settings: ActorSystem.Settings, config: Config) = this()

  // The create method is called to create the MessageQueue
  final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
    new MessageQueue {
      val message = new java.util.concurrent.atomic.AtomicReference[Envelope]

      final def cleanUp(owner: ActorRef, deadLetters: MessageQueue): Unit =
        Option(message.get) foreach {deadLetters.enqueue(owner, _)}

      def enqueue(receiver: ActorRef, handle: Envelope): Unit =
        for {e <- Option(message.getAndSet(handle))} 
          receiver.asInstanceOf[InternalActorRef].
            provider.deadLetters.
            tell(DeadLetter(e.message, e.sender, receiver), e.sender)

      def dequeue(): Envelope = message.getAndSet(null)

      def numberOfMessages: Int = Option(message.get).size

      def hasMessages: Boolean = message.get != null
    }
}

请注意,我必须将此类添加到包 akka.actor 中才能使用 InternalActorRef 将旧消息发送到死信,例如 implemented for BoundedQueueBasedMessageQueue

如果您只想跳过旧消息,您可以像这样实现enqueue

def enqueue(receiver: ActorRef, handle: Envelope): Unit = message.set(handle)

用法:

object Test extends App {
  import akka.actor._
  import com.typesafe.config.ConfigFactory

  // you should use your config file instead of ConfigFactory.parseString
  val actorSystem: ActorSystem =
    ActorSystem("default", ConfigFactory.parseString(
"""
  akka.daemonic=on
  myMailbox.mailbox-type = "akka.actor.test.SingleMessageMailbox"
"""))

  class EchoActor extends Actor {
    def receive = {
      case m => println(m); Thread.sleep(500)
    }
  }

  val actor = actorSystem.actorOf(Props[EchoActor].withMailbox("myMailbox"))

  for {i <- 1 to 10} {
    actor ! i
    Thread.sleep(100)
  }

  Thread.sleep(1000)

}

测试:

$ sbt run
1
[INFO] <dead letters log>
[INFO] <dead letters log>
[INFO] <dead letters log>
5
[INFO] <dead letters log>
[INFO] <dead letters log>
[INFO] <dead letters log>
[INFO] <dead letters log>
10

另见akka/Mailboxes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 1970-01-01
    • 1970-01-01
    • 2014-06-22
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    相关资源
    最近更新 更多