ZIO Actors
ZIO Actors is a high-performance, purely functional library for building, composing, and supervising typed actors based on ZIO.
Introduction
The Actor Model is used to build distributed highly scalable applications. The core concept behind the actor model is the ability to create multiple actors which run concurrently. The actor would receive a message do some computation on the message and then output a new message. Each actor runs independently of each other with no shared state between them and as such failure of one actor won't have an affect on the running of another. In its simplest form the goal of this project is to provide the ability to write actors in Functional Way that are typed leveraging ZIO.
ZIO Actors is based on the Actor Model which is a conceptual model of concurrent computation. In the actor model, the actor is the fundamental unit of computation, unlike the ZIO concurrency model, which is the fiber.
Each actor has a mailbox that stores and processes the incoming messages in FIFO order. An actor allowed to:
- create another actor.
- send a message to itself or other actors.
- handle the incoming message, and:
- decide what to do based on the current state and the received message.
- decide what is the next state based on the current state and the received message.
Some characteristics of an Actor Model:
-
Isolated State β Each actor holds its private state. They only have access to their internal state. They are isolated from each other, and they do not share the memory. The only way to change the state of an actor is to send a message to that actor.
-
Process of One Message at a Time β Each actor handles and processes one message at a time. They read messages from their inboxes and process them sequentially.
-
Actor Persistence β A persistent actor records its state as events. The actor can recover its state from persisted events after a crash or restart.
-
Remote Messaging β Actors can communicate with each other only through messages. They can run locally or remotely on another machine. Remote actors can communicate with each other transparently as if there are located locally.
-
Actor Supervision β Parent actors can supervise their child actors. For example, if a child actor fails, the supervisor actor can restart that actor.
Here's list of contents available:
- Basicsβ Instantiating
ActorSystem
, defining actor's behavior, spawning actors. - Supervisionβ Short description of supervision functionality usage
- Remotingβ Defining remoting configuration, usage example, restrictions
- Persistenceβ Event sourcing mechanism, datastore configuration
- Akka Interopβ Integration with akka typed actors.
Installation
To use this library, we need to add the following line to our library dependencies in build.sbt
file:
libraryDependencies += "dev.zio" %% "zio-actors" % "0.1.0"
Akka actors also has some other optional modules for persistence (which is useful for event sourcing) and integration with Akka toolkit:
libraryDependencies += "dev.zio" %% "zio-actors-persistence" % "0.1.0"
libraryDependencies += "dev.zio" %% "zio-actors-persistence-jdbc" % "0.1.0"
libraryDependencies += "dev.zio" %% "zio-actors-akka-interop" % "0.1.0"
Example
Let's try to implement a simple Counter Actor which receives two Increase
and Get
commands:
import zio.actors.Actor.Stateful
import zio.actors._
import zio.clock.Clock
import zio.console.putStrLn
import zio.{ExitCode, UIO, URIO, ZIO}
sealed trait Message[+_]
case object Increase extends Message[Unit]
case object Get extends Message[Int]
object CounterActorExample extends zio.App {
// Definition of stateful actor
val counterActor: Stateful[Any, Int, Message] =
new Stateful[Any, Int, Message] {
override def receive[A](
state: Int,
msg: Message[A],
context: Context
): UIO[(Int, A)] =
msg match {
case Increase => UIO((state + 1, ()))
case Get => UIO((state, state))
}
}
val myApp: ZIO[Clock, Throwable, Int] =
for {
system <- ActorSystem("MyActorSystem")
actor <- system.make("counter", Supervisor.none, 0, counterActor)
_ <- actor ! Increase
_ <- actor ! Increase
s <- actor ? Get
} yield s
override def run(args: List[String]): URIO[zio.ZEnv, ExitCode] =
myApp
.flatMap(state => putStrLn(s"The final state of counter: $state"))
.exitCode
}
Resources
- Acting Lessons for Scala Engineers with Akka and ZIO by Salar Rahmanian (November 2020)
- Introduction to ZIO Actors by Salar Rahmanian (November 2020)
Documentation
Learn more on the ZIO Actors homepage!
Contributing
For the general guidelines, see ZIO contributor's guide.
Code of Conduct
See the Code of Conduct