Classic Serialization

Note

Pekko Classic pertains to the original Actor APIs, which have been improved by more type safe and guided Actor APIs. Pekko Classic is still fully supported and existing applications can continue to use the classic APIs. It is also possible to use the new Actor APIs together with classic actors in the same ActorSystem, see coexistence. For new projects we recommend using the new Actor API.

Serialization is the same for Classic and Typed actors. It is described in Serialization, aside from serialization of ActorRef that is described here.

Dependency

To use Serialization, you must add the following dependency in your project:

sbt
val PekkoVersion = "1.0.3"
libraryDependencies += "org.apache.pekko" %% "pekko-actor" % PekkoVersion
Maven
<properties>
  <scala.binary.version>2.13</scala.binary.version>
</properties>
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.apache.pekko</groupId>
      <artifactId>pekko-bom_${scala.binary.version}</artifactId>
      <version>1.0.3</version>
      <type>pom</type>
      <scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>org.apache.pekko</groupId>
    <artifactId>pekko-actor_${scala.binary.version}</artifactId>
  </dependency>
</dependencies>
Gradle
def versions = [
  ScalaBinary: "2.13"
]
dependencies {
  implementation platform("org.apache.pekko:pekko-bom_${versions.ScalaBinary}:1.0.3")

  implementation "org.apache.pekko:pekko-actor_${versions.ScalaBinary}"
}

Serializing ActorRefs

All ActorRefs are serializable when using Serialization with Jackson, but in case you are writing your own serializer, you might want to know how to serialize and deserialize them properly. In the general case, the local address to be used depends on the type of remote address which shall be the recipient of the serialized information. Use Serialization.serializedActorPath(actorRef) like this:

Scala
sourceimport org.apache.pekko
import pekko.actor._
import pekko.actor.typed.scaladsl.Behaviors
import pekko.cluster.Cluster
import pekko.serialization._
Java
sourceimport org.apache.pekko.actor.*;
import org.apache.pekko.serialization.*;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
Scala
source// Serialize
// (beneath toBinary)
val serializedRef: String = Serialization.serializedActorPath(theActorRef)

// Then serialize the identifier however you like

// Deserialize
// (beneath fromBinary)
val deserializedRef = extendedSystem.provider.resolveActorRef(serializedRef)
// Then use the ActorRef
Java
source// Serialize
// (beneath toBinary)
String serializedRef = Serialization.serializedActorPath(theActorRef);

// Then just serialize the identifier however you like

// Deserialize
// (beneath fromBinary)
final ActorRef deserializedRef = extendedSystem.provider().resolveActorRef(serializedRef);
// Then just use the ActorRef

This assumes that serialization happens in the context of sending a message through the remote transport. There are other uses of serialization, though, e.g. storing actor references outside of an actor application (database, etc.). In this case, it is important to keep in mind that the address part of an actor’s path determines how that actor is communicated with. Storing a local actor path might be the right choice if the retrieval happens in the same logical context, but it is not enough when deserializing it on a different network host: for that it would need to include the system’s remote transport address.

Scala
sourceval selfAddress = Cluster(system).selfAddress

val serializedRef: String =
  theActorRef.path.toSerializationFormatWithAddress(selfAddress)
Java
sourceAddress selfAddress = Cluster.get(system).selfAddress();

String serializedRef = theActorRef.path().toSerializationFormatWithAddress(selfAddress);
Note

ActorPath.toSerializationFormatWithAddress differs from toString if the address does not already have host and port components, i.e. it only inserts address information for local addresses.

toSerializationFormatWithAddress also adds the unique id of the actor, which will change when the actor is stopped and then created again with the same name. Sending messages to a reference pointing the old actor will not be delivered to the new actor. If you don’t want this behavior, e.g. in case of long term storage of the reference, you can use toStringWithAddress, which doesn’t include the unique id.

There is also a default remote address which is the one used by cluster support (and typical systems have just this one); you can get it like this:

Scala
sourceval selfAddress = Cluster(system).selfAddress

val serializedRef: String =
  theActorRef.path.toSerializationFormatWithAddress(selfAddress)
Java
sourceAddress selfAddress = Cluster.get(system).selfAddress();

String serializedRef = theActorRef.path().toSerializationFormatWithAddress(selfAddress);