Home - reyou/Ggg.AkkaNET GitHub Wiki

Install

$ install-package Akka
$ install-package Akka.Remote


If an exception is thrown while a message is being processed (i.e. taken out of its mailbox and handed over to the current behavior), then this message will be lost. It is important to understand that it is not put back on the mailbox. So if you want to retry processing of a message, you need to deal with it yourself by catching the exception and retry your flow. Make sure that you put a bound on the number of retries since you don't want a system to livelock (so consuming a lot of cpu cycles without making progress).

If an exception is thrown while a message is being processed, nothing happens to the mailbox. If the actor is restarted, the same mailbox will be there. So all messages on that mailbox will be there as well.

If code within an actor throws an exception, that actor is suspended and the supervision process is started (see Supervision and Monitoring). Depending on the supervisor’s decision the actor is resumed (as if nothing happened), restarted (wiping out its internal state and starting from scratch) or terminated.

A livelock is similar to a deadlock, except that the states of the processes involved in the livelock constantly change with regard to one another, none progressing. Livelock is a special case of resource starvation; the general definition only states that a specific process is not progressing.

As a real-world example, livelock occurs when two people meet in a narrow corridor, and each tries to be polite by moving aside to let the other pass, but they end up swaying from side to side without making any progress because they always both move the same way at the same time.

https://en.wikibooks.org/wiki/Operating_System_Design/Concurrency/Livelock


Killing an Actor

You can kill an actor by sending a Kill message. This will cause the actor to throw a ActorKilledException, triggering a failure. The actor will suspend operation and its supervisor will be asked how to handle the failure, which may mean resuming the actor, restarting it or terminating it completely. See What Supervision Means for more information.

Use Kill like this:

// kill the 'victim' actor
victim.Tell(Akka.Actor.Kill.Instance, ActorRef.NoSender);

Stopping actors

Termination of an actor proceeds in two steps: first the actor suspends its mailbox processing and sends a stop command to all its children, then it keeps processing the internal termination notifications from its children until the last one is gone, finally terminating itself (invoking PostStop, dumping mailbox, publishing Terminated on the DeathWatch, telling its supervisor). This procedure ensures that actor system sub-trees terminate in an orderly fashion, propagating the stop command to the leaves and collecting their confirmation back to the stopped supervisor. If one of the actors does not respond (i.e. processing a message for extended periods of time and therefore not receiving the stop command), this whole process will be stuck.

public class MyStoppingActor : ReceiveActor
{
    private IActorRef child;

    public MyStoppingActor()
    {
        Receive<string>(s => s.Equals("interrupt-child"), msg =>
        {
            Context.Stop(child);
        });

        Receive<string>(s => s.Equals("done"), msg =>
        {
            Context.Stop(Self);
        });
    }
}

PoisonPill

You can also send an actor the Akka.Actor.PoisonPill message, which will stop the actor when the message is processed. PoisonPill is enqueued as ordinary messages and will be handled after messages that were already queued in the mailbox.

Use it like this:

myActor.Tell(PoisonPill.Instance, Sender);

Reply to messages

If you want to have a handle for replying to a message, you can use Sender, which gives you an IActorRef. You can reply by sending to that IActorRef with Sender.Tell(replyMsg, Self). You can also store the IActorRef for replying later, or passing on to other actors. If there is no sender (a message was sent without an actor or task context) then the sender defaults to a 'dead-letter' actor ref.

Receive<string>(() => 
{
    var result = calculateResult();

    // do not forget the second argument!
    Sender.Tell(result, Self);
})

Unmatched messages

If the actor receives a message for which no handler matches, the unhandled message is published to the EventStream wrapped in an UnhandledMessage. To change this behavior override Unhandled(object message)

protected override void Unhandled(object message)
{
  //Do something with the message.
}

Receive messages

To receive a message you should create a Receive handler in a constructor.

Receive<string>(ms => Console.WriteLine("Received message: " + msg));

Note that Receive<object>(Action<object> handler) behaves the same as ReceiveAny() as it catches all messages. These two are equivalent:

