MuleESB / Akka interview questions
Akka is a toolkit (not a framework) for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala.
Akka is the implementation of the Actor Model on the JVM.
Actors are message driven, stateful building blocks.
- Messages are passed asynchronously.
- Actors may create other actors and form supervision hierarchies.
- Actors are lightweight and won't hold threads.
Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking backpressure.
Akka streams are created on top of Reactive streams so the terminology is different however the workflow/functionality resembles.
Akka is a toolkit, not a framework. This means that you use it together with Spring, Akka for structuring your runtime aspects (concurrency, parallelism, fault-handling) while the other for wiring together components.
One of the OOPS principles, encapsulation, cannot be addressed efficiently without using locks which is expensive.
Sharing memory/data using volatile markers or Atomic wrappers is a costly operation.
Concurrency with work delegation needs to handle service faults.
Common programming practices do not properly address the needs of demanding modern, distributed systems.
Use of actors model allows us to:,
- Enforce encapsulation without resorting to locks.
- Use the model of cooperative entities reacting to signals, changing state, and sending signals to each other to drive the whole application forward.
- Abstracts the execution mechanism that allows retrieving results and resilient.
Actor model resolves the issues with common programming practices by message passing that avoids locking and blocking. Instead of calling methods, actors send messages to each other. Sending a message does not transfer the thread of execution from the sender to the destination. An actor can send a message and continue without blocking. The difference between passing messages and calling methods is that messages have no return value. By sending a message, an actor delegates work to another actor(receiving). The receiving actor delivers the results in a reply message.
Actors execute independently from the senders of a message, and they react to incoming messages sequentially, one at a time. While each actor processes messages sent to it sequentially, different actors work concurrently with each other so that an actor system can process as many messages simultaneously as the hardware will support.
There are no locks used anywhere, and senders are not blocked.
Each Actor will have these component to achive the messaging behavior.
- mailbox, (the queue where messages end up)
- behavior (the state of the actor, internal variables, etc.).
- Messages (pieces of data representing a signal, similar to method calls and their parameters).
- Execution environment (the machinery that takes actors that have messages to react to and invokes their message handling code).
- An address.
When the delegated task on the target actor failed due to an error in the task, service actor will reply to the sender with a message about the error case.
Akka enforces that all actors are organized into a tree-like hierarchy, i.e. an actor that creates another actor becomes the parent of that new actor. When a service itself encounters an internal fault, its parent actor is notified and it can react to the failure.
if the parent actor is stopped due to error conditions, all of its children are recursively stopped, too.
- Actor library, core Akka library that provides a consistent, integrated model that relieves you from individually solving the challenges that arise in concurrent or distributed system design.
- Remoting enables actors that live on different computers to seamlessly exchange messages.
- Cluster maintain a set of actor systems (a cluster) that can communicate with each other and consider each other as part of the cluster.
- Cluster Sharding helps to solve the problem of distributing a set of actors among members of an Akka cluster, by balancing a large set of persistent entities (backed by actors) to members of a cluster.
- Cluster Singleton ensure that only one instance of a service is running in the whole cluster.
- Cluster Publish-Subscribe broadcast messages to an interested set of parties in a cluster.
- Persistence provides patterns to enable actors to persist events that lead to their current state. Upon startup, events can be replayed to restore the state of the entity hosted by the actor.
- Distributed Data accept writes even in the face of cluster partition while share data at the same time ensuring low-latency local read and write access.
- Streams handle streams of events or large datasets with high performance, exploiting concurrency and keeping resource usage tight.
- HTTP expose services of a system or cluster to the external world via an HTTP API in a performant way. Also can stream larget dataset/events.
- Event-driven model: Actors perform work in response to messages. Communication between Actors is asynchronous, allowing Actors to send messages and continue their own work without blocking to wait for a reply.
- Strong isolation principles: There is no sharing of state between Actors; the only way to observe another actor's state is by sending it a message asking for it.
- Location transparency: The system constructs Actors from a factory and returns references to the instances. Because location doesn't matter, Actor instances can start, stop, move, and restart to scale up and down as well as recover from unexpected failures.
- Lightweight.
Yes. Messages must be immutable as it is shared across different threads.
You cannot create an instance of an Actor using the new
keyword. Instead, you create Actor instances using a factory (akka.actor.ActorSystem
) which does not return an actor instance, but a reference, akka.actor.ActorRef
, that points to the actor instance.
The akka.actor.ActorSystem
factory acts as a container for Actors and manages their life-cycles. The actorOf
factory method creates Actors and takes two parameters, a configuration object called Props and a name (Actor name).
An actor in Akka always belongs to a parent. getContext().actorOf()
creates an actor and inject it as a child into an existing tree. Thus the creator actor becomes the parent of the newly created child actor.
All actors have a common parent, the user guardian. Before you create an actor in your code, Akka has already created three actors in the system that supervise every child actor in their path.
- / the so-called root guardian. This is the parent of all actors in the system, and the last one to stop when the system itself is terminated.
- /user the guardian. This is the parent actor for all user created actors. Every actor you create using the Akka library will have the constant path /user/ prepended to it.
- /system the system guardian.
Both create actors however the difference lies where the actors are created in the actor tree hierarchy.
Actors spawned with System.actorOf
will be children of the guardian actor while the actors spawned with context.actorOf
will be children of the context itself - i.e. the actor which invokes the method.
preStart() is invoked after the actor has started but before it processes its first message.
postStop() is invoked just before the actor stops. No messages are processed after this point.
package net.javapedia.akka.example; import akka.actor.AbstractActor; import akka.actor.ActorRef; import akka.actor.ActorSystem; import akka.actor.Props; import akka.event.Logging; import akka.event.LoggingAdapter; public class TestActor extends AbstractActor { private LoggingAdapter log = Logging.getLogger(getContext().getSystem(), this); @Override public Receive createReceive() { //Doesn't handle any message return receiveBuilder().build(); } public static Props props() { Props props = Props.create(TestActor.class, TestActor::new); return props; } @Override public void preStart() throws Exception { log.info("Application started..."); } @Override public void postRestart(Throwable reason) throws Exception { log.info("Application restarted..."); } @Override public void postStop() throws Exception { log.info("Application stopped."); } public static void main(String[] args) { ActorSystem actorSystem = ActorSystem.create("actorSystem"); try { ActorRef actorRef = actorSystem.actorOf(TestActor.props(), "supervison"); } finally { actorSystem.terminate(); } } }
Output:
Task :TestActor.main() [INFO] [09/16/2019 20:53:34.704] [actorSystem-akka.actor.default-dispatcher-2] [akka://actorSystem/user/supervison] Application started... [INFO] [09/16/2019 20:53:34.707] [actorSystem-akka.actor.default-dispatcher-2] [akka://actorSystem/user/supervison] Application stopped.
At-most-once delivery: each message is delivered zero or one time, implies that messages can be lost, but are never duplicated. This is the default behavior which is cheap and produces high performance.
At-least-once delivery: multiple attempts may be made to deliver each message, until at least one succeeds; messages can be duplicated but are never lost. Overhead of keeping the state at the sending end and having an acknowledgment mechanism at the receiving end
Exactly-once delivery: each message is delivered exactly once to the recipient; the message can neither be lost nor be duplicated. This may exhibit worst performance.
Simple and high-level abstractions for distribution, concurrency and parallelism.
Asynchronous, non-blocking and highly performant message-driven programming model.
Very lightweight event-driven processes (several million actors per GB of heap memory).
Yes. ActorSystem is a heavyweight while Actor is light weight. So keep 1 ActorSystem per logical application.
- OneForOneStrategy (default).
- AllForOneStrategy.
Both are configured with a mapping from exception type to supervision directive. The difference is OneForOneStrategy applies directive only to the failed child while AllForOneStraegy applies to all siblings.
The AllForOneStrategy is applicable in cases where the ensemble of children has such tight dependencies among them, that a failure of one child affects the function of the others.
/user is the guardian actor for all user-created top-level actors; actors created using ActorSystem.actorOf are found below this one.
/system is the guardian actor for all system-created top-level actors, e.g. logging listeners or actors automatically deployed by configuration at the start of the actor system.
/deadLetters is the dead letter actor, which is where all messages sent to stopped or non-existing actors are re-routed.
/temp is the guardian for all short-lived system-created actors, e.g. those which are used in the implementation of ActorRef.ask.
/remote is an artificial path below which all actors reside whose supervisors are remote actor references.
No. Actor beans must not have singleton scope when using DI frameworks such as Spring, Guice etc.
Both methods can be used to send messages to the actor.
"tell" method send a message asynchronously and return immediately. The "tell" method works like "fire and forget".
"ask" method is similar to tell method, send messages asynchronously and return Future object which expect a possible reply.
Prefer to "tell" method for performance and use "ask" method only if there is a need.