In the .NET community perhaps the Actor model of computing is not all that familiar. In this post I will briefly describe my understanding of the possibilities it gives the “real life” Enterprise LOB-application developer without going into specifics.
Different abstraction
The Actor model is a different abstraction from the normally more machine-like multithreaded server process model. This model sees the software system as a set of Actors listening for incoming messages. Messages are sent using to mailing addresses, conceptually, as in – not a hard reference to a running piece of code of any kind. An actor can only send messages to other actors that it knows the address of. It will know only of actors it receives messages from or creates itself. Unintuitively, the communication between actors is handled through a concept known as Channels, which would imply something more direct than a mail-drop system. In common implementations today, channels are implemented as queues.
The Actor will react to the incoming message by either sending a finite number of messages, creating a finite number of actors as well as deciding how to handle the next incoming message. The messages contain all the information needed for this processing, so there is no global state to modify or any shared resources to wait for. Each actor has its own execution context. Whether you execute all actors in the same thread in the same process or whether you have tens of thousands of separate machines with a multitude of threads of execution in several processes does not matter to the actor. This leads to a fantastic potential for scalability as there is little or no negative coupling.
Real-world examples?
If you think about it more closely, the Actor model can be said to better model the organizations we are building systems for. If a process requires a certain number of steps to be taken, modelling the steps, or actually the coworkers performing the step, as actors and the piece of paper or file that would travel from desk to desk can be modelled as a message. Just like faceless corporate drones, actors can be stateful, as is evident from the definition above.
In the Actor model data is not encapsulated but rather transmitted as messages between actors that perform actions with that data and then send the information forward. In object oriented programming you still send messages, conceptually, but actually you call methods on the object, but the objects are one with their data and the only way to modify the data is by sending a message to the object and hope the object will allow itself to be modified in the way you want.
There are large practical similarities between classic object oriented programming and the Actor model, and the Actor model frameworks implement actors as “objects” and use the OOP toolbox to implement the infrastructure that the frameworks provide, but the conceptual differences are importan remember when working with this model.
What frameworks exist for .NET?
There are two well known Actor model framework for .NET, Retlang (http://code.google.com/p/retlang/) and Stact. They both provide the basics you can expect from a framework of this kind, only Retlang stray somewhat from the Actor model nomenclature and provide no remote messaging capability, focusing on high performance in-memory messaging. It is unclear how development proceeds with Retlang, but there is support for .NET 4.
Another framework that supports Actor model is Stact https://github.com/phatboyg/Stact) which uses concepts more closely linked to the specification and would be preferable by those who like conceptual clarity.
Recently, F# has introduced the concept of F# Agents, which is essentially actors. They have isolation between instances (actors) and respond asynchronously to incoming statically typed messages. (http://www.developerfusion.com/article/139804/an-introduction-to-f-agents/)
We will proceed and show a brief example using F# as it is less verbose than other ways of implementing this architecture. The examples are slightly modified from the above link and also ripped straight from the horse’s mouth, i e Don Syme’s post http://blogs.msdn.com/b/dsyme/archive/2010/02/15/async-and-parallel-design-patterns-in-f-part-3-agents.aspx
open Microsoft.FSharp.Control
type Agent<‘T> = MailboxProcessor<‘T>
let agent =
Agent.Start(fun inbox ->
let rec loop n = async {
let! msg = inbox.Receive()
printfn “now seen a total of %d messages” (n+1)
return! loop (n+1)
}
loop 0 )
for i in 1 .. 1000 do
agent.Post (sprintf “Hello %d” i )
The above agent uses a builtin class called MailboxProcessor that allows you to post messages, passing in the actual Processor function as an argument, “putting the fun back into functional”.
Of course this is a silly example that doesn’t make anybody happy. Let’ s show something more network-related, so that you can imagine the implications.
open System.Net.Sockets
/// serve up a stream of quotes
let serveQuoteStream (client: TcpClient) = async {
let stream = client.GetStream()
while true do
do! stream.AsyncWrite( “MSFT 10.38″B )
do! Async.Sleep 1000.0 // sleep one second
}
What is the point of this then? Well, the agents are asynchronous and take no resources while waiting. You might as well create a bunch of actors that will all lie there waiting for an incoming request and deal with them asynchronously with mindblowing efficiency. Every Actor (or instance of Agent) has its own state completely isolated from the others. See? No problems with shared mutable state. Put the foot on the accelerator and press it all the way to the floor.
Error management in Actors
Below is an example of error management using a supervisor that will take care of your vast pool of actors and deal with their failures. Again, contrived example, but remember you have all of the .NET framework at your disposal, so just imagine the supervisor doing some really clever reporting or cleanup.
open Microsoft.FSharp.Control
type Agent<'T> = MailboxProcessor<'T>
module Agent =
let reportErrorsTo (supervisor: Agent<exn>) (agent: Agent<_>) =
agent.Error.Add(fun error -> supervisor.Post error); agent
let start (agent: Agent<_>) = agent.Start(); agent
let supervisor =
Agent<int * System.Exception>.Start(fun inbox ->
async { while true do
let! (agentId, err) = inbox.Receive()
printfn "an error '%s' occurred in agent %d" err.Message agentId })
let agents =
[ for agentId in 0 .. 10000 ->
let agent =
new Agent<string> (fun inbox ->
async {
while true do
let! msg = inbox.Receive()
if msg.Contains("agent 99") then
failwith "I don't like that cookie!" }
)
agent.Error.Add(fun error -> supervisor.Post (agentId, error))
agent.Start()
(agentId, agent) ]
for (agentId, agent) in agents do
agent.Post (sprintf "message to agent %d" agentId )
Conclusion
The implications that arise from the Actor model and F# Agents in particular is that where you can use messaging (you are free to use F# Type Providers to make your various XML WebService packets become F# Types) you can process them with great efficiency to trigger all sorts of .NET functionality asynchronously. You could use F# Agents to back your WebApi services to make the code more concise and responsive. There is no end to the possibilities.
For further reading, please don’t hesitate to follow Don Syme at http://blogs.msdn.com/b/dsyme/ and the various blogs and documentation available through there.