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