Developed by MS Open Tech, a subsidiary of Microsoft, ActorFx intends to offer a “non-prescriptive, language-independent model of dynamic distributed objects.” The model is divided into two components, the actor itself and its data storage. These are represented by the interfaces IActor and IActorState.
State Management with IActorState
Originally IActorState was a very simple interface. It is essentially a dictionary that exposes Set, Get, TryGet, Remove, and Flush. The last operation, which can be run asynchronously, is used to commit changes. By convention, changes are stored in a “local side-effect buffer” until commit is called, at which point they should be atomically saved to a durable store.
This simplicity hides a secret, IActorState can be sued to store anything including active code.
Actor Methods
Prior to using an actor, that actor must be taught what it can do. Unlike traditional .NET programming, this isn’t done by manually deploying code to the server. Instead, methods are pushed to the actor at run time using the IActor.AddAssembly function. The assemblies are represented as a byte array so they can be transmitted over the wire and stored inside the IActorState itself.
This means each actor is a fully encapsulated object. It doesn’t inherit methods from a class or prototype, it owns them itself. And those methods can be changed at any time simply by pushing a new assembly with the same name.
Since they are loaded an assembly at a time, actual actor methods need to be tagged using the ActorMethod attribute. Methods so tagged must adhere to the delegate pattern Func<IActorState, object[], object>.
Sending Messages to Actors
IActor.CallMethod is used to send messages to an actor. IActor.CallMethod accepts a string that represents the name of a method from one of the previously added assemblies along with an array of parameters. Its return type is simply System.Object.
When methods are invoked via IActor.CallMethod, the client is expected to also pass in a client ID and sequence number. This allows the actor runtime to detect duplicate requests resend the result of previous invocations. The client will of course be responsible for ensure that the pair of values is unique to a given message.
Actor to Actor Communication
Actors communicate with each other via transient instances of IActorProxy. The proxy allows one actor to invoke another actor’s CallMethod. The simplest way to use the proxy is to
In addition to allowing direct method invocation, the proxy can be used to setup a subscription. Based on a modified version of IObserver, events from the publishing actor can cause methods to be invoked on the subscribing actor. The subscription can be cancelled by calling Dispose on a token returned by the Subscribe method.
A Sample Client
Most of what we’ve been talking about is of interest to actor runtime implementations. Most developers will be dealing with a client such as FabricActorClient. Here we see the key methods:
public class FabricActorClient
{
public FabricActorClient(Uri fabricUri, Uri actorUri, bool useGateway);
public bool AddAssembly(string assemblyName, byte[] assemblyBytes);
public Object CallMethod(string methodName, object[] parameters);
public IDisposable Subscribe(string eventType, IObserver<ActorEvent> eventObserver);
}
Again, it is important to note that AddAssembly is looking for static functions that match the signature Func<IActorState, object[], object>.
ActorFx is currently in alpha state and is available on CodePlex under the Apache License.