The implementation principle of actor message communication in Akka (source code parsing)

Source: Internet
Author: User
Tags message queue volatile

Akka send a message to the actor in the following way! Tell means "Fire-and-forget", that is, asynchronously sending a message without waiting for the result to be returned

? Ask asynchronously sends a message and returns a future that represents a possible reply.

Messages are ordered on a per-sender basis. MailBox

The Akka mailbox contains messages destined for the actor. Usually each actor has its own mailbox, but there are exceptions, such as balancingpool all routes will share a mailbox instance.

where MessageQueue (Akka.dispatch.MessageQueue) is one of the heart components that form the Akka mailbox.
The normal message sent to the actor is queued (and then out of the queue) and it needs to support the thread safety of at least n producers and one consumer. It implements the method of queue, out queue, etc.

  Def enqueue (Receiver:actorref, Handle:envelope): Unit
  def dequeue (): Envelope
  def numberofmessages:int
  def Hasmessages:boolean
  def cleanUp (Owner:actorref, deadletters:messagequeue): Unit

Where envelope encapsulates the two members of Message:any and Sender:actorref

Final Case Class Envelope private (Val Message:any, Val sender:actorref)

Systemmessagequeue provides the systemenqueue (into queue) and Systemdrain (all out queue) methods. Mailbox inherits from system Message Queuing Systemmessagequeue and Forkjointask, implements the Runnable interface, and includes Actorcell members and MessageQueue members

Private[akka] Abstract class Mailbox (Val messagequeue:messagequeue)
  extends Forkjointask[unit] with Systemmessagequeue with Runnable {
  var Actor:actorcell = _
  }

Where Forkjointask is an excellent architecture for performing massive, independent tasks with a small number of threads (independent tasks mean no shared data between tasks and tasks, otherwise there is a problem of concurrent access)
Mailbox has represented all methods of MessageQueue. The specific type of MessageQueue is different depending on the mailboxtype. Tell Operation

When Actorsystem is created, the default dispatcher is initialized, and the default Forkjoinpool (Executorservice)
In use Actorref! When message is sent, the Actorcell corresponding SendMessage method is called, which calls the Dispatcher.dispatch method

You can see it in the Actorref.

    Def! (Message:any) (Implicit sender:actorref = Actor.nosender): Unit

In the Actorcell.scala

    Final def sendMessage (Message:any, sender:actorref): Unit =
        sendMessage (Envelope (message, sender, System))

You can then trace the Dispatch.scala file to dungeon.

  def sendMessage (msg:envelope): Unit =
    try {
      val msgtodispatch =
        if (system.settings.SerializeAllMessages) Serializeanddeserialize (msg)
        else msg

      Dispatcher.dispatch (this, msgtodispatch)
    } catch HandleException

The Dispatcher.dispatch in the code can be found in the Dispatch.dispatcher:

     /** * INTERNAL API */Protected[akka] def dispatch (Receiver:actorcell, invocation:envelope): Unit = {val mbox = Receiver.mailbox mbox.enqueue (receiver.self, invocation) registerforexecution (mbox, tr UE, FALSE)} Protected[akka] override Def registerforexecution (Mbox:mailbox, Hasmessagehint:boolean, Hassyste Mmessagehint:boolean): Boolean = {if (mbox.canbescheduledforexecution (Hasmessagehint, Hassystemmessagehint)) {//
             This needs to is here to ensure thread safety and no races if (mbox.setasscheduled ()) {try { 
               Executorservice execute mbox true} catch {case e:rejectedexecutionexception⇒  try {Executorservice execute mbox true} catch {//retry Once case E:rejectedexecutionexception⇒mbox.setasidle () eventst Ream.publish (Error (E, GETclass.getname, GetClass, "Registerforexecution was rejected twice!"))
 Throw e}}} else false} else false}

The

Dispatch method does two things:
One is to put the message in the Actorcell message queue (Maibox is a member variable of Actorcell)
Two is to invoke the dispather underlying thread pool executor execute Mbox executes Mbox.run () (mailbox inherits the
Runnable interface so it can be executed in executorservice),

  Override final Def run (): Unit = {try {if (!isclosed) {//volatile read, needed here Processallsyste Mmessages ()//first, deal with any system messages ProcessMailbox ()//then deal with messages}} Finall

  y {setasidle ()//volatile write, needed here Dispatcher.registerforexecution (this, false, False)}} /** * Process The messages in the mailbox * * @tailrec private final def processmailbox (left:i NT = Java.lang.Math.max (Dispatcher.throughput, 1), Deadlinens:long = if (dispatcher.isthroughputdeadlinetimedefine
        D = = true) System.nanotime + Dispatcher.throughputDeadlineTime.toNanos else 0L): Unit = if (shouldprocessmessage) { Val next = Dequeue () if (next NE null) {if (mailbox.debug) println (actor.self + "Processing me Ssage "+ Next" actor invoke next if (thread.interrupted ()) throw new Interruptedexception ("Interrupted while procEssing actor Messages ") Processallsystemmessages () if ((Left > 1) && ((Dispatcher.isthrough putdeadlinetimedefined = = False) | |
 (System.nanotime-deadlinens) < 0) ProcessMailbox (left-1, Deadlinens)}}

Execute Mbox.run (), the system message is first processed from the SystemMessage list, and the user message is processed from the MessageQueue member.
When processing a user message, run is a recursive function, each call processes a message, and the
processing logic is implemented by invoking the Invoke method of Actorcell, which determines how many messages are processed according to the throughput of dispatcher
,
Depending on how long the throughputdeadlinetime of dispatcher is processed, the
length and time are checked once after processing a message.

  Final def invoke (messagehandle:envelope): Unit = {
    val influencereceivetimeout =! Messagehandle.message.isinstanceof[notinfluencereceivetimeout]
    try {
      currentmessage = MessageHandle
      if (influencereceivetimeout)
        cancelreceivetimeout ()
      messagehandle.message match {case
        msg: Autoreceivedmessage⇒autoreceivemessage (messagehandle) Case
        msg                      ⇒receivemessage (msg)
      }
      Currentmessage = NULL/reset current message after successful invocation
    } catch Handlenonfatalorinterruptedexcept Ion {e⇒
      handleinvokefailure (Nil, E)
    } finally {
      if (influencereceivetimeout)
        Checkreceivetimeout//Reschedule receive timeout
    }

 final def receiveMessage (msg:any): Unit = Actor.aroundreceive (Behaviorstack.head, msg)

For Poisonkill, Terminate system messages are processed in Autoreceivemessage,
The processing of ordinary messages in ReceiveMessage,

private var behaviorstack:list[actor.receive] = Emptybehaviorstack

Can see Behaviorstack is a list[actor.receive],

Type Receive = Partialfunction[any, Unit]

Where the receive (Partialfunction[any, Unit]) function is the processing logic we write to the message.
Because the Actor supports switching patterns through become/unbecome,
So Behaviorstack.head is the current receive processing logic.

For Forkjoinpool This executor, each time you execute execute (mbox),
a mailboxexecutiontask that
inherits from Forkjointask is created first. Where the Exec method calls the Mbox.run method, a Forkjointask object is created each time the execution occurs.

Also, Message Queuing is placed in the actor's corresponding mailbox (the message itself and sender are encapsulated in the form of envelope),
The executed task object is placed in the work queue for each thread of executor, and the task and message each use a different queue.

Reference
Https://doc.akka.io/docs/akka/snapshot/mailboxes.html
Https://doc.akka.io/docs/akka/snapshot/actors.html#send-messages
http://spartan1.iteye.com/blog/1641322

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.