ReceiveAny(o => Console.WriteLine("Received object: " + o);
Receive<object>(0 => Console.WriteLine("Received object: " + o);

Send messages

Messages are sent to an Actor through one of the following methods.

  1. Tell() means fire-and-forget, e.g. send a message asynchronously and return immediately.
  2. Ask() sends a message asynchronously and returns a Future representing a possible reply.

There are performance implications of using Ask since something needs to keep track of when it times out, there needs to be something that bridges a Task into an IActorRef and it also needs to be reachable through remoting. So always prefer Tell for performance, and only Ask if you must.


Actor Lifecycle


Recommended Practices

https://getakka.net/articles/actors/receive-actor-api.html#recommended-practices

It is always preferable to communicate with other Actors using their IActorRef instead of relying upon ActorSelection. Exceptions are: sending messages using the At-Least-Once Delivery facility, initiating first contact with a remote system. In all other cases ActorRefs can be provided during Actor creation or initialization, passing them from parent to child or introducing Actors by sending their ActorRefs to other Actors within messages.

// will look all children to serviceB with names starting with worker
Context.ActorSelection("/user/serviceB/worker*");

// will look up all siblings beneath same supervisor
Context.ActorSelection("../*");

It is a good idea to provide static factory methods on the ReceiveActor which help keeping the creation of suitable Props as close to the actor definition as possible.

Another good practice is to declare local messages (messages that are sent in process) within the Actor, which makes it easier to know what messages are generally being sent over the wire vs in process.:

The name parameter is optional, but you should preferably name your actors, since that is used in log messages and for identifying actors. The name must not be empty or start with $$, but it may contain URL encoded characters (eg. %20 for a blank space). If the given name is already in use by another child to the same parent an InvalidActorNameException is thrown.

Actors are automatically started asynchronously when created.


What Should I Use Dead Letters For?

The main use of this facility is for debugging, especially if an actor send does not arrive consistently (where usually inspecting the dead letters will tell you that the sender or recipient was set wrong somewhere along the way). In order to be useful for this purpose it is good practice to avoid sending to DeadLetters where possible, i.e. run your application with a suitable dead letter logger (see more below) from time to time and clean up the log output. This exercise—like all else—requires judicious (good judgment or sound thinking) application of common sense: it may well be that avoiding to send to a terminated actor complicates the sender's code more than is gained in debug output clarity.


Peer-to-Peer vs. Client-Server

Akka.NET Remoting is a communication module for connecting actor systems in a peer-to-peer fashion, and it is the foundation for Akka Clustering. The design of remoting is driven by two (related) design decisions:

Communication between involved systems is symmetric: if a system A can connect to a system B then system B must also be able to connect to system A independently. The role of the communicating systems are symmetric in regards to connection patterns: there is no system that only accepts connections, and there is no system that only initiates connections. The consequence of these decisions is that it is not possible to safely create pure client-server setups with predefined roles (violates assumption 2). For client-server setups it is better to use Akka I/O.


Top-Level Scopes for Actor Paths

At the root of the path hierarchy resides the root guardian above which all other actors are found; its name is "/". The next level consists of the following:

  1. "/user" is the guardian actor for all user-created top-level actors; actors created using ActorSystem.ActorOf are found below this one.
  2. "/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.
  3. "/deadLetters" is the dead letter actor, which is where all messages sent to stopped or non-existing actors are re-routed (on a best-effort basis: messages may be lost even within the local CLR).
  4. "/temp" is the guardian for all short-lived system-created actors, e.g. those which are used in the implementation of ActorRef.ask.
  5. "/remote" is an artificial path below which all actors reside whose supervisors are remote actor references The need to structure the name space for actors like this arises from a central and very simple design goal: everything in the hierarchy is an actor, and all actors function in the same way. Hence you can not only look up the actors you created, you can also look up the system guardian and send it a message (which it will dutifully discard in this case). This powerful principle means that there are no quirks to remember, it makes the whole system more uniform and consistent.

The Interplay with Remote Deployment

https://getakka.net/articles/concepts/addressing.html#the-interplay-with-remote-deployment


ActorOf only ever creates a new actor, and it creates it as a direct child of the context on which this method is invoked (which may be any actor or actor system).
ActorSelection only ever looks up existing actors when messages are delivered, i.e. does not create actors, or verify existence of actors when the selection is created.


Absolute vs. Relative Paths

You can for example send a message to a specific sibling:

Context.ActorSelection("../brother").Tell(msg);

Absolute paths may of course also be looked up on context in the usual way, i.e.

Context.ActorSelection("/user/serviceA").Tell(msg);

will work as expected.

Context.ActorSelection("../*").Tell(msg);

will send msg to all siblings including the current actor.

Actor Path Anchors

"akka://my-sys/user/service-a/worker1"                   // purely local
"akka.tcp://[email protected]:5678/user/service-b" // remote

Here, akka.tcp is the default remote transport; other transports are pluggable. A remote host using UDP would be accessible by using akka.udp. The interpretation of the host and port part (i.e.serv.example.com:5678 in the example) depends on the transport mechanism used, but it must abide by the URI structural rules.


There are several special types of actor references which behave like local actor references for all practical purposes:

  1. PromiseActorRef is the special representation of a Task for the purpose of being completed by the response from an actor. ICanTell.Ask creates this actor reference.
  2. DeadLetterActorRef is the default implementation of the dead letters service to which Akka routes all messages whose destinations are shut down or non-existent.
  3. EmptyLocalActorRef is what Akka returns when looking up a non-existent local actor path: it is equivalent to a DeadLetterActorRef, but it retains its path so that Akka can send it over the network and compare it to other existing actor references for that path, some of which might have been obtained before the actor died.

Actor References, Paths and Addresses


Actor Best Practices

https://getakka.net/articles/concepts/actor-systems.html#actor-best-practices

  1. Actors should be like nice co-workers: do their job efficiently without bothering everyone else needlessly and avoid hogging resources. Translated to programming this means to process events and generate responses (or more requests) in an event-driven manner. Actors should not block (i.e. passively wait while occupying a Thread) on some external entity—which might be a lock, a network socket, etc.—unless it is unavoidable; in the latter case see below.
  2. Do not pass mutable objects between actors. In order to ensure that, prefer immutable messages. If the encapsulation of actors is broken by exposing their mutable state to the outside, you are back in normal .NET concurrency land with all the drawbacks.
  3. Actors are made to be containers for behavior and state, embracing this means to not routinely send behavior within messages. One of the risks is to accidentally share mutable state between actors, and this violation of the actor model unfortunately breaks all the properties which make programming in actors such a nice experience.
  4. Top-level actors are the innermost part of your Error Kernel, so create them sparingly and prefer truly hierarchical systems. This has benefits with respect to fault-handling (both considering the granularity of configuration and the performance) and it also reduces the strain on the guardian actor, which is a single point of contention if over-used.

Concurrency vs. Parallelism

Concurrency

Parallelism


Every single actor inside Akka.NET has a unique address – expressed via an ActorUri and ActorPath, like this:

akka.tcp://<hostname>:<port>@<actor-system-name>/user/{parent}/{child}


The actor model adopts the philosophy that everything is an actor. This is similar to the everything is an object philosophy used by some object-oriented programming languages.

An actor is a computational entity that, in response to a message it receives, can concurrently:

  1. send a finite (constrained by bounds) number of messages to other actors;
  2. create a finite number of new actors;
  3. designate the behavior to be used for the next message it receives.

Electronic mail (e-mail) can be modeled as an Actor system. Accounts are modeled as Actors and email addresses as Actor addresses.

Actors are objects that do not operate on the same content that changes (mutable states) and communicate with each other via messages (message passing). As the result, the actor model eliminates many of the headaches that programmers face when solving the concurrency issues involving millions of senders and receivers of messages. how-twitter-is-scaling


  1. Objects can only guarantee encapsulation (protection of invariants) in the face of single-threaded access, multi-thread execution almost always leads to corrupted internal state. Every invariant can be violated by having two contending threads in the same code segment.
  2. While locks seem to be the natural remedy to uphold encapsulation with multiple threads, in practice they are inefficient and easily lead to deadlocks in any application of real-world scale.
  3. Locks work locally, attempts to make them distributed exist, but offer limited potential for scaling out.

In summary, this is what happens when an actor receives a message:

  1. The actor adds the message to the end of a queue.
  2. If the actor was not scheduled for execution, it is marked as ready to execute.
  3. A (hidden) scheduler entity takes the actor and starts executing it.
  4. Actor picks the message from the front of the queue.
  5. Actor modifies internal state, sends messages to other actors.
  6. The actor is unscheduled.

To accomplish this behavior, actors have:

  • A Mailbox (the queue where messages end up).
  • A Behavior (the state of the actor, internal variables etc.).
  • Messages (pieces of data representing a signal, similar to method calls and their parameters).
  • An Execution Environment (the machinery that takes actors that have messages to react to and invokes their message handling code).
  • An Address.

Messages are put into so-called Mailboxes of Actors. The Behavior of the actor describes how the actor responds to messages (like sending more messages and/or changing state).
An Execution Environment orchestrates a pool of threads to drive all these actions completely transparently.

Top Level Architecture


Stopping an actor can be done by calling Context.Stop(actorRef). It is considered a bad practice to stop arbitrary actors this way. The recommended pattern is to call Context.Stop(self) inside an actor to stop itself, usually as a response to some user defined stop message or when the actor is done with its job.

The actor API exposes many lifecycle hooks that the actor implementation can override.
The most commonly used are PreStart() and PostStop().

  1. PreStart() is invoked after the actor has started but before it processes its first message.
  2. PostStop() is invoked just before the actor stops. No messages are processed after this point.

These are the rules in Akka.NET for message sends:

At-most-once delivery, i.e. no guaranteed delivery. Message ordering is maintained per sender, receiver pair.

When it comes to describing the semantics of a delivery mechanism, there are three basic categories:

  1. At-most-once delivery means that for each message handed to the mechanism, that message is delivered zero or one time; in more casual terms it means that messages may be lost, but never duplicated. The first one is the cheapest, highest performance, least implementation overhead because it can be done in a fire-and-forget fashion without keeping the state at the sending end or in the transport mechanism.

  2. At-least-once delivery means that for each message handed to the mechanism potentially multiple attempts are made at delivering it, such that at least one succeeds; again, in more casual terms this means that messages may be duplicated but not lost. The second one requires retries to counter transport losses, which means keeping the state at the sending end and having an acknowledgment mechanism at the receiving end.

  3. Exactly-once delivery means that for each message handed to the mechanism exactly one delivery is made to the recipient; the message can neither be lost nor duplicated. The third is most expensive, and has consequently worst performance: in addition to the second, it requires the state to be kept at the receiving end in order to filter out duplicate deliveries.


Akka.NET lifts the responsibilities of guarantees to the application itself, i.e. you have to implement them yourself. On the other hand, you are in full control of the guarantees that you want to provide.


We also want to keep the original sender of the request so that our device actor can reply directly. This is possible by using Forward instead of the Tell operator. The only difference between the two is that Forward keeps the original sender while Tell always sets the sender to be the current actor. Just like with our device actor, we ensure that we don't respond to wrong group IDs.


Typically you use a very lightweight ActorSystem inside ASP.NET applications, and offload heavy-duty work to a seperate Windows Service via Akka.Remote / Akka.Cluster. https://getakka.net/articles/intro/use-case-and-deployment-scenarios.html#aspnet


If App Master shuts down or restarts, all of its child actors shutdown or restart with it – if a child actor dies on its own, it’s up to the App Master to decide what to do next. This is the essence of how actor supervision works.


One noteworthy aspect is that actors have an explicit lifecycle, they are not automatically destroyed when no longer referenced; after having created one, it is your responsibility to make sure that it will eventually be terminated as well—which also gives you control over how resources are released When an Actor Terminates.


But the most important aspect is that it is not possible to look inside an actor and get hold of its state from the outside, unless the actor unwisely publishes this information itself.

Instead of having to synchronize access using locks you can just write your actor code without worrying about concurrency at all.


The Top-Level Supervisors


Lifecycle Monitoring in Akka.NET is usually referred to as DeathWatch.


⚠️ **GitHub.com Fallback** ⚠️