Akka Actor how to only process the latest message

后端 未结 2 1160
感动是毒
感动是毒 2020-12-30 14:40

Say I am sending messages to an Actor, when it is processing one message several more messages may arise. Now when it is ready to process the next message I want it to only

相关标签:
2条回答
  • 2020-12-30 14:51

    There is no need to implement your own mailbox. At all.

    Removed a lot of text and let this piece of code speak for itself:

    // 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
    }
    

    This means that work will only be performed if there is no newer work in the mailbox.

    0 讨论(0)
  • 2020-12-30 15:16

    You could implement your own mailbox, this approach will not affect your actor implementation. See this answer for solution with changes in actor implementation instead of custom mailbox implementation.

    Implementation of mailbox that drops old messages on 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
        }
    }
    

    Note that I have to add this class into package akka.actor to send old message to dead letters using InternalActorRef like implemented for BoundedQueueBasedMessageQueue.

    If you want to just skip old messages you could implement enqueue like this:

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

    Usage:

    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)
    
    }
    

    Test:

    $ 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
    

    See also akka/Mailboxes.

    0 讨论(0)
提交回复
热议问题