Note: Code examples in this blog post are out of date, see the Akka documentation for latest information on this topic.
We believe Akka Typed will be adopted in existing systems gradually and therefore it’s important to be able to use typed and untyped actors together, within the same ActorSystem
. Also, we will not be able to integrate with all existing modules in one big bang release and that is another reason for why these two ways of writing actors must be able to coexist.
There are two different ActorSystem
implementations; akka.actor.ActorSystem
and akka.typed.ActorSystem
. The latter can currently only host and run typed actors and can therefore only be used for greenfield projects that are only using typed actors. It doesn’t have any integration with other Akka modules yet, such as Akka Remoting, Cluster, and Streams. That will of course be implemented. The advantage of the new ActorSystem
implementation is that it is simplified and also more efficient, e.g. messages are not wrapped in envelope because the sender reference is removed.
akka.typed.ActorSystem
is interesting, but currently very limited. Good news is that the untyped akka.actor.ActorSystem
can host and run a mix of untyped and typed actors via an adapter layer. Pretty much everything you would expect works, such as:
- send message from typed to untyped, and opposite
- spawn and supervise typed child from untyped parent, and opposite
- watch typed from untyped, and opposite
First we start an ordinary ActorSystem
and create an untyped actor:
val system = akka.actor.ActorSystem("sys")
system.actorOf(MyUntyped1.props(), "first")
The untyped actor looks like this:
import akka.typed.scaladsl.adapter._
object MyUntyped1 {
def props(): akka.actor.Props = akka.actor.Props(new MyUntyped1)
}
class MyUntyped1 extends akka.actor.Actor {
// context.spawn is an implicit extension method
val second: akka.typed.ActorRef[MyTyped1.Command] =
context.spawn(MyTyped1.behavior, "second")
// context.watch is an implicit extension method
context.watch(second)
// self can be used as the `replyTo` parameter here because
// there is an implicit conversion from akka.actor.ActorRef to
// akka.typed.ActorRef
second ! MyTyped1.Ping(self)
override def receive = {
case MyTyped1.Pong =>
println(s"$self got Pong from ${sender()}")
// context.stop is an implicit extension method
context.stop(second)
case akka.actor.Terminated(ref) =>
println(s"$self observed termination of $ref")
context.stop(self)
}
}
It spawns a typed child:
object MyTyped1 {
sealed trait Command
final case class Ping(replyTo: akka.typed.ActorRef[Pong.type]) extends Command
case object Pong
val behavior: Behavior[Command] =
akka.typed.scaladsl.Actor.immutable { (ctx, msg) =>
msg match {
case Ping(replyTo) =>
println(s"${ctx.self} got Ping from $replyTo")
replyTo ! Pong
same
}
}
}
There is one import
that is needed to make that work:
import akka.typed.scaladsl.adapter._
That adds some implicit extension methods that are added to untyped and typed ActorSystem
and ActorContext
in both directions. Note the inline comments in the example above. In the javadsl
the corresponding adapter methods are static methods in akka.typed.javadsl.Adapter
.
Let’s turn the example upside down and first start the typed actor and then the untyped as a child.
import akka.typed.scaladsl.adapter._
val system = akka.actor.ActorSystem("sys")
// system.spawn is an implicit extension method
system.spawn(MyTyped2.behavior, "first")
The typed actor looks like this:
import akka.typed.scaladsl.adapter._
object MyTyped2 {
final case class Ping(replyTo: akka.typed.ActorRef[Pong.type])
sealed trait Command
case object Pong extends Command
val behavior: Behavior[Command] =
akka.typed.scaladsl.Actor.deferred { context =>
// context.spawn is an implicit extension method
val second: akka.actor.ActorRef =
context.actorOf(MyUntyped2.props(), "second")
// context.watch is an implicit extension method
context.watch(second)
// illustrating how to pass sender, toUntyped is an implicit extension method
second.tell(MyTyped2.Ping(context.self), context.self.toUntyped)
akka.typed.scaladsl.Actor.immutable[Command] { (ctx, msg) =>
msg match {
case Pong =>
// it's not possible to get the sender, that must be sent in message
println(s"${ctx.self} got Pong")
// context.stop is an implicit extension method
ctx.stop(second)
same
}
} onSignal {
case (ctx, akka.typed.Terminated(ref)) =>
println(s"${ctx.self} observed termination of $ref")
stopped
}
}
}
It spawns an untyped child:
object MyUntyped2 {
def props(): akka.actor.Props = akka.actor.Props(new MyUntyped2)
}
class MyUntyped2 extends akka.actor.Actor {
override def receive = {
case MyTyped2.Ping(replyTo) =>
// we use the replyTo ActorRef in the message,
// but could use sender() if needed and it was passed
// as parameter to tell
println(s"$self got Pong from ${sender()}")
replyTo ! MyTyped2.Pong
}
}
There is one caveat regarding supervision of untyped child from typed parent. If the child throws an exception we would expect it to be restarted, but supervision in Akka Typed defaults to stopping the child in case it fails. The restarting facilities in Akka Typed will not work with untyped children. However, the workaround is simply to add another untyped actor that takes care of the supervision, i.e. restarts in case of failure if that is the desired behavior.
In this blog post we have illustrated how you can try out Akka Typed in your existing projects and use it for some actors while not having to migrate everything in one go.
The full source code of these examples, including corresponding Java examples, are available in patriknw/akka-typed-blog.
This post is part of the "Introducing Akka Typed" series. Explore other posts in this series:
- Akka Typed: Hello World in the new API
- → Akka Typed: Coexistence
- Akka Typed: Mutable vs. Immutable
- Akka Typed: Protocols
- Akka Typed: Supervision
- Akka Typed: Lifecycle and Watch
- Akka Typed: Timers
- Akka Typed: New Cluster API
- Akka Typed: New Cluster Tools API
- Akka Typed: New Persistence API
- Announcing the course Programming Reactive Systems!
- Typed Supervision: why the changes?
- Akka 2.5.22 Brings Akka Typed To Production Ready
- Akka 2.6 roadmap
- Tour of Akka Typed