Comparing .NET virtual actor frameworks
The previous article in the series introduced the concept of virtual actor model. Now let’s take a look at what tools we have at our disposal in the .NET ecosystem. In the next few sections we’ll describe the popular framework choices: Orleans, Proto.Actor, Akka.Net, and Dapr. We’ll focus on their unique features and approach.
Orleans
The Orleans framework is the precursor of virtual actor model. It comes from a Microsoft Research project started in 2010. It powered backend services of well-known games like Halo 4. Back when it started, it was a little rough around the edges, with static classes, a lot of reflection, XML configuration etc. However now, a few iterations later, it is rather pleasant to use.
Orleans focuses solely on the virtual actor model - the traditional actor hierarchies are not explicitly supported.
Orleans implements its own clustering mechanism and relies on external database to provide membership information to the cluster members. Orleans also has its own code generated serializer for remote messaging (although serializers are pluggable). Similarly, the network protocol is specific to Orleans.
Pros:
- Mature open-source project, backed by Microsoft
- Comprehensive documentation
- Large and active community
- Support for pub-sub streaming between actors
- Persistent reminders – timers that work even if the actor has been deactivated
- Implementations of the Membership Table for popular databases, e.g. Mongo DB provided by community. Similarly, many persistence implementations are available.
- Orleans Dashboard is a nice addition that allows a quick peek into the cluster operation
- Distributed ACID transactions – if you really need them (we haven’t tried this feature ourselves)
Cons:
- Traditional actor hierarchy is not explicitly supported
- No commercial support available
- Still a bit too much “configuration via attributes” and other auto-magic for our tastes
Akka.Net
Akka.Net is a port of the Akka project from Java ecosystem. It is a project under .NET Foundation, backed by the Petabridge company. It has an open-source core and tools and services offered as commercial addons.
Being a close port of another framework, Akka.Net brings all the good ideas from the original, but also brings controversial design decisions (e.g. HOCON configuration).
Akka.Net focuses mostly on the use case of traditional actors and supervision hierarchies. But it also has the clustering module that can create actor systems across multiple machines. In particular, the Cluster Sharding mechanism resembles the virtual actor approach. The main difference from the user perspective is that Akka.Net does not deal with single virtual actors. It rather groups them into shards according to user-specified sharding strategy and then assigns those shards to machines in the cluster. It has some unique solutions in this regard, which will be discussed in another blog post.
Akka.Net follows the route of implementing its own clustering mechanism as well as network protocol and serialization (it’s possible to swap out implementations). Although out of the box version 1.4 uses Newtonsoft JSON serializer, our tests show that better performance can be achieved using the Hyperion serializer (currently in beta).
Pros:
- Backed by a company, commercial support plans available
- Comprehensive documentation and lots of examples and video materials
- Based on concepts of the well-known Akka framework
- Ability to combine clustering with local supervision hierarchies
- Cluster auto-load balancing and “remembering entities” mechanism
Cons:
- HOCON configuration and some other quirks imported from Akka
- Some essential components require license purchase, like the monitoring library Phobos
- Focus on the traditional actor model
- Clustering is based on seed nodes, which requires some of the cluster nodes to have stable network addresses. It is recommended to use the Lighthouse service and e.g. deploy it as a Stateful Set in Kubernetes.
Proto.Actor
Proto.Actor is a framework founded by the creator of Akka.Net. It incorporates the learnings from Akka.Net, but at the same time it makes “do not reinvent the wheel” its main philosophy.
This means that aspects like serialization, messaging and clustering all reuse existing and battle tested solutions. In particular, Proto.Actor makes use of gRPC with protobuf. It also uses existing clustering providers like Consul, Zookeeper or even the native Kubernetes APIs. You can select the implementation that fits your use case and your infrastructure.
The virtual actors are a first-class concept in Proto.Actor. There are many ways the framework supports this programming model, including code-generated base classes that encapsulate low level communication concerns. At the same time, it is also possible to create traditional supervision hierarchies. These approaches are easy to mix and match in Proto.Actor.
Proto.Actor also provides an interesting mechanism called Local Affinity, which we will explore in a later blog post. Another differentiator is the Go version of the framework.
Pros:
- Uses well known and tested standards for communication and clustering
- Ability to combine clustering with local supervision hierarchies
- Highest message throughput in our ping-pong benchmark
- Documentation has received many improvements in recent months
- Various options for distributing and locating actors across the cluster (Partition Identity Lookup, Partition Activator Lookup, DB Lookup)
- Local Affinity mechanism
- Best programming API in our subjective opinion
- OpenTelemetry compatible monitoring
Cons:
- No commercial support available
- Still hasn’t reached version 1.0 which results in some breaking API changes occasionally
- Relatively small community
- Focus on the event-sourced persistence, which in many cases is not relevant. Fortunately, it is easy to provide your own persistence implementation.
Dapr
Dapr is another open source project backed by Microsoft. It is a broader framework, that delivers service discovery, secure and reliable communication between services as well as abstractions over storage and pub-sub functionality. But one additional part of Dapr is the virtual actor model implementation with some concepts borrowed from Orleans.
Dapr implements its functionality in a technology agnostic way. The framework itself is written in Go, but it runs next to the actual application (e.g. in sidecar container) and it communicates with it via HTTP or gRPC calls. This is interesting because you can build an actor-based solution with any technology. However, the Dapr runtime does not take care of a crucial aspect - actor’s state. The actor should keep its state in memory and only interact with persistent storage when needed. If you use one of the Dapr SDKs, the state will be cached for you in memory, otherwise you must implement a similar solution yourself.
On the downside, the sidecar approach introduces overhead. It seems, that Dapr’s virtual actors implementation is not meant for high throughput scenarios.
The showcase application, eShopOnDapr, uses virtual actors to implement a persistent workflow (Process Manager pattern) which is an interesting use case.
Pros:
- Project backed by Microsoft
- It’s relatively easy to start working with Dapr
- Technology agnostic (although using one of the SDKs will make your life easier)
- Convenient if you already use Dapr
- Persistent reminders – timers that work even if the actor has been deactivated
Cons:
- Overhead of HTTP communication between sidecar and the application
- Traditional actor hierarchy is not explicitly supported
- Complex deployment structure, multiple components needed e.g. to run in Kubernetes, including CRDs for configuration
- Requires Dapr runtime on development machine
Conclusion
The virtual actor frameworks available in .NET ecosystem often offer unique features. There is no clear answer to which one is the best, because as usual - everything depends on the use case.
To dig a little bit deeper into some of these frameworks, we will compare their performance and cluster topology change handling in the upcoming blog posts.
Marcin Budny, R&D Lead
Marcin is a software architect and developer focused on distributed systems, cloud, APIs and event sourcing. He worked on projects in the field of IoT, telemetry, remote monitoring and management, system integration and computer vision with deep learning. Passionate about latest tech and good music