Akka.NET Succinctly
By
Zoran Maksimovic
If you obtained this book from any other source, please register and download a free copy from
www.syncfusion.com.
The authors and copyright holders provide absolutely no warranty for any information provided.
The authors and copyright holders shall not be liable for any claim, damages, or any other
liability arising from, out of, or in connection with the information in this book.
Please do not use this book if the listed terms are unacceptable.
3
Table of Contents
Introduction .............................................................................................................................12
Purpose ................................................................................................................................12
Resources .......................................................................................................................14
Scaling up ........................................................................................................................18
4
The Reactive Manifesto ........................................................................................................19
Responsive ......................................................................................................................19
Resilient ...........................................................................................................................20
Elastic ..............................................................................................................................20
Message-driven ...............................................................................................................20
Conclusion ...........................................................................................................................20
Akka.TestKit ....................................................................................................................22
Akka.Remote ...................................................................................................................22
Akka.Cluster ....................................................................................................................22
Akka.Streams ..................................................................................................................23
Akka.Persistence .............................................................................................................23
Actors ...................................................................................................................................25
ActorSystem .........................................................................................................................26
UntypedActor ...................................................................................................................28
ReceiveActor ...................................................................................................................28
Receive ...........................................................................................................................31
ReceiveAsync ..................................................................................................................32
5
ReceiveAny .....................................................................................................................33
IActorRef..........................................................................................................................35
Serialization ..........................................................................................................................46
Conclusion ...........................................................................................................................47
Actor’s lifecycle.....................................................................................................................48
Example ..........................................................................................................................50
Stopping an actor.............................................................................................................53
6
Chapter 9 Supervision ...........................................................................................................76
EventBus ..............................................................................................................................82
DeadLetters..........................................................................................................................84
UnitTesting ...........................................................................................................................90
Actor definition....................................................................................................................105
7
Chapter 14 Akka.NET Remoting..........................................................................................111
Transport ............................................................................................................................112
Akka.NET server............................................................................................................115
8
The Story Behind the Succinctly Series
of Books
Daniel Jebaraj, Vice President
Syncfusion, Inc.
Whenever platforms or tools are shipping out of Microsoft, which seems to be about every other
week these days, we have to educate ourselves, quickly.
While more information is becoming available on the Internet and more and more books are
being published, even on topics that are relatively new, one aspect that continues to inhibit us is
the inability to find concise technology overview books.
We are usually faced with two options: read several 500+ page books or scour the web for
relevant blog posts and other articles. Just as everyone else who has a job to do and customers
to serve, we find this quite frustrating.
We firmly believe, given the background knowledge such developers have, that most topics can
be translated into books that are between 50 and 100 pages.
This is exactly what we resolved to accomplish with the Succinctly series. Isn’t everything
wonderful born out of a deep desire to change things for the better?
9
Free forever
Syncfusion will be working to produce books on several topics. The books will always be free.
Any updates we publish will also be free.
As a component vendor, our unique claim has always been that we offer deeper and broader
frameworks than anyone else on the market. Developer education greatly helps us market and
sell against competing vendors who promise to “enable AJAX support with one click,” or “turn
the moon to cheese!”
We sincerely hope you enjoy reading this book and that it helps you better understand the topic
of study. Thank you for reading.
10
About the Author
Zoran Maksimovic is a Solution Architect and Software Developer with almost 20 years of
professional experience. He is passionate about programming and web platforms, especially on
Microsoft technologies. Among others, his specialties and interests focus on: Microsoft.NET,
OOD, TDD, DDD (Domain Driven Development), CQRS/ES, and event streaming. He is also a
certified Scrum Master.
He spent most of his professional life working as a consultant on various projects for clients in
the financial sector based in Switzerland, Italy, Germany, and France.
In his spare time, he is contributing to his personal blog and active on Twitter as @zoranmax.
Zoran is also the author of the ServiceStack Succinctly and MongoDB 3 Succinctly e-books.
When not coding or spending time with his family, he enjoys playing guitar, baroque music,
good food, and Italian wine. He is married and the father of Alexei, Xenia, and Sofia.
11
Introduction
Purpose
The purpose of this book is to make you aware of Akka.NET framework and to let you start
working with this technology in the fastest possible way.
My hope is that after reading this e-book, you will have enough knowledge to start coding and
using Akka.NET effectively.
Target audience
This book is intended for software developers and readers with a good understanding of object-
oriented programming, the Microsoft.NET Framework (all of the examples are written in C#),
and the usage of the asynchronous execution in .NET. Knowledge of multithreading is also nice
to have.
You should be already familiar with ASP.NET Core, Microsoft Visual Studio, and Microsoft.NET
in general, as those concepts are not fully explained here.
Some shortcuts have been taken in this e-book, as the intention is not to go too deep into the
details—but rather to show the various options, possibilities, and concepts.
If you want to know more about the technologies mentioned in this book, take a look at the
following resources:
• C# Language
• Distributed Computing
• Reactive Manifesto
Source code
Akka.NET is an open-source framework written in C#, and at the time of writing it’s hosted on
GitHub here.
12
Akka.NET groups and communities
There are several groups on the web where additional information is available. The following list
contains a few that you might find useful:
Software requirements
To get the most out of this book and the included examples, you will need to have a version of
Microsoft Visual Studio IDE installed on your computer, and at least Microsoft.NET v4.
At the time of writing, the most current and stable edition of Visual Studio available is Visual
Studio 2017. You can download Visual Studio Community Edition 2017 for free directly from
the Microsoft website. Please pay attention to the licensing notes, as some restrictions might
apply, depending on the usage.
In addition, there are quite a few examples that use Microsoft .NET Core v2, which is also
required. You can install .NET Core here.
All of the examples in this book have been written and tested on Microsoft Windows 10 using
Microsoft Visual Studio 2017 Community Edition and Visual Studio 2017 Professional.
Tip: This will identify tips and tricks throughout the book.
Source code
Source code is written in a consistent manner. Command Prompt (terminal) code follows the
following style:
13
Code Listing 1: Command prompt code style
C:\>command
Most of the code examples are written in C#, and the following style is used when working with
C#.
[SomeAttribute]
public class Actor
{
public string Property { get; set; }
}
Resources
The code mentioned in this book can be downloaded here.
Akka.NET version
All of the examples and explanations apply to the version of Akka.NET v.1.3.2, which is the
latest stable version at the time of writing.
14
Chapter 1 Introduction
Millions of people are accessing all kinds of Internet services. We are living in an era where
there is a need for highly responsive and robust applications to serve exact user needs. The
typical user is affected by a large amount of information, and because of a proliferation of
services of all kinds all across the Internet, the user’s attention span is very short, and a few
seconds of unresponsive systems might mean that an item won’t be sold, or a blog article won’t
be read.
At the same time, the current technology allows us to have interconnected systems, grids of
servers, data centers, cloud infrastructure, and multi-core machines. The computing power of a
CPU, GPU, RAM, and HDD space costs have dropped substantially. This practically enables
the processing of the Internet of Things (IoT), online streams of data, big data, machine
learning, and artificial intelligence, among others.
We are living in a new world full of opportunities given by the technological advance. Let’s take
a look at a very high level of technology evolution to understand better what is going on:
Past Present
Very limited HDD space Potentially unlimited and cheap HDD space
While there are several approaches to implementing a multithreaded, concurrent, scalable, and
distributed system, the actor model paradigm is recently (re)emerging. As we are going to see,
this is not a new approach, but certainly a good one that is worth exploring. This book is fully
dedicated to Akka.NET, an actor model framework written exclusively for Microsoft.NET.
15
Short history of actor model and Akka implementation
Let’s start from the very beginning. If you are very new to the actor model, you might be
surprised that it is not a new concept—it originated in 1973 with the computer scientists Carl
Hewitt, Peter Bishop, and Richard Steiger. The concept has been captured in a publication
called “A Universal Modular Actor Formalism for Artificial Intelligence IJCAI'73.”
This paper proposes a modular actor architecture and definitional method for artificial
intelligence that is conceptually based on a single kind of object: actors (or, if you will, virtual
processors, activation frames, or streams). The actor model on its own is based upon a
mathematical model of a concurrent computation.
Fast forward to the current days: The Akka.NET framework is an implementation of an actor
model. It was introduced in 2014 by Aaron Stannard and Roger Alsing. Akka.NET itself is a port
of the Java/Scala Akka framework, which was introduced earlier on in 2009.
Akka.NET is a community-driven port, and is not affiliated with the Lightbend company, which
made the original Java/Scala version. The first version of Akka.NET was released in 2015. The
latest version (1.3) is .NET Core-compatible.
There are other programming languages, such as Erlang, Elixir, Pony, and Scala, that have
been written around the idea of the actor model and, in addition to these, quite a few
frameworks are available.
Microsoft alone has recently made quite a contribution, with two implementations of frameworks,
Microsoft Orleans and Service Fabric, and it supports actors on Microsoft Azure. On the other
hand, an alternative implementation of the actor model from one of the creators of Akka.NET is
Proto.Actor, which supports multiple platforms (.NET, Go, JavaScript, and Kotlin).
We can say that the actor model is more like a programming paradigm than a set of tools.
To implement the actor model, there are some fundamental rules to follow:
16
• Actors can change their state or behavior.
• Actors can send messages to other actors.
• Actors can create a finite number of child actors.
On the surface, actors look a lot like the objects in object-oriented programming (OOP). If we
think about it, OOP is first and foremost about message passing and abstraction. The actor
model is all about independent actors maintaining state and passing messages to each other.
This sounds very similar, right?
The traditional OOP languages (C#, Java, etc.) weren't designed with concurrency as a first-
class use case. While they do support the ability to spawn multiple threads, anyone who's done
multithreaded programming with these languages knows how easy it is to introduce race
conditions (for example, data desynchronization or corruption). From that perspective, it would
be really useful to compare the two paradigms when it comes to thread management and
memory access:
Actors are following the share-nothing philosophy, while typically the OOP languages are not
truly built around parallelism.
In the actor model, there are no race conditions, which results in simpler code. The share-
nothing philosophy of actors also means that the actors don’t have to live on the same
machine—we can spawn actors on different nodes and make them collaborate “natively.”
17
Thread Thread Thread Thread Thread Thread Thread Thread
Scaling up
Because the Akka.NET framework is managing concurrency for us, we are able to scale up the
servers on which the application is running. By adding additional CPUs, the system can have an
effective usage of those shared resources without us writing any additional code.
Scaling out
The Akka.NET framework is effectively helping to scale out as well, due to its share-nothing
philosophy, as mentioned earlier. By adding additional nodes, once again without us writing any
particular implementation, and just through some configuration, the application can effectively
use those resources. This is a big win, as we can concentrate on delivering value to our
customers rather than writing infrastructure code that enables distributed collaboration.
Common framework
All of the above-mentioned features make Akka.NET a framework that has a lot of capabilities.
Having it all available in one place makes it easier for teams to handle the various complex
aspects in a single framework, and makes the learning easier than having to master several
technologies to achieve the same result.
18
• Batch processing: Actors would allow dividing the workload between them.
• Services: REST, SOAP, and general communication services like chats or real-time
notifications.
When it comes to the Microsoft.NET framework, we can practically use the framework in every
imaginable way:
Responsive
Elastic Resilient
Message
Driven
As you can see in Figure 3, the Reactive Manifesto urges systems to have the following
characteristics.
Responsive
The system responds in a timely manner, and it focuses on providing rapid and consistent
response times so that the quality of service is constant. As a benefit of such a system, the
error-handling is simplified, meets the user expectations, and encourages further interaction.
19
Resilient
In general, “resilience is the ability to provide required capability in the face of adversity,” as
defined by the International Council on Systems Engineering, and in software-engineering
terms, this would mean that the system stays responsive in the face of failure. There are several
solutions that would help the system to stay resilient—such as replication, containment,
isolation, and delegation. These are all in the context of how to handle the failures by not
affecting the system as a whole, and how to recover from them successfully.
Elastic
An elastic system is able to adapt to workload changes by provisioning and deprovisioning
resources in an autonomic manner, such that at each point in time, the available resources
match the current demand as closely as possible. This implies designs that have no contention
points or central bottlenecks, resulting in the ability to shard or replicate components and
distribute inputs among them.
Message-driven
Reactive systems rely on asynchronous message-passing in order to ensure loose coupling
among components, isolation, and location transparency. In turn, the system can be adapted
easier (in order to become elastic). Another important aspect is the location transparency of
components, which assumes the caller should not worry about the physical location of the
target.
Conclusion
This chapter was about the theoretical aspects of the actor model, its history, and various
aspects of a reactive system. While entire books can be written on some of these topics, the
idea was to give some basic information and context to what this book is about.
20
Chapter 2 Akka.NET Components
In this chapter, we are going to list the main building blocks of Akka.NET. This includes the
libraries (modules) and some key concepts used by the framework.
In order to write any application, we have to rely upon the dependency on the Akka.NET
framework. Akka.NET comes as a set of very modular libraries that compose quite a wide range
of functionalities. All of the libraries are available through NuGet, as we are going to see later
on.
A part of the Akka(Core), Akka.Remote, and Akka.TestKit, the other components won’t be
discussed in this book; however, it’s still good to know about them.
Akka.Streams
Akka.Persistence
Akka.TestKit
Akka.Remote
Akka.Cluster
It contains the definition of an actor, the default object serialization mechanism, routing rules,
Human-Optimized Config Object Notation (HOCON) configuration rules, the message
dispatching mechanism, and much more. In order to use Akka.NET, we have to have a
dependency on this library.
21
The following command can be run in the Visual Studio Package Manager to reference the
Akka base library in Visual Studio:
Akka.TestKit
Akka.TestKit is a base library with building blocks that allow the effective testing of an actor
system. Akka.TestKit defines the core libraries, and every unit-testing framework will
implement the plumbing needed to translate the unit-testing engine’s specific needs.
Akka.Remote
Akka.Remote brings the capability to build an ActorSystem across multiple processes over a
computer network. Akka.NET supports communication between actors deployed remotely (living
on different servers), with the great advantage that the programming model hides this
complexity. Communication from the programming view is not different from the local versus
remote, as the code would look exactly the same. Usually the Akka.Remote package is not
used in isolation, but as a building block for the clustering capability.
To reference the library in Visual Studio, the following command can be run in the Visual Studio
Package Manager:
The installation of Akka.Remote will automatically include a reference to the Akka (core) library
and DotNetty (an event-driven asynchronous network application framework), which is used as
the transport mechanism.
Akka.Cluster
While Akka.Remoting solves the problem of addressing and communicating with components
on remote systems, clustering gives the ability to organize a number of ActorSystems to
behave as a “single unit,” which enables the scalability and high availability of the system itself.
22
• Handling the remote systems so that they can communicate with each other in a reliable
way.
• Handling the change in the server’s setup: new cluster memberships, removal (failure) of
servers, etc.
• Detecting disconnected systems that are temporarily unreachable.
• Distributing the computation between members (scaling out).
To reference the library in Visual Studio, the following command can be run in the Visual Studio
Package Manager:
Code Listing 5: Installing Akka.Cluster package via Package Manager in Visual Studio
The installation of Akka.Cluster will automatically include a reference to the Akka (core)
library, DotNetty, and Akka.Remote.
Akka.Streams
Sometimes actors are not the most suitable tool when it comes to processing a stream
(unlimited) of data. In this sense, Akka.Streams builds on top of actors and provides a higher
level of abstraction. Akka.Streams is also an implementation of the Reactive Streams standard.
• Handling streams of events or large datasets, keeping the proper usage of performance
and resources.
• Flexible pipelines in order to reuse functionalities.
• Enabling consumption of Reactive Streams compliant interfaces of other, third-type
libraries.
To reference the library in the Visual Studio solution, run the following in the Visual Studio
Package Manager:
The installation of Akka.Streams will automatically include a reference to the Akka (core) library
and Reactive.Streams library.
Akka.Persistence
Persistence provides a means to enable actors to persist their current state. There are several
libraries that implement persisting data to various systems, such as Microsoft SQL Server,
MySql, and Redis.
23
• How to restore the state of an entity/actor when system restarts or crashes.
• Implementation of a system following a CQRS/Event Sourcing pattern.
• Reliable delivery of messages.
To reference the library in Visual Studio, run the following in the Visual Studio Package
Manager:
Additionally, there are a number of other packages that have a specific implementation for a
given database system:
Code Listing 8: Installing Akka.Persistence packages via Package Manager in Visual Studio
24
Chapter 3 Introduction to Actors
Actors
We have mentioned several times the existence of an actor, the most basic feature and one of
the main building blocks of Akka.NET. An actor encapsulates an object’s behavior and state,
and communicates with other actors by exchanging messages. Every instance of an actor has
its own MailBox where the messages will be enqueued and successively processed by the
actor, one by one, respecting the order of messages.
By using actors, we have simple and high-level abstractions for concurrency and parallelism.
This is due to the fact that every time an actor processes a message, this might happen on
another thread.
Tip: To better visualize the idea of an actor, let’s think of an actor as a real person.
This person can distribute (delegate) the work to other people, or simply fail at
processing certain messages.
Actors are built and organized around a hierarchical structure in order to split the tasks into
smaller and more manageable pieces. If we think about it, this is very similar to object-oriented
programming where we have classes and functions in order to split or organize some larger
logic into a smaller subset of building blocks. This also implies that actors are able to create and
supervise subactors and control their lifetimes: creation and termination. This also implies that
every actor has only one supervisor.
Thread Thread
actor actor
Mailbox Transport 1 2 3 4 Mailbox
Messages
Behaviour Behaviour
ActorRef
State State
Supervision Supervision
Strategy Strategy
25
Actor lifecycle
Every actor has its own lifecycle, which exists in the moment when the actor is created, is
running, and is terminated. Even though creating a new instance of an actor is relatively cheap
in terms of memory and processing, a proper handling of the life of an actor should be planned.
ActorSystem
One of the ActorSystem responsibilities is to manage the resources in order to run the actors.
An ActorSystem is a hierarchical group of actors that share common configuration, for
example, dispatchers, deployments, thread pooling, remote capabilities, and addresses. An
actor cannot exist outside the ActorSystem; therefore, we can also say that an ActorSystem is
a container or a host of actors.
An ActorSystem is not cheap to create, it’s typically created only once per application, even
though multiple systems can run in the same application on top of .NET runtime. Once it’s up
and running, it will usually be there for the whole lifetime of the application, or at least until it’s
not needed (terminated). In that case, the different actor systems won’t have anything in
common and would live in separate memory spaces, threads, etc.
Application
ActorSystem 1 ActorSystem 2
26
• Creates and allocates threads, so that the actors can use the underlying threads.
• Hosts the configuration.
Actor reference
As stated previously, an actor has a state, behavior, mailbox, child actors, and supervision
strategy. All of this is encapsulated behind an actor reference.
In an actor system, actors are referenced by actor references, which are like pointers to an
instance of an actor. The actor reference enables actors to have a reference to another actor
(send messages); however, this is unrelated to the actual state (lifecycle) of the actor itself.
Removing a reference to an actor doesn’t “destroy” that actor, and vice versa.
The actor reference, on the other hand, makes possible the so-called location transparency of
actors, which means that an actor can be deployed on a remote system, but from the actor-
reference perspective (client calling the actor), nothing changes in the programming model.
Phone calls are a good example to explain the location transparency: We have the phone
number (of a person), so sending an SMS or placing a phone call is always possible, where
ever the other person is located.
An actor reference can be passed around as a variable. An actor through actor references
always has the ability to access the child actors, self, sender, and its parent actor.
27
Chapter 4 Working with Actors
In the first three chapters, we mentioned some basic building blocks and the theory around
actor model programming and Akka.NET. In this chapter, we will finally start implementing the
mentioned concepts, starting from the very simple aspects, and end up building a live system.
Type of actors
Akka.NET offers two kinds of actor base types: ReceiveActor and UntypedActor. The
difference between the two, as we are going to see, is the signature for receiving messages.
One is strongly typed, while the other is not. The usage of one or the other would most probably
depend on the use case and handling of types of incoming messages.
An actor in Akka.NET is implemented as a class that inherits from one of the aforementioned
base types.
UntypedActor
As shown in Code Listing 9, implementing an actor is quite straightforward. In the case of an
UntypedActor, we have to override the OnReceive method (please note that the OnReceive
method accepts a generic object). The OnReceive method will be the entry point for all the
messages sent to the actor, and the implementation of this method should handle all kinds of
message types that are expected.
ReceiveActor
On the other hand, the ReceiveActor has a bit of a different structure. There is no OnReceive
method to be overridden anymore, and the mapping to the message handlers is done directly in
the actor’s constructor. The mapping is done through one of the many Receive methods to
which we have to specify the type and the actual handler.
28
{
public MyTypedActor()
{
/* some code goes here */
}
}
Actor instantiation
Creating a new instance of an actor can only be done through the ActorSystem. The
ActorSystem offers two methods: ActorOf and ActorOf<T>, which are responsible for the
actor’s creation.
In order to be identified, an ActorSystem has to have a name. This is mainly needed in order to
ensure the actor’s location, as we are going to see later on. In our case, the name of the
ActorSystem is my-first-akka. The name can be any string.
By using the generic version of the ActorOf method, we can only specify the name of the actor.
The name is optional, and if it’s not specified, one will be assigned automatically by the system.
Usually the name given is in the form of “$a”, “$b”, etc.
Code Listing 11: Creation of an Actor through ActorSystem by using generic ActorOf<T>
Note: The two IActorRef objects: typedActor and untypedActor, are actor
references, through which we can communicate with the actor.
The alternative to ActorOf’s generic version is to use the nongeneric version, which accepts
different parameters: we have to supply a parameter of type Props. Props is a configuration
class to specify options for the creation of actors. By using Props, we can define a
SupervisionStrategy, create the actor by its constructor attributes, or specify a Factory for it.
Let’s see an example of using Props when creating our previously defined actors.
29
Props untypedActorProps = Akka.Actor.Props.Create<MyUntypedActor>();
There are several ways of using Props, as we can see in the following code snippet.
ReceiveActor construction
In this book, we will only be using the ReceiveActor, for the simplicity and as a matter of
preference. In this section, we will be looking at the various methods that the ReceiveActor
offers, and will expand on those.
The configuration of message handlers always happens in the actor’s constructor. So, often you
will be writing code similar to the following:
Code Listing 14: Handler message methods are declared in the constructor
30
The Receive method is defined in the base (ReceiveActor) class, and it registers a handler
(method) for a particular type of message that the actor receives.
It’s worth pointing out that there are several Receive methods available, such as Receive,
Receive<T>, ReceiveAsync<T>, ReceiveAsync, ReceiveAny, and ReceiveAnyAsync. Let’s go
through some of them.
Receive
The Receive method, being strongly typed, will ensure that the defined message is handled by
a predefined handler (method).
Since the Receive<T> method in question has several overloads, and may accept the Func<T,
bool>, Action<T>, or Predicate<T> objects as arguments, it’s possible to write either an inline
declaration of the code to be executed, or a pointer to an actual method that implements such
an interface.
Alternatively, you can use another method to handle the message—Code Listing 15 is
equivalent to Code Listing 16.
An important thing to note here is that the messages an actor receives will be processed one-
by-one in a synchronous manner. This means no new messages will be processed until the
HandleStringMessage has finished processing the current message.
31
It is not a good practice (and will actually cause issues) to use the async/await pattern inside a
Receive method, as this would break the order of messages—the Receive method will exit
before the actual processing is finished. This is a very important concept, and to handle this
scenario, Akka.NET has implemented the ReceiveAsync method.
ReceiveAsync
ReceiveAsync<T> and ReceiveAsync enable the async/await pattern when defining
handlers.
Even though the ReceiveAsync handles asynchronous messages, it will process one message
at a time in order to preserve the ordering of messages—so don’t expect parallel processing of
messages inside an actor by using the ReceiveAsync. Getting new messages from the actor’s
mailbox in this method will therefore behave exactly as the normal Receive method: one-by-
one, and in an ordered fashion.
Console.WriteLine("\n=====================================");
Console.WriteLine($"Data for {url}");
Console.WriteLine(html.Trim().Substring(0, 100));
}
}
receiveAsyncActor.Tell("https://www.agile-code.com");
receiveAsyncActor.Tell("https://www.microsoft.com");
32
receiveAsyncActor.Tell("https://www.syncfusion.com");
}
The first thing to note is that the ReceiveAsync signature can now mark the method as an
async method and await it (async url => await GetPageHtmlAsync(url)).
When running the code in Code Listing 17, we can clearly see that the order of messages will
be maintained.
ReceiveAny
The ReceiveAny and ReceiveAnyAsync methods will handle all or any type of message, if the
type of a particular message is not already handled by one of the other Receive methods. This
can be used as a catch-all in case we don’t support some messages, in order to have a
managed handling of improperly registered messages. So, it is a good practice to put the
ReceiveAny at the end of the list of Receive methods; otherwise, you will get the following
exception:
In Code Listing 18, we can see an example of how to use the ReceiveAnyAsync by following
the previous example of the HTML page download actor. We can see that now the
GetPageHtmlAsync method accepts an object rather than a string.
33
public DownloadAnyHtmlActor()
{
ReceiveAnyAsync(async obj => await GetPageHtmlAsync(obj));
}
Console.WriteLine("\n=====================================");
Console.WriteLine($"Data for {url}");
Console.WriteLine(html.Trim().Substring(0, 100));
}
else
throw new ArgumentNullException("Actor doesn't accept this kind
of message");
}
}
We can also see that the GetPageHtmlAsync also checks for the type of the object passed. In
case of a string, or if a System.Uri object is passed, the actor would download the page, and
otherwise will throw an exception.
Also in the client code, we can clearly see that we pass three types of messages to the actor: a
string, a System.Uri, and a GreetingMessage (which is not currently being handled).
receiveAsyncActor.Tell("https://www.agile-code.com");
receiveAsyncActor.Tell(new Uri("https://www.syncfusion.com"));
receiveAsyncActor.Tell(new GreetingMessage("hi"));
Console.Read();
system.Terminate();
}
The output is as we may expect: two successfully downloaded pages, and one exception
thrown.
34
Figure 8: Output of DownloadAnyHtmlActor execution
IActorRef
One thing you have probably already noticed is that when we create a new instance of an actor,
what is being returned is an implementation of an IActorRef rather than an instance of the
actor itself. We spoke in Chapter 3 about actor references, and this is how they are represented.
IActorRef is a handle to an actor. Having a reference to an actor guarantees that the actor is
alive, or that it existed in the past. Unfortunately, this handle doesn’t answer the question of
whether or not the actor is still alive.
Among others, it contains three methods that enable communication between actors, being
local or remote: Ask, Tell, and Forward. In addition to this, it is possible to see the full Path of
an actor.
Tell
The Tell method uses the fire-and-forget pattern; using Tell, one actor simply sends a
message to another actor and doesn’t wait for any response back, and it returns immediately (it
is a nonblocking call).
When we send a message using Tell, it might seem as if we are doing nothing more than
sending a simple plain-old CLR object (POCO). This is not fully the case. When you invoke
Tell, the information about the Sender of the message tags along. Within a given Actor, there
is an implicit property called Context. We can use Context to determine the parent of
the Actor, and Context.Parent. Context.System can be used to access the
root ActorSystem, under which the Actor resides. You can get a reference to the Actor itself
using Context.Self.
35
Forward
Forward is a special method of Tell, where the sender information will be carried as part of the
message context, which means that even if the message goes through several actors, the
original actor that sent the message will be preserved in this context.
Ask
Ask is a request/response-based communication pattern: it sends a message to another actor,
expecting it to respond with another message, and returns a Task, asynchronously notifying
when the response will come back.
In general, there are performance implications for using Ask, since under the hood there is quite
some work to be done in order to enable the mechanics of mapping the request with the
response, etc. So, you should prefer using Tell for performance, and only use Ask if you have
no other choice.
As an alternative of Ask, we can always use Tell or Forward, and return back the message in
a fluent manner, as depicted in Figure 9.
B Ask C
Ask Ask
A D
B Forward C
Tell Forward
A D
In the first section, we can see that in order to communicate between Actors A and D, we have
to block several actors (B and C) in order to get the response back.
In the second part, we can see that by simply passing the message, and maintaining the
context, it is possible to have an asynchronous response by combining Tell and Forward.
36
Messages and immutability
One very important aspect of the data (messages) being passed to actors is that those
messages should be immutable. Immutable messages are crucial because we can send the
same message to many actors concurrently, and if each one of those actors makes a
modification to the state of the message, all of those state changes are local to each actor.
Obviously, we want to avoid this, so the preferred way of creating messages sent to actors
would be to avoid using public setters, which automatically force us to pass and set properties
via constructors.
In this way, we can be completely sure that there will be no side effects if this exact instance of
a message is used by multiple actors at the same time. An example of an immutable message
is defined in the following code:
Dependency injection
Akka.NET supports dependency injection, which is supported by the DependencyResolver
class that can create Props using the dependency injection (DI) container.
There are several containers supported out of the box by Akka.NET, and there are libraries
ready to be referenced and used. A few examples are Ninject, AutoFac, Microsoft Unity,
SimpleInjector, and StructureMap.
37
Figure 10: Dependency injection libraries
In the example we are going to show, we are creating an actor MusicActor that has
MusicSongService as a dependency. We are keeping things very simple here just to
demonstrate the plumbing of the various parts.
First things first. When creating a new Visual Studio project, if we want to use the DI, then we
need to reference the library that will enable us to do so. In our example, we are using AutoFac.
Therefore, our project has to install the following NuGet packages:
Code Listing 21: Installing Akka–AutoFac dependencies
In reality, installing only Akka.DI.AutoFac would autoinstall the two other libraries and the
AutoFac container library itself.
Let’s start with defining the service that our MusicActor depends upon:
Code Listing 22: MusicSongService definition
38
{
Song GetSongByName(string songName);
}
Nothing too difficult here: our service MusicSongService has one method and returns an object
of type Song. To keep the example simple, there is no real implementation of the song retrieval,
which isn’t really relevant for our example.
The following is the implementation of the actor. In bold, we can see that the MusicActor
requires that an object of type IMusicSongService gets injected at creation time. Our actor
receives a message of type string, and the HandleSongRetrieval method will be responsible
for using the service to retrieve the song.
39
}
Now that we have all the moving parts defined, let’s see how to inject the IMusicSongService
into the actor upon creation effectively.
using Akka.Actor;
using Akka.DI;
using Akka.DI.AutoFac;
using Akka.DI.Core;
using Autofac;
musicAct.Tell("Bohemian Rhapsody");
Console.Read();
system.Terminate();
}
We can see that the first part of the program is the creation and configuration of the AutoFac
container system, where we have to register all the dependencies. Don’t forget to register the
MusicActor, which is the actor itself. This is no different from what we got used to when
working with inversion of control containers. ContainerBuilder will return the actual container
after the builder.Build() method has been called.
One important thing to notice here is that the DependencyResolver has to be instantiated and
attached to the ActorSystem. This is done behind the scenes by the
AutoFacDependencyResolver.
DependencyResolver will be eventually used by the system.DI() extension method (for this,
we need to include Akka.DI.Core in the using section). system.DI().Props<MusicActor>()
is equivalent to the Props we have already seen, with the only difference being that it will use
the dependency injection framework to create the actor.
40
From there on, everything works as with normal actors.
In order to do this, let’s create a new Console Application in Visual Studio (it’s fine if you
want to use the .NET Core or the full .NET, as both are supported by Akka.NET). Don’t forget to
reference the Akka base library from NuGet by running the following:
Let’s create our actors, which will implement a bit more than we have previously seen. Upon
receiving a message, we will display some information about the context as well as the
message being sent.
41
}
The UntypedActor’s logic is exactly the same, except that we have to handle the actual
message type, as we can get any type of message.
And finally, in Code Listing 29 Listing 29 is the main method that wires everything together by
assigning specific names to the actual actors. Please note that we are assigning names, such
as untyped-actor-name and typed-actor-name, just to be able to identify the instance of the
actor when the data is displayed in the console windows.
In this method, we can also see the usage of the method Tell, where the GreetingMessage is
being passed as an argument.
42
ActorSystem system = ActorSystem.Create("my-first-akka");
Console.Read();
}
As we can see, the two actors were able to receive a message, and to display the information
about the message received. At the same time, more information about the Context has been
displayed, too.
43
Figure 12: Sequence diagram depicting communication with CalculatorActor
Let’s start with the definition of the CalculatorActor and the two messages that we are going
to exchange, Add and Answer:
44
}
We can see that the CalculatorActor accepts a message of type Add, and returns an object
of type Answer to the sender. Sender is a special object that represents the producer of the
original message. The message is returned to the sender via the Tell method.
In Code Listing 31, we can see how to send a message to the CalculatorActor:
The main part of this piece of code is the calculator.Ask method. Ask, as we have already
mentioned, returns a Task object. In our case, we define the return object as part of the generic
signature of the call calculator.Ask<Answer>. The Add message is being sent as a parameter
to Ask. In order to return the result, we use .Result, as we would normally do in .NET. As the
Main method is not marked as async, we are forced to call .Result; otherwise, we would use
the async/await pattern.
The output of this call is, without much surprise, as shown in Figure 13.
45
Serialization
You have probably noticed the warning yellow message at the top of the output. Currently,
Akka.NET uses NewtonsoftJsonSerializer as the default serializer, but the decision has
been made to change it in favor of the Hyperion library. Therefore, you may want to install
Hyperion from the Visual Studio package manager. Hyperion has some advantages over the
NewtonsoftJsonSerializer.
As the url provided in the warning didn’t work for me, here we can take a look at the basic
changes needed to make the warning disappear and, obviously, to set up Hyperion as the
default serialization library.
The first thing to do is install the Akka.Serialization.Hyperion package. This version is still
in beta at the time of writing, but is stable enough to be used.
The second step is to change the app.config file in order to instruct Akka.NET to use
Hyperion as the default serializer.
For those familiar with .NET configurations, there is nothing fancy about this, apart from the fact
that Akka.NET uses the HOCON object model in order to set up properties. For the time being,
we won’t discuss all of the possible configurations available.
<configSections>
<section name="akka"
type="Akka.Configuration.Hocon.AkkaConfigurationSection, Akka" />
</configSections>
<akka>
<hocon>
<![CDATA[
akka
{
actor
{
serializers
{
hyperion = "Akka.Serialization.HyperionSerializer,
Akka.Serialization.Hyperion"
}
serialization-bindings
{
"System.Object" = hyperion
}
}
46
}
]]>
</hocon>
</akka>
Conclusion
In this chapter, we have seen how to create actors with or without Preps. We have also seen
the basic building blocks when it comes to the creation and instantiation of an ActorSystem,
created instances of actors, and just scratched the surface of passing a message to an actor.
Even though we didn’t get into details about HOCON configuration, we have seen that
Akka.NET supports this way of setting up the configuration properties, and that it’s quite easy to
configure a default serializer.
47
Chapter 5 Actor Lifecycle and States
Actor’s lifecycle
An actor has a lifecycle. With the lifecycle, we attend to the actual stages or statuses through
which the actor passes: creation, starting, stopping, termination, etc. The actor API offers
extension points, so that it is possible to hook into the various stages of the actor’s lifecycle, and
perform actions.
Let’s start with a diagram depicting the various states of an actor’s lifecycle:
Initialized
(actor created, constructor called)
PreStart
Started
PostRestart
(actor is active and processes messages)
Stop message/
instruction
We can see the stages (in blue) and the actual methods executing after that phase (in orange).
Initialized The actor instance is created and the actor’s constructor has been
called. At this point no messages are yet received—only after the
constructor is executed will the PreStart be called. PreStart is a
method that enables us to prepare all the necessary work before
starting to receive messages.
Started After the PreStart method has finished executing, the actor is alive
and able to receive and process messages from the mailbox, one by
one. The actor can be instructed to Stop, as we are going to see
later. The stop message (or the stop instruction) will lead to the
state change, and the actor will go to the next state: Stopped.
48
Stopped There are several ways to stop an actor, and two possible outcome
scenarios:
• Scenario one: The actor terminates, in which case the
PostStop method will be called.
• Scenario two: The actor restarts, in which case
PreRestart will be called. PreRestart, as implemented in
the base class, will automatically call the PostStop as the
next step. If we want to truly restart the actor, we should not
call the base class implementation.
In any case, if PostStop is invoked, there is no way back—the actor
is terminated.
Terminated The state in which the actor is not active anymore, and cannot be
restarted—in effectƒ, this actor doesn’t exist anymore.
Restarting If the parent actor has identified the error, and it doesn’t want to
terminate the actor, then this actor can be asked to restart.
At this point, the actor will be in the restarting state. After this state,
the actor is going to become active again, as when originally
created.
After the PreRestart has run (as we have seen in the Stopped
phase), just before the actor starts again, we have the ability to
implement the PostRestart method.
PreStart This method is called before the actor starts receiving its first message.
Here we can place any kind of custom initialization code. For instance,
opening files, database connections, etc.
PostStop This method is called after the actor has been stopped, and is not
receiving or processing messages.
Here we can place any custom cleanup code.
PreRestart This method is called before the actor begins restarting. While
PreStart and PostStop are not receiving any input parameters,
PreRestart will have available the latest message being processed,
and the exception that caused it to restart.
One of the reasons for the existence of this method is to save the
message being processed when the exception occurs, to be
reprocessed later after the actor restarts.
49
PostRestart PostRestart gets called after the PreRestart method, but before the
PreStart method.
PostRestart as an input parameter has the exception that last
occurred, so it allows the code to do something with this exception: to
run some diagnostics or logging, etc.
Example
In the following example, we are going to see how to track the invocation of the actor’s
constructor, PreStart, and PostStop methods.
The idea here is that we have a very trivial EmailMessage to be sent by using the
EmailSenderActor. EmailSenderActor will implement the overloads of the Hook methods in
order to display in the console what is happening.
Let’s define the EmailMessage, which is the message to be passed to the actor. EmailMessage
is for demonstration purposes only, and doesn’t implement anything unusual.
Code Listing 34: Definition of the EmailMessage
Let’s implement the actual actor, with the overloads, which will be further explained.
Code Listing 35: Definition of the EmailSenderActor
50
Console.WriteLine($"Email sent from {message.From} to
{message.To}");
}
51
Now we can create the code that sends the message and eventually stops the ActorSystem.
This is something we have not yet mentioned: the ActorSystem.Terminate method will
actually terminate the actor system, which will obviously terminate all of the actors instantiated.
In our case, this will also cause our actor to terminate.
Code Listing 37: Sending messages to the EmailSenderActor
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
Thread.Sleep(1000);
system.Terminate();
Console.Read();
}
The output of this is as follows:
Actor termination
In this section, we are going to explain how to terminate an actor: how to programmatically
make sure that an actor will be terminated.
There are several ways to achieve this:
52
• Terminating the ActorSystem, as we have seen in the previous example.
• Using the GracefulStop.
Stopping an actor
Stopping an actor can be done by using the ActorContext.Stop method. This is available in a
few places, such as:
• The ActorSystem contains the Stop method, to which we can pass the actual actor
reference.
• The actor itself has the ability to call the Stop method by calling Context.Stop(Self).
• The actor can stop child actors by calling Context.Stop(childActorReference).
One of the most important things to note is that the Stop method will only let the actor execute
the message currently being executed before shutting down the whole actor instance. This
means that no other messages will be executed, including those that are enqueued before the
actual Stop() method is invoked.
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
emailSender.Tell(emailMessage);
system.Stop(emailSender);
system.Terminate();
}
In Code Listing 38, we are sending three email messages to be executed; however, as we are
going to see, only the first and second ones will be processed, while the third one will not, as it
53
comes after the PoisonPill message. The PoisonPill.Instance represents an instance of
the message.
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
emailSender.Tell(emailMessage);
emailSender.Tell(emailMessage);
emailSender.Tell(PoisonPill.Instance);
emailSender.Tell(emailMessage);
Thread.Sleep(1000);
system.Terminate();
Console.Read();
}
With the following output, we can clearly see that the first two emails are sent, but the third one
is not.
54
Code Listing 40: Sending the Kill.Instance message to an actor
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
emailSender.Tell(emailMessage);
emailSender.Tell(emailMessage);
emailSender.Tell(Kill.Instance);
emailSender.Tell(emailMessage);
Thread.Sleep(1000);
system.Terminate();
Console.Read();
}
Executing this code produces the following output—as before, the third message hasn’t been
delivered!
55
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
emailSender.Tell(emailMessage);
var result = emailSender.GracefulStop(TimeSpan.FromSeconds(10));
Thread.Sleep(1000);
system.Terminate();
}
Restarting an actor
Restarting an actor is just a bit more complicated, as it involves the understanding of the
supervision strategies. We are going to discuss the actor hierarchy and supervision strategies in
the next chapter, but for the time being, let’s just say that every time we create a new actor,
there is a default supervision strategy being associated to this actor. This means there is a
standard behavior, and the system will know how to handle the failure (an exception being
thrown by the actor).
The default supervision strategy will make it so that every time the actor has a processing
error (an exception being thrown), it will ask the actor to restart, which means that a new
instance of an actor will be created.
56
Note: By using Props.Create<Actor>(), we are able to supply a Supervision
Strategy. If none has been supplied, the default one will be used.
The following example shows what happens when the actor throws an error. In order to
demonstrate this, let’s slightly change our EmailSenderActor to throw an ArgumentException
in case the email content is not set (null or empty). So, let’s go and change the
HandleEmailMessage method as follows:
IActorRef emailSender =
system.ActorOf<EmailSenderActor>("emailSender");
emailSender.Tell(validEmail);
emailSender.Tell(invalidEMail);
emailSender.Tell(validEmail);
Console.Read();
system.Terminate();
}
What we are doing here is simply sending a valid versus an invalid (empty content) email. As
we have previously mentioned, we already know that the second message (invalidEmail) will
raise an error and cause the actor to restart.
Let’s just check this output, as shown in the following figure.
57
Figure 19: Restarting an actor
What we can see is that the first message gets sent just fine. The second one is obviously
raising an exception (shown in red), but just before that, we can see that the PreRestart and
PostStop events have been raised. Immediately afterwards, we can see that the actor restarts
and the constructor gets called again, followed by the PostRestart method. What we have
clarified here is that the PostRestart actually happens after the constructor is called.
Note: It’s important to keep in mind that when the actor restarts, it doesn’t
maintain its state. That means a new instance of the actor gets created, and all
of the private variables will simply be lost!
58
Chapter 6 The Actor’s Switchable Behavior
Actors intrinsically have the ability to change their behavior at runtime! What do we mean by
behavior? An actor’s behavior is its ability to react differently to the received message,
depending on the conditions.
When the actor starts, it has its own default behavior, which means that the function to be
executed when the message is received is set up at that point. However, this function might
change based on some conditions. For instance, if the client is not authorized to execute
something, the actor can switch the behavior and deny any further execution.
Why is behavior interesting? One way of implementing behaviors would be to put conditional
statements (if, then, else) all over the place in order to deal with a particular condition (is call
authorized). Instead, the actor model solves this by allowing you to switch to a different
message processor, so when the next message comes, this will be executed within the new
behavior.
There is no limit on how many behaviors an actor may have, and changing of behavior is
handled by the function Become(). After we call Become(), the previous behavior is forgotten
unless we use the BecomeStacked() function, which creates a stack of behaviors, so that the
behavior can be reverted by using UnbecomeStacked().
Note: The change in the behavior always applies to the next message to be
processed.
The music player we are building accepts two message types, and has some rules:
• PlaySongMessage: Instructs the player to start playing a song. The rule is that the
player cannot play another song if a song is already playing.
• StopPlayingMessage: Instructs the player to stop playing a song. If we stop an already
stopped player, nothing happens.
This means that the music player has two behaviors (or states): one in which a song is currently
being played, and one in which the player is stopped.
59
Code Listing 44: Music player input messages definition
As we can see, the StopPlayingMessage class is pretty much a marker to instruct the player to
stop playing. There is no additional information, as it’s not needed. We know there can be only
one song played at a time.
On the other hand, the PlaySongMessage has an attribute called Song, which represents the
song name.
The actor we are going to create is called MusicPlayerActor, and will inherit from the
ReceiveActor base class, which we have previously discussed.
public MusicPlayerActor()
{
StoppedBehavior();
}
60
Receive<StopPlayingMessage>(m => StopPlaying());
}
Become(PlayingBehavior);
}
Become(StoppedBehavior);
}
}
After looking at the content of the actor, we can see there are two methods that define a
behavior of an actor, as explained in Table 4.
We are setting the behavior of the actor by simply creating the two methods that will decide
what happens to the messages being handled.
In Code Listing 45, we can see how the Become method is being used to switch to the new
behavior. We use Become just after we stop the player, or start playing a new song. Very
straightforward!
61
One thing to note is the fact that the music player actor has a state, defined by the
CurrentSong property, which is set every time we play a new song. This shows how an actor
can hold state. We mentioned that one of the attributes of an actor is that it has a state, and this
is exactly it! It’s not any different from a typical usage of a class, as we used when working with
C#.
Let’s see how to call the actor by starting and playing various songs.
Code Listing 46: Client code for running the music player
IActorRef musicPlayer =
system.ActorOf<MusicPlayerActor>("musicPlayer");
Console.Read();
system.Terminate();
}
We can see that if we try to play a new song while the previous song is being played, the
following message is displayed: Cannot play. Currently playing ‘Smoke on the water’.
In the same way, if we try to stop an already-stopped music player, the following message will
be shown: Cannot stop, the actor is already stopped.
62
Chapter 7 Actor Hierarchies
We’ve seen how to create the top-level actors by using the ActorSystem directly. In this
chapter, we are going to see how to create the hierarchy of actors, which means: how an actor
can create subactors and form a hierarchy.
ActorContext (or just Context, as this is how the actor base library exposes it) exposes
contextual information for the actor and the current message. It provides more information, such
as: factory methods to create child actors (in the same way the ActorSystem does), lifecycle
monitoring, supervised children, and so on.
That said, let’s see the actor’s hierarchy in action by creating an example that expands the
music player application so that it can support multiple users playing a song.
If you recall, in the previous example we only had one instance of the MusicPlayerActor. This
actor was only capable of playing one song at a time. We will expand this example and create a
sort of a multi-user music player that will be able to serve requests from multiple users.
It’s this actor’s responsibility to create new instances (child actors) of MusicPlayerActor based
on the user, so that each user will have its own copy of the MusicPlayerActor, so that it can
play its own songs.
In Figure 21, we can see how the MusicPlayerCoordinatorActor receives the two messages
(PlaySongMessage and StopPlayingMessage) and coordinates with the subactors.
63
PlaySongMessage StopPlayingMessage
Child Actors
The input messages have to change slightly to include the user together with the song. The
highlighted changes can be seen in the following code:
64
{
protected Dictionary<string, IActorRef> MusicPlayerActors;
public MusicPlayerCoordinatorActor()
{
MusicPlayerActors = new Dictionary<string, IActorRef>();
if (musicPlayerActorReference == null)
{
//create a new actor's instance.
musicPlayerActorReference =
Context.ActorOf<MusicPlayerActor>(user);
//add the newly created actor in the dictionary.
MusicPlayerActors.Add(user, musicPlayerActorReference);
}
return musicPlayerActorReference;
}
65
This actor contains a dictionary MusicPlayerActors object defined as Dictionary<string,
IActorRef>, which contains the instances of the child actors. The string key is the player’s
name, as we would like to have only one MusicPlayerActor per user that plays the song.
We can see that every time a new message is received, this will be forwarded to the subactor
that corresponds to the user playing the song. We also give a user’s name as the name of the
actor.
The MusicPlayerActor has been slightly changed so that it can display information about the
current user. Now this actor contains the user name in the messages displayed.
public MusicPlayerActor()
{
StoppedBehavior();
}
Console.WriteLine(
66
$"{CurrentSong.User} is currently listening to
'{CurrentSong.Song}'");
Become(PlayingBehavior);
}
Now we can finally define the client code and send a few messages to the coordinator actor. We
can see that now the User is specified in the message with the name of the song.
IActorRef dispatcher =
system.ActorOf<MusicPlayerCoordinatorActor>("player-
coordinator");
dispatcher.Tell(new StopPlayingMessage("John"));
dispatcher.Tell(new StopPlayingMessage("Mike"));
dispatcher.Tell(new StopPlayingMessage("Mike"));
Console.Read();
system.Terminate();
}
We can see that we have a very similar output to when we had a single MusicPlayer, except
that now we can handle an unlimited number of users.
67
Figure 22: Result of sending multiple messages to the controller
68
Chapter 8 Actor Path and Actor Selection
In this chapter we are going to discuss the actor path and actor selection. We’ll see how the two
are different, and their relationship with the actor reference, which we have mentioned already.
Actor path
Every time a new actor is created, a unique path is automatically associated to it. When we
create an actor, we can set a name so that the actor is easily recognizable; otherwise, the
framework will automatically add a random name. This is necessary for the actor to have its own
unique path. This is very similar to the actual HTTP URL.
As an example of the music player seen in the previous chapter, let’s simply add some
information in the MusicPlayerActor to display some information about the internals. The parts
in bold are new.
DisplayInformation();
Become(PlayingBehavior);
}
The DisplayInformation method will now be displayed every time a new song is being
played.
69
var dispatcher = system.ActorOf<MusicPlayerCoordinatorActor>("player-
coordinator");
Console.Read();
system.Terminate();
}
akka://my-first-akka/user/player-coordinator/John
We can distinguish the various parts in the actor’s path itself: protocol, actor system, and the
actual path of the actor.
akka://my-first-akka/user/player-coordinator/John
akka://my-first-akka@localhost:8000/user/player-coordinator/John
70
Table 5: Actor's path parts
Actor system my-first-akka The name of the ActorSystem to which the actor
belongs.
Actor selector
Let’s imagine a situation where the two actors have to communicate to each other, but there is
no direct connection between the two—an actor to which we are not holding an actor reference.
In all of the examples we have seen so far, we always had an actor reference, which is a direct
link to an actor.
In order to enable the communication between the two not directly connected actors, Akka.NET
offers the ActorSelection mechanism. So instead of using the IActorRef instance to send a
message to an actor, we can use the ActorSelection and directly reference the actor by using
the actor’s path.
71
Actor Actor Reference Tell() Actor
Figure 25: Actor's communication through Actor Reference and Actor Selection
To keep the previous music player example: let’s imagine that we would like to Log all of the
songs that have been played to get some sort of statistics or user preferences for the songs, so
that the next time the user logs in, we can actually display similar songs or propose the old
songs.
In order to do this, we are going to create a new actor, SongPerformanceActor (only one
instance!), whose responsibility it is to keep track of songs being played, and every
MusicPlayerActor will inform this new actor about the currently played song.
ActorSystem
PlaySongMessage StopPlayingMessage
Child Actors
SongPerformanceActor
72
The new SongPerformanceActor is quite straightforward: the plan is to have only one instance
of it, and it will keep the state. The actor has the SongPerformanceCounter property, which
holds the number of times one song (key) has been played.
Every time a new PlaySongMessage is sent to the actor, we will increase the counter by 1.
public SongPerformanceActor()
{
SongPeformanceCounter = new Dictionary<string, int>();
The whole logic of the actor is placed into the IncreaseSongCounter method. First, we check
whether the song is already in the dictionary, and if not, we simply add it with the count of 1.
Code Listing 54: Change made to the MusicPlayerActor in order to track statistics
73
{
CurrentSong = message;
Become(PlayingBehavior);
}
We can see that in order to send a message, the MusicPlayerActor used the
Context.ActorSelection method. The ActorSelection method accepts the Path of the
actor, which we are supplying. It is possible to supply relative or absolute paths to the method:
How do we know the path? In the Main method, we have given the actor name statistics. As
we are directly creating the actor under the system.ActorOf, this makes it a top-level actor. We
can clearly see this, as the actor is under the /user path directly.
The following code creates this actor and makes it available to be used by other actors.
Code Listing 55: Main code that creates an instance of the SongPerformanceActor
system.Terminate();
}
74
Figure 27: ActorSelection example result
We can clearly see that the statistics are tracked correctly. Another thing to note is the order in
which the messages are displayed. We can see that the SongPeformanceActor displays the
message, but not fully in sequence. This is obviously due to the fact that this is fully
asynchronous and under the hood: since we have three users (John, Mike, and Andrew), three
MusicPlayerActor instances will be created. Tell is not a blocking call!
75
Chapter 9 Supervision
In the previous chapters, we have mentioned several times the fact that actors have
dependencies and can define a supervision strategy. In this chapter, we are going to learn more
about what the supervision is and how it works.
What is supervision?
Supervision describes the dependency relationship between actors. This intrinsically means that
an actor can create other actors and delegate tasks to them. This ability also comes with the
responsibility of handling those subactor’s failures.
The actor that creates other actors is also known as a supervisor, and its children are known as
subordinates. The failures of subordinates will be propagated to the supervisor actor.
In general, there are only a handful of ways to handle failures propagated back; let’s take a look
at them:
Supervisor
76
Stop Stops the subordinate permanently.
Escalate This means the supervisor actor is not handling the error itself,
but the handling of it is escalated to the supervisor’s parent
actor.
The supervisor actor is failing itself!
After looking at these possible options, it becomes very clear that supervision is about forming a
recursive fault-handling structure. Just remember what we have mentioned previously: the
failure-handling is part of the actor model itself!
Root Guardian
User guardian
The user guardian defines the entry point for all of the actors defined by the application
developer. Those are all the actors we are actually discussing in this book, as opposed to the
system guardian, by which actors are actually managed and created by Akka.NET itself.
Therefore, all of the actors created by actorSystem.ActorOf(), and their subsequent
subactors, fall into this section.
77
User Guardian
Supervisor for (user s ) top Level Actors
In the previous chapter we saw that when we create an actor, the path created is under the
/user section. All of the user guardian actors will be placed there.
System guardian
System guardian was introduced in order to enable the shutting down of the system in an
orderly manner. We don’t have to worry about creating the system hierarchy, as the system will
take care of it. System guardian actors will be recognized by the path starting with /system.
Root guardian
Root guardian is the ultimate place where the decision should be made. If the errors are
propagated to the root guardian, it will make sure that the user guardian is shut down (or
handled), therefore bringing down the whole system.
Supervision strategy
Now that we know which directives are available in order to handle the failure conditions, we
have to understand how the parent handles this in terms of code. This is done through
Supervision Strategies.
Whenever we create a new actor, the default supervision strategy is assigned to the actor,
which is to Resume the actor. We obviously can override this behavior.
78
• OneForOneStrategy: This strategy will apply the directive only to the failing actor that
has thrown an error. No other actors are affected.
• AllForOneStrategy: This strategy applies the directive to all of its children. This is
particularly useful if the children actors are dependent on each other, so that the failure
of one brings the logical failure of others.
The example we are going to build is a continuation of the music player we saw in Chapter 7,
where the MusicPlayerCoordinatorActor creates a new child MusicPlayerActor for every
user that plays a song, so that every user has its own MusicPlayer.
Let’s imagine a situation where the song played is not available, and therefore, the
MusicPlayerActor will be in the faulted state. Just for this example, let’s imagine that when the
player tries to play the song “Bohemian Rhapsody,” the SongNotAvailableException is
thrown. The same happens when “Stairway to Heaven” plays, but this time,
MusicSystemCorruptedException gets thrown. This is obviously hardcoded, and just to
demonstrate that the actor is able to throw messages!
Console.WriteLine(
$"{CurrentSong.User} is currently listening to
'{CurrentSong.Song}'");
}
}
}
79
{
public MusicSystemCorruptedException(string message): base(message)
{
}
}
We can see that we need to return a SupervisionStrategy (in our case, the supervision
strategy only acts on the faulty actor), and we also need to specify the Directive.
var dispatcher =
system.ActorOf(Props.Create<MusicPlayerCoordinatorActor>());
80
Console.Read();
system.Terminate();
}
81
Chapter 10 Other Components
EventBus
By definition, in Akka.NET the actors are communicating directly with each other by sending
messages (by using Tell, Ask, Forward, etc.). This is what we can call one-to-one
communication; one actor sends a message to another actor. However, sometimes there is a
need to send information to more than one actor at a time (one-to-many), where an individual
actor sends a message to a group of actors. In effect, this means that Akka.NET offers a
publisher-subscriber mechanism out of the box.
The ActorSystem uses the EventStream for a number of internal things, including logging,
sending dead letters, and cluster events.
An Akka.NET, both system and user-generated events (messages) can be published by using
the EventStream. EventStream is the simplest and most common implementation of an
EventBus.
The scenario is: One actor publishes a message (of some type) to the EventBus, while on the
other side, one or more actors are subscribed to the events published to the EventBus of a
particular type. Subscribers will automatically receive messages of that particular type as soon
as the message is published.
Note: EventStream only works within one ActorSystem context. This means that
the messages won’t be available on another node (server).
We are creating two actors: BookPublisher, responsible for publishing new books, and
BookSubscriber, which will receive a message every time a new event is published on the bus.
The implementation of the BookPublisher is interesting, as it uses the
Context.System.EventStream.Publish() method to publish the message, meaning that the
EventStream is available and accessible within an actor.
BookSubscriber is not any different from the actors we already checked. It just handles the
receiving of the NewBookMessage type.
The NewBookMessage type is a simple class that only contains the name of the book we are
publishing.
82
Code Listing 59: Publisher/Subscriber example
The client code contains some new methods we haven’t seen yet. In the Main method, after
creating an ActorRef, we are subscribing that actor to the event stream, and in specific cases,
to receive the NewBookMessage types only.
We are using the Subscribe method directly in the Main function; however, this could be used
directly when the actor gets instantiated, by using either the actor’s constructor or PreStart
method.
83
Code Listing 60: Main method enabling publishing and subscribing to messages
system.EventStream.Subscribe(subscriber1, typeof(NewBookMessage));
system.EventStream.Subscribe(subscriber2, typeof(NewBookMessage));
Console.Read();
system.Terminate();
}
In the output, as we might expect, both subscriber actors will get the two published messages.
DeadLetters
There is a possibility that messages are not delivered to an actor. Those messages will be
automatically delivered to a special actor called DeadLetters, which is available at the
/deadLetters path. This rule usually applies to the nontransport lost messages, which means
that Akka.NET makes no guarantees for lost messages at the transport layer.
Every time an actor terminates, there is a chance that some messages will be lost. If the actor is
not available, and other actors are sending messages to it, those messages will end up in the
DeadLetters mailbox.
84
How can we monitor DeadLetters?
An actor can subscribe to class Akka.Event.DeadLetter on the event stream. The subscribed
actor will then receive all dead letters published in the (local) system from that point onwards.
As dead letters are not propagated over the network, we would need to create an instance of
the subscriber on each node.
Console.WriteLine(msg);
}
}
system.EventStream.Subscribe(deadLettersSubscriber,
typeof(DeadLetter));
echoActor.Tell(PoisonPill.Instance);
echoActor.Tell("Hello");
Console.Read();
system.Terminate();
}
85
There are two actors—one is called DeadLetterMonitor, whose responsibility is to subscribe
to and handle the DeadLetter messages. In order to get the DeadLetter messages, the actor
has to subscribe to the EventStream, and specify the type of messages it would receive (in our
case, the DeadLetter type). Don’t forget that the DeadLetter type is part of the Akka.Event
namespace, so it has to be declared with using Akka.Event.
In order to simulate the unsuccessful sending of a message, we are going to create an instance
of an EchoActor, which we will stop just before sending the first message. In this case, the
system is going to try to send a message to a stopped actor, and as we have seen, this is not
possible, and therefore the message will end up being captured by the DeadLetterMonitor.
We can clearly see that the message did get delivered to the DeadLetterMonitor actor. We
can also see that akka://dead-letter-example/deadLetters is the actor sending the
message.
86
Chapter 11 Unit Testing Akka.NET
It’s hard to imagine modern application development without the possibility of performing unit
tests, or testing in general. Automated tests are not only an important part, but also an essential
part of any application development cycle.
As testing actors is a bit different from testing any other piece of object-oriented software that
we’re used to, Akka.NET comes with a dedicated module, Akka.TestKit, for supporting tests
at different levels.
Akka.TestKit is the fundamental library to be used in order to perform tests. In addition to this,
there are different libraries built on top of it in order to support several other unit-testing
frameworks, such as Xunit, NUnit, and Visual Studio tests.
Akka.TestKit
Including these libraries is as easy as installing them in the usual way from the NuGet package
manager console.
Code Listing 62
The TestKit class is the base for all of the unit tests, and all the unit test classes should inherit
from it. TestKit class is actually implemented by the various libraries supporting the specific
testing framework—as the real base class we have the Akka.TestKit.TestKitBase class,
which is implemented in the Akka.TestKit library.
We would usually never inherit directly from the Akka.TestKit.TestKitBase, since there
would be quite a few things to be implemented, and indeed, every library specific to the testing
framework in use (for example, NUnit) would implement the plumbing needed to work with the
actor system hiding this complexity from us.
87
Akka.TestKit -> TestKitBase
[TestFixture]
public class CalculatorActorTest: Akka.TestKit.NUnit.TestKit
{
[Test]
public void SomeTest()
{
/* test goes here */
}
}
If you are not familiar with [TestFixture] and [Test], these are the two attributes used to
mark tests with NUnit, which we are going to use in the examples. You might use your favorite
testing framework equally; the concepts are usually very similar.
• Direct: Unit tests are written in the same way as normal classes. However, this method
doesn’t use any actor system, and therefore is limited in the sense that when performing
direct tests, we are completely avoiding the message passing, which is the reason why
we use the actor system. On the other hand, this kind of testing allows us to inspect the
state of the actor, so it’s very useful.
• Unit Testing: Unit tests are written by using the ActorSystem, and performed against
one actor in particular.
• Integration Testing: We can use this technique when there is a need to test multiple
message passing between actors.
• Mixed Mode: Unit tests that use the unit testing/integration testing with the help of the
direct testing.
88
Direct testing
Performing direct testing is pretty straightforward, and not any different from what we usually do
when testing any other kind of class. There is one thing in particular that we need to be careful
about, which is to mark all of the message handlers as public, so that they’re accessible to the
unit test code. At first, this might sound like a security issue, but in reality, when actors are used
within the actor system, these public methods are never accessed outside the actor itself.
Let’s use our SongPerformanceActor as the actor to be tested. If you recall, this actor was
used to increase the count of how many times a song has been played. So, this actor has a
state, and this state can be tested. As a reminder, this actor looks like the following. Please note
that we changed the SongPerformanceCounter property to public, together with the
IncreaseSongCounter method.
public SongPerformanceActor()
{
SongPeformanceCounter = new Dictionary<string, int>();
We changed the private/protected methods to public so that they can be accessed by the unit
test.
Our first test to check that the SongPeformanceCounter doesn’t contain any entry upon an
actor’s creation would look as follows:
[TestFixture]
89
public class SongPerformanceActorTest: Akka.TestKit.NUnit.TestKit
{
[Test]
public void ShouldStartWithAnEmptyState()
{
var actor = new SongPerformanceActor();
Assert.False(actor.SongPeformanceCounter.Any());
}
}
Nothing really difficult here. We instantiate a new actor, and simply check that there are no
entries in the SongPerformanceCounter property.
The next thing to test would be increasing the count after playing a song. To achieve this, we
would simply use the IncreaseSongCounter method and assert the result once again.
Let’s add a new test method and invoke the IncreaseSongCounter method. We are asserting
that the counter was set to 1 after “playing” the song once. Needless to say, this test passes.
Code Listing 65: Check whether the statistics are increased by one
[Test]
public void ShouldIncreaseSongByOne()
{
var actor = new SongPerformanceActor();
actor.IncreaseSongCounter(songMessage);
Assert.True(actor.SongPeformanceCounter[songMessage.Song] == 1);
}
This is very direct, and not at all different from what we already know about unit testing.
UnitTesting
A unit test is about testing the actor using the messaging involved by using the actor system.
Now things are getting more complicated, and we need to introduce few concepts first.
When executing tests, the base TestKit class will create a test actor system in the background,
referred to as Sys. Being an actor system, it will allow us to instantiate new actors, as we have
seen in previous chapters, by using ActorOf. There is an alternative to this method without
Sys.ActorOf, which is to use ActorOf<T>. Let’s see a quick example of the ActorOf usage.
[Test]
90
public void ShouldIncreaseSongByOne()
{
//var actor = base.Sys.ActorOf(new
Actor.Props(typeof(SongPerformanceActor)));
IActorRef actor = ActorOf<SongPerformanceActor>();
actor.Tell(songMessage);
Assert.True(actor.SongPeformanceCounter[songMessage.Song] == 1);
}
However, we can quickly notice that the SongPerformanceCounter class attribute is not any
more visible! Unfortunately, this test cannot be performed, so we need to use the
ActorOfAsTestActorRef<T> method. This is a wrapper method, and it has the
UnderlyingActor attribute, which represents the underlying actor’s instance, so that we can
access its internal properties.
[Test]
public void ShouldIncreaseSongByOne()
{
TestActorRef<SongPerformanceActor> actor =
ActorOfAsTestActorRef<SongPerformanceActor>();
actor.Tell(songMessage);
Assert.True(actor.UnderlyingActor.SongPeformanceCounter[songMessage.Song]
== 1);
}
91
Code Listing 68: Addition of the Sender.Tell method
Let’s see how can we prove that this message is sent back to the sender.
Akka.TestKit offers a set of assert helper methods such as ExpectMsg, ExpectMsgFrom, and
ExpectNoMsg, in order to test that the message is sent back from an actor. In our test example,
we can see the ExpectMsg and the commented-out ExpectMsgFrom in action.
[Test]
public void ShouldResendCounterIncreasedMessage()
{
TestActorRef<SongPerformanceActor> actor =
ActorOfAsTestActorRef<SongPerformanceActor>();
actor.Tell(songMessage);
/*
* specify the actor explicitly
* var replyMessage = ExpectMsgFrom<CounterIncreasedMessage>(actor);
92
*/
var counter =
ExpectMsg<CounterIncreasedMessage>(TimeSpan.FromSeconds(5));
It’s worth noticing that the ExpectMsg returns an instance of the CounterIncreasedMessage for
further inspection and assertion. By default, the method will wait for three seconds, but this can
be overridden as needed (TimeSpan.FromSeconds(5)).
ExpectNoMsg will test the fact that no messages are received, which would work fine in our
previous example, before we decided to send back the message.
ExpectMsgFrom will test that the message comes from a specific actor.
Integration testing
In this section we are going to see how to check the parent-child relationship between two
actors. If you recall, with MusicPlayerCoordinatorActor and MusicPlayerActor, the first will
receive the message and create a new instance of the MusicPlayerActor for every user that
plays music. The new child actor will be given the name of the user itself.
public MusicPlayerActor()
{
Receive<PlaySongMessage>(m => PlaySong(m));
}
93
Code Listing 70: Simplified version of the MusicPlayerCoordinatorActor for testing
public MusicPlayerCoordinatorActor()
{
MusicPlayerActors = new Dictionary<string, IActorRef>();
One aspect of the testing we might tackle is the creation of the child actor. How can we ensure
that the child actor is created after we send a message to the MusicPlayerCoordinator actor?
One way is to use the ActorSelection as follows:
[Test]
public void ShouldInstantiateANewChildActor()
{
TestActorRef<MusicPlayerCoordinatorActor> actor =
ActorOfAsTestActorRef(() => new MusicPlayerCoordinatorActor(),
"Coordinator");
94
actor.Tell(songMessage);
Assert.That(child != null);
}
Since we know that every actor has a Path and can have a name specified, we are specifying
the coordinator’s actor name explicitly, as it is going to help us to define the final path. When
creating the MusicPlayerCoordinatorActor, we are telling the test system to call it
Coordinator.
We also already know that when the new child actor gets created, it will have the name of the
user playing the song, so we know which Path the child actor will have upon instantiation, and
we are going to use this information. In our case, this is John.
In order to figure out whether the child got created, we use the ActorSelection object,
specifying the direct path to the child actor and checking for its existence. The resolution of the
actor will take up to five seconds, and if the actor is not created in this timeframe, the test will
throw an error and fail.
95
Chapter 12 Akka.NET Routers
So far, we have seen a single usage of actors: creating one actor at a time, and sending
messages to and from actors. However, there are use cases where we would like to scale out
the application and be able to have a responsive application, even under a heavy load.
Akka.NET provides special actors, called routers, that have the ability to group together actors
and distribute a single message to several actors by applying different strategies. As a
consequence of this, we can build systems that can perform as many operations simultaneously
as possible.
In this chapter, we are going to explain how to work with the most common routers:
• Round Robin
• Random routing
• Shortest Mailbox queue
• Consistent Hashing
We can specify the router that an actor will use at the time of the creation of that actor. Props
object contains a method WithRouter that can be used to specify it.
Round Robin
The Round Robin router is defined by the class RoundRobinPool, and is part of the namespace
Akka.Routing. The Round Robin router can be configured to contain a number of instances of
actors we would like to support in our application. Every time a new message arrives, the router
will make sure that the next actor in the list will receive that message. This is particularly useful
when we would like to balance the amount of work each actor needs to perform.
In Figure 35, we can see that the sender sends five messages, but we have configured the
RoundRobinPool to contain only four actors. This would mean that Actor 1 will receive Message
1, Actor 2 will receive Message 2, Actor 3 will receive Message 3, and Actor 4 will receive
Message 4. The next message (Message 5) will simply start from Actor 1, and so on, in a loop.
96
5 1
Actor 1
2
5 4 3 2 1
Actor 2
Sender Router 3
Actor 3
4
Actor 4
To demonstrate this with the code, we will use the CalculatorActor we have seen previously,
but modify it to include the debugging info so that we can follow which actor instance received
which message.
Code Listing 75 is the Main method that contains the code that creates the CalculatorActor
.WithRouter(new Akka.Routing.RoundRobinPool(4)) to define the RoundRobin router, and
in addition, sets up four instances of CalculatorActor to be created and used.
97
.WithRouter(new
Akka.Routing.RoundRobinPool(4));
Console.WriteLine($"Result 1 : {result1.Value}");
Console.WriteLine($"Result 2 : {result2.Value}");
Console.WriteLine($"Result 3 : {result3.Value}");
Console.WriteLine($"Result 4 : {result4.Value}");
Console.WriteLine($"Result 5 : {result5.Value}");
Console.Read();
system.Terminate();
}
Running this code returns the following output, where we can clearly see that the fifth message
was delivered to the actor named $a, which was also the first actor in the sequence.
Notice that the names of actors are automatically generated, while the name calculator,
which we set when setting up the router, becomes the parent. RoundRobinPool doesn’t allow
you to set up custom names for these actors.
98
Random routing
Random routing probably defines the easiest way of routing, where the messages are sent
randomly to the available actors, and therefore without any order. Random routing is defined by
the RandomPool class, which, among other parameters, accepts the number of instances to be
used.
As shown in Figure 37, the messages can be delivered to any actor at any given time. It’s worth
pointing out that one message goes to only one actor.
2
Actor 1
1
5 4 3 2 1
Actor 2
Sender Router 4
Actor 3
5 3
Actor 4
To use the Round Robin example, we will slightly change the previous example so that we use
RandomPool rather than RoundRobinPool.
…
}
If we run the application, we can see that in the output, only the actors $a and $c have received
the messages, in a random order.
99
Figure 38: Random router output
To speed up this process, Akka.NET offers a mechanism so that the message will be sent to the
actor whose MailBox contains fewer items in its queue. This way, the message can be
processed as quickly as possible. In Figure 39, we can see that the router will choose Actor 2 to
process the next message.
MailBox
Actor 1
1
MailBox
3 2 1
Actor 2
Sender Router
MailBox
Actor 3
MailBox
Actor 4
We leave the example as it is, and only change the router, as follows:
100
Code Listing 77: Shortest Mailbox router
…
}
Consistent Hashing
Consistent Hashing is a very interesting router, and a bit different from the previous ones we
have seen. Instead of choosing a random router, we want to consistently send the messages
with some characteristics, always to the same actor. The decision is based on the hashing of
the message, or better, of the chosen property of the message. Consistent Hashing is
implemented by the ConsistentHashingPool class, for which we can override the default
hashing mechanism by using the WithHashMapping method, as follows:
using Akka.Routing;
return x;
}));
101
calculatorRef.Tell(new Add(14, 25));
Console.Read();
system.Terminate();
}
if (!(Sender is DeadLetterActorRef))
Sender.Tell(new Answer(add.Term1 + add.Term2));
}
…
}
In this example, we are choosing the Term1 property to be hashed, so the actor to be used will
depend on the hash value of the Term1 property. Because we are using Tell, we had to slightly
change the handling of the message in the CalculatorActor so that it doesn’t return a
message when the Sender is not an actor.
By executing this code, we can see that the Actor for Term1 = 100 is always $a.
The risk with this strategy is that one of the routers might take most of the load, depending on
the messages—if most of the calculation performed is with number 100, then mostly only one
actor will be used, which might not give optimal results.
102
Chapter 13 Actors in ASP.NET Core
The integration of Akka.NET with an ASP.NET Core application is relatively simple. As you
might expect, ASP.NET Core is needed to expose an HTTP endpoint to the actor system, and
we then can place all of the business logic inside actors, as we have seen in various examples.
Since ASP.NET Core is a relatively new framework built to run on multiple platforms, it’s a
perfect fit for our example, as we are going to demonstrate that it’s possible to use Akka.NET in
a .NET Core application.
Our application won’t be too difficult, but good enough to demonstrate the integration between
the two frameworks. We are going to reuse one of the actors we already created, the
CalculatorActor, and expose the addition as a RESTful service.
We won’t be going into the details of ASP.NET Core, as the expectation is that you already
know the basics.
The next step is to choose which kind of .NET Core application we want to create. In our case,
this is Web API. We will also choose .NET Core and ASP.NET Core 2.0 as the running
framework.
103
Figure 42: Choosing the right .NET Core project
First, we should reference Akka.NET from NuGet. So, let’s run the following command in the
Package Manager Console:
104
Actor definition
Let’s remind ourselves of what our actor looks like. The actor receives the AddMessage, and it
sends back to the Sender the AnswerMessage, which is the result of the calculation.
ActorSystem integration
First, we need to find a place to instantiate the ActorSystem. We know that the creation of the
ActorSystem is not cheap in terms of time and resource usage, so we want to do it only once,
and reuse it throughout the whole application.
In order to do this, we can use the Startup.cs, which is an integral part of the ASP.NET Core
template, and has already been created for us.
105
We will register the ActorSystem with the Service collection. The Startup class contains the
ConfigureService method. After creating an instance of the ActorSystem, we will add it to the
list of services, but as a singleton (AddSingleton method).
The statement (serviceProvider) => actorSystem means that every time the instance is
requested, the actorSystem instance that we already created will be used.
Controller definition
We are going to create a new controller called CalculatorController.
CalculatorController only has one method, called Sum, which can be accessed by using the
GET HTTP method. The Sum method has two parameters, x and y, which correspond to the two
numbers we would like to sum up. The content of the Sum method is already familiar. We
instantiate the CalculatorActor, construct the AddMessage, and Ask the actor to return the
result of type AnswerMessage.
As we have registered the ActorSystem as part of services, we can now inject it to the
controller via a constructor.
[Route("api/[controller]")]
public class CalculatorController : Controller
{
private ActorSystem _actorSystem;
[HttpGet("sum")]
public async Task<double> Sum(double x, double y)
{
106
var calculatorActorProps = Props.Create<CalculatorActor>();
var calculatorRef = _actorSystem.ActorOf(calculatorActorProps);
return answer.Value;
}
}
When running the application in the browser (by pressing F5 in Visual Studio), we can now
navigate to the <host>:<port>/api/calculator/sum?x=1&y=2 page to see if the method
returns the correct answer. As we can see in Figure 44, the correct answer is returned.
One thing to pay attention to is the following: In the previous example, we are creating a new
actor instance every time the Sum method is called, which is not that great, as it will consume
memory, and those actors will be simply hanging around.
One workaround would be to try to get the instance of the actor via ActorSelection, but this
also might lead to issues in a highly concurrent environment. A naïve implementation using
ActorSelection would be:
[HttpGet("sum")]
public async Task<double> Sum(double x, double y)
{
IActorRef calculatorRef;
try
{
calculatorRef = await
_actorSystem.ActorSelection("/user/calculator")
107
.ResolveOne(TimeSpan.FromMilliseconds(100));
}
catch (ActorNotFoundException exc)
{
var calculatorActorProps = Props.Create<CalculatorActor>();
calculatorRef =
_actorSystem.ActorOf(calculatorActorProps,"calculator");
}
return answer.Value;
}
In this example, we are trying to get an instance of the actor via ActorSelection by specifying
the path, and if not found, we would create one and carry on. But, as I mentioned, there is a
possibility that two concurrent ASP.NET Core threads will attempt to perform the actor selection
at the same time, and to create an instance of the new calculator, which then would lead to
errors. So, even though the version in Code Listing 83 would work, there is a potential risk that it
would fail.
If we want to have only one actor serving all the requests, a better implementation would be the
following:
108
We will create a wrapper class around the actor itself. The logic stays the same as we had it
previously in the controller. This new class will be registered in the ASP.NET Core services list
as a singleton item, as follows:
typeof(CalculatorActorInstance));
Now we need to change the controller in order to inject the actor itself, rather than the
ActorSystem.
The new version of the controller is much simpler, and it contains less code.
[Route("api/[controller]")]
public class CalculatorController : Controller
{
private readonly ICalculatorActorInstance CalculatorActor;
[HttpGet("sum")]
public async Task<double> Sum(double x, double y)
{
var answer = await CalculatorActor.Sum(new AddMessage(x, y));
return answer.Value;
}
}
109
Now we are pretty certain that there will be only one actor serving the requests. This will also
allow us to better control the number of actor instances. We can choose to create not one
instance of the CalculatorActor within the ICalculatorActorInstance implementation, but
many, and have a pool of actors to serve the Sum method.
110
Chapter 14 Akka.NET Remoting
In the previous chapters, we saw the creation of actors belonging to only one ActorSystem and
the communication between them. Being part of one ActorSystem automatically means that the
application is hosted and running as part of one single process, which is a single application
space.
While this is perfect for some kinds of applications, it might not be for others that need to scale
out in order to increase the overall processing capabilities. This is where we need Akka.NET
Remoting.
Remoting overview
Akka.NET remoting enables communication between ActorSystems deployed on different
application processes. This might include a different process on a single machine, or a
completely different server.
Let’s see an example. As shown in Figure 45, we have two actor systems deployed: one
running on the client host, and one on the server host.
The communication between the two will happen via the network, and all of the messages sent
across the wire will be serialized and deserialized. In order for the serialization to work, both
actor systems should have the common definition of the messages (for instance, by sharing a
library with message classes).
In addition to this, the communication between actors on two hosts will happen by using the full
Path, which comprehends the protocol, address, port, and actor’s path. We have discussed the
actor path in previous chapters.
Transport over
network
(TCP)
Client Host Server Host
(well known IP address)
ActorSystem akka.tcp://actorsystem@192.158.2.1:9000/user/a1
ActorSystem
akka.tcp://actorsystem@192.158.2.5:8000/user/a2
111
The client host (as shown in Figure 45) would typically be responsible for initiating the
communication with the server host. The client host is also responsible for the creation of new
actor instances.
With the use of remoting, the client host can choose where to instantiate new actors: either on
the client (the local machine) or on the server host (remotely). To specify where the actor is
going to be created, we use either the HOCON notation in the configuration file, or do that
directly in the code.
The great thing about this is that the IActorRef returned upon creating an actor will not change
its behavior, which means that our code will work in the same way as if the actor were running
locally. This ability of the IActorRef to abstract the location of the actor is also known as the
location transparency.
Transport
By default, Akka.NET uses the DotNetty library for the TCP transport. The responsibilities of the
transport in Akka.NET are as follows:
• IP addressing and ports: All Akka.NET endpoints (client and servers) have to be
accessible by an IP address and port.
• Message delivery: Should enable the delivery of messages from ActorSystem A to
ActorSystem B.
• Message framing: Distinguishes individual messages within a network stream.
• Disconnect and error reporting: Tells Akka.Remote when a disconnect or transport
error occurred.
• Preserving message order (optimal): Some transports (like UDP) make no such
guarantees, but in general, it's recommended that the underlying transport preserve the
write order of messages on the read side.
• DNS resolution (optional): Be able to resolve the DNS.
As the demo application, we will expand the previously built ASP.NET Core calculator web
application by changing it a bit, as follows:
1. We will add a new Console Application project that will be used to host the remote
ActorSystem, and execute the code as requested by the client.
2. We will create a new Common Code library, which is a library that will be shared by the
two other applications.
112
ASP.NET Core Console
(Client) Common Code Application
(Messages, (Server)
Actors, etc)
Let’s start by creating the two needed projects, which are to be added to the already existing
ASP.NET Core solution.
As we can see in Figure 47, we are creating a .NET Core Library project called
Akka.Net.Succinctly.Core.Common, and in addition to that, we need to create a .NET Core
Console Application project called Akka.Net.Succinctly.Core.Server.
113
Now our Visual Studio Solution should contain three projects.
We will start by adding the reference to the Akka library from NuGet.
We also add the following code, which defines our CalculatorActor, AddMessage, and
AnswerMessage classes. In bold, we have added the console logging of the currently
processing message, which is important for debugging purposes, in order to see if the server
received any message to be processed.
114
{
Value = value;
}
We will add another class that will be shared across the two solutions (client, server) and is
responsible for reading out the Akka.NET configuration from the file. The HoconLoader class is
a very simple class that reads the content of a file and returns the
Akka.Configuration.Config object, which contains the object model for the HOCON string.
Akka.NET server
External references
The server is probably the simplest of the three projects, as it only hosts the ActorSystem.
We will start by adding the references to Akka and Akka.Remote from NuGet.
Code Listing 90: Installing NuGet packages required for Akka remoting
115
In addition to this, we reference the already-created Akka.Net.Succinctly.Common project.
Configure remoting
The next step is to configure Akka to allow remote connections. In order to do this, we will
create a new file in the console application and call it akka.net.hocon, where we place the
necessary HOCON configuration for the remoting.
Do not forget to set the properties for this file in Visual Studio as:
akka {
actor {
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
}
remote {
dot-netty.tcp {
port = 8888 # bound to a specific port
hostname = localhost
}
}
}
The next section to be configured is remote. Here we have to define the protocol supported for
accessing this server application, the port, and the hostname. Dot-netty.tcp is the library that
supports the remoting channel on the TCP protocol and the creating of sockets, in effect
enabling the communication between the two actor systems. If you’d like to know a bit more
about DotNetty, visit the official website.
Port = 8888 defines the port at which the server will be responding. Being a server, this port
has to be well known, and the client calling the server has to be aware of it.
The entry point for the Server application simply loads the content of the akka.net.hocon file
and injects the configuration settings into the ActorSystem at the time of the creation (Create
method).
116
Code Listing 87: Server’s main function definition
Console.WriteLine("Server started");
Console.Read();
system.Terminate().Wait();
}
Now we can run the Server (start the server application by pressing F5). As we can see in
Figure 50, the application now listens to Port 8888, just as we have configured it.
Another important thing to notice is that the full path to the actor system is mentioned, which is
akka.tcp://server-system@localhost:8888. This is very important because the client will
need to know the full address as defined by the server.
Akka.NET client
We have already mentioned that the client is our ASP.NET Core application, which we also
have to configure to allow remoting.
External references
As we did for the Server, we need to reference the Akka and Akka.Remote libraries from
NuGet.
Code Listing 93
117
Configure remoting
The next step is to configure Akka to allow remote connections. In order to do this, we will add a
file to the project and call it akka.net.hocon. As we did for the Server, this file will contain the
configuration of the client ActorSystem.
In Visual Studio, do not forget to set the properties for this file as:
akka {
actor {
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
deployment {
/calculator {
remote = "akka.tcp://server-system@localhost:8888"
}
}
}
remote {
dot-netty.tcp {
port = 0 # bound to a dynamic port assigned by the OS
hostname = localhost
}
}
}
The actor provider part looks exactly the same as when we configured the server by setting the
RemoteActorRefProvider as actor provider.
The interesting part is the fact that we have to configure the deployment section. In the
deployment section, we can place the configuration per actor (name) and define where remotely
we would like to run this actor. So, /calculator is the name of the actor (no need to specify
/user before it), and we are instructing Akka to instantiate this actor on a remote system
defined by the path. We can also specify multiple actors to point to multiple servers.
Code Listing 89: Specifying more than one actor in deployment section
actor {
provider = "Akka.Remote.RemoteActorRefProvider, Akka.Remote"
deployment {
/calculator {
remote = "akka.tcp://server-system@localhost:8888"
}
/actorXXX{
remote = "akka.tcp://server-system@system2:8888"
118
}
}
}
The next section to be configured is remote. Again, as we did for the Server, we are
configuring dot-netty.tcp as the transport protocol, with the difference of the Port. We don’t
have to specify the port for the client, since the port will be automatically assigned by Akka and
transmitted to Server so that Server knows where to answer back. So, port = 0 means
dynamic address.
The only part that really changes—and this is the beauty of this solution—is the configuration of
the actor system. We need to change the ConfigureServices method in the Startup class so
that it includes the reading of the HOCON configuration, and inject that into the ActorSystem
creation method.
Code Listing 90: Configuration of the client—Injecting HOCON config into the ActorSystem
Before running the application, we have to Visual Studio up so that it starts the server before
starting the client project. To do so, right-click on the solution name, and choose Properties. As
shown in Figure 51, we select the Multiple startup projects option and set
Akka.Net.Succintly.Core.Server and Akka.Net.Succinctly.Core.WebApi to Start.
119
Figure 51: Setting up multiple starting projects
Now we are ready to run the application—press F5 in Visual Studio. The server and the browser
will open, and we will be ready to query our Calculator controller.
By placing the console application (server) and the browser (client) side by side, we can see
that every time we query the browser, the console will show an entry. This is evidence that
communication between the two systems is working properly.
120
Final Words
In this book, which was a great challenge for me, we have touched upon some of the most
important aspects of the Akka.NET framework:
Unfortunately, the format of this book didn’t allow me to talk about Akka.NET persistence,
Akka.NET clustering, and Akka.NET streams, which are some more advanced topics that would
enable the creation of real enterprise-scale applications, fully scalable and persisted. That said,
with the baseline provided in this book, you can learn those topics without big issues, since they
are based on the same concepts we have already seen.
As with other books in the Syncfusion Succinctly series, what is mentioned in this book should
give you a good starting point not only for designing an application, but also to start exploring
more advanced options on your own.
Thank you for reading this book—I hope I’ve managed to fulfill the expectations you might have
had when you started it. It certainly helped me to clear up some ideas of my own about the
framework: I learned a lot about this fantastic framework during this journey.
121