Scaling out with RPC and scaling up with low-level threading aren't good ideas. RPC pretends that a call over the network is no different from a local method call. Every RPC call needs to block the current thread and wait for a response from the network for the local method call abstraction to work, which can be costly. The database calls are essentially RPC, and almost all standard database drivers use blocking I/O.
Actors are like Message Queues.
Messages are simple data structures that can't be changed after they've been created. They are immutable. Actors can receive messages one at a time and execute some behavior whenevera message is received. Unlike queues, they can also send messages. Everything an actor does is executed asynchronously.
An actor is a lightweight process that has only four core operations: create, send, become, and supervise. An actor can only communicate with another actor by sending it messages. Actors don't allow any access to internal state. Therefore, a mutable data structure can be safe as well.
State machines are a great tool for making sure that a system only executes particular actions when it's in a specific state.
The supervisor gets notified with special messages that indicate which actor has crashed.
Changes Kept as a Sequence of Events
All changes are kept as a sequence of events. The current state can be rebuilt by replaying the events that occurred to the in-memory journal, and the technique is known as Event Sourcing.
Thread only to Scale Up
In JVM, processes are expressed in objects and methods, which are executed on threads.
Threads might be executed on many CPUs in parallel, or using some sharing mechanism like time slicing on one CPU.
Actor System
The first thing every Akka application does is create an ActorSystem. The actor system can create so called top-level actors.
ActorRef can be used to send messages to the actor. ActorPath is used to look up an actor in the actor system. Messages are sent to the actor's ActorRef. Messages sent to the ActorRef will be temporarily stored in the mailbox to be processed later, one at a time, in the order they arrived.
Dispatcher
Actors are invoked at some point by a dispatcher, which pushes the messages in the mailbox through the actors. When you send a message to an actor, all you're really doing is leaving a message behind in its mailbox. Eventually a dispatcher will push it through the actor. Actors are lightweight because they run on top of dispatchers. Akka actors take a lot less space than threads: around 2.7 million actors can fit in 1 GB of memory, compared to 4096 threads.
Testing Actors
You can easily simulate behaviors by sending messages.
TestActorRef allows access to the underlying actor instance. TestProbe class make it possible to receive replies from actors, inspect messages.
It's completely safe to set/update the internalState var, since the Actor is protected from multithread access.
Props(args) takes a by-name creator parameter, which are evaluated when they're referenced for the first time. Creating the Props in the companion object has the benefit that you can't refer to an actor's internals, in the case where you would need to create an actor from an actor. Using something internal to the actor from the Props could lead to race conditions, or it could cause serialization issues if the Props itself were used inside a message that needs to be sent across the network.
object SendingActor { def props(receiver: ActorRef) = Props(new SendingActor(receiver)) case class Event(id: Long) } class SendingActor(receiver: ActorRef) extends Actor{ import SendingActor._ } val props = SendingActor.props(testActor) val sendingActor = system.actorOf(props, "sendingActor")
No comments:
Post a Comment