1. Overview

After the introduction to the library Akka Typed, it’s time to go deeper and describe one of the main interaction patterns between actors: the Tell Pattern, also known as fire and forget.

2. Akka Dependencies

To use the Akka Typed library, we need to import akka-actor-typed and akka-actor-testkit-typed for testing:

libraryDependencies += "com.typesafe.akka" %% "akka-actor-typed" % "2.8.0",
libraryDependencies += "com.typesafe.akka" %% "akka-actor-testkit-typed" % "2.8.0" % Test,

3. Scenario

To talk about the actors’ interactions, we need to define some actors first. Imagine we’re developing a microservice architecture using the Akka library and the actor model. One of the principles of microservices architecture is to create observable services.

An observable service should implement the Log Aggregation pattern. Hence, the service should write logs into a centralized logging server, which provides searching and alerting.

We want to implement the pattern using Akka actors. First, we need to define the desired protocol. For the sake of simplicity, there are only three log levels for our logs:

sealed trait Log
final case class Trace(msg: String) extends Log
final case class Info(msg: String) extends Log
final case class Error(msg: String) extends Log

The actor who receives the logging messages will simply log them using the Akka library’s standard logging system:

object LogKeeperActor {
  def apply(): Behavior[Log] = {
    Behaviors.receive { (context, message) =>
      message match {
        case Trace(msg) => context.log.trace(msg)
        case Info(msg) => context.log.info(msg)
        case Error(msg) => context.log.error(msg)
      }
      Behaviors.same
    }
  }
}

The only step we’re missing is to show how to send logging messages to the LogKeeperActor.

4. Tell, Don’t Ask!

The protocol and behavior defined by LogKeeperActor are straightforward. The receipt of a logging message does not generate any response to the caller that can continue its computation. This kind of interaction is called asynchronous.

Fully asynchronous communication is also the preferred type of interaction among actors and is called the Tell Pattern. It’s so crucial in the Akka library that developers reserved a dedicated symbol for sending a message using the pattern: the bang “!” (or exclamation mark).

To use the Tell Pattern, an actor must retrieve an actor reference to the actor it wants to send the message to. In our scenario, it’s feasible to pass an actor reference to the LogKeeperActor during the definition of actor behavior in the apply method:

object MicroserviceActor {
  final case class DoSomeStuff[T](stuff: T)

  def apply(logger: ActorRef[LogKeeperActor.Log]): Behavior[DoSomeStuff[_]] = {
    Behaviors.receiveMessage {
      case DoSomeStuff(stuff) =>
        logger ! LogKeeperActor.Info(stuff.toString)
        Behaviors.same
    }
  }
}

The MicroserviceActor receives a message that carries a payload and logs the payload using the centralized service we defined earlier.

logger ! LogKeeperActor.Info(stuff.toString)

The actor does not block waiting for a response from the logger actor. It merely goes ahead, listening for the next message to come.

5. Pros and Cons

Like any other pattern, the Tell Pattern has its pros and cons. Its most important feature is the full asynchronicity of the protocol it defines. Non-blocking operations are at the base of many modern programming paradigms, such as Reactive Programming.

However, the Tell Pattern makes the caller unaware of whether the message was ever received or processed by the receiver. There is no way to know, for example, if the network lost the message or if the mailbox of the receiver was full and resulted in an OutOfMemoryError.

Moreover, it’s improbable that we can define such a simple protocol, in which a request does not correspond to any response. Often, an actor requests to another actor to produce some result back to the sending of a message.

For the above use case, the Akka library implements another pattern, called the Ask Pattern. This pattern mixes the use of Akka actors with the asynchronous features of the Scala Future type.

Furthermore, in the masterpiece book Effective Akka, Jamie Allen defines how to implement a request/response protocol using only the Tell Pattern. That final solution is known as the Cameo Pattern.

6. Conclusions

In this article, we reviewed one of the most common interaction patterns between Akka actors: the Tell Pattern. We gave a practical example of its use, and finally, we listed the pros and cons of the design.

As always, the full code examples used in this tutorial are available over on GitHub.


» 下一篇: Scala中的类型转换