add docs about how to serialize ActorRefs
- scala & java samples of how to obtain the correct address to inject when calling ActorPath.toStringWithAddress
This commit is contained in:
parent
d8bb688ece
commit
7d342e5c96
4 changed files with 233 additions and 27 deletions
|
|
@ -7,6 +7,7 @@ import org.junit.Test;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
//#imports
|
//#imports
|
||||||
import akka.actor.*;
|
import akka.actor.*;
|
||||||
|
import akka.remote.RemoteActorRefProvider;
|
||||||
import akka.serialization.*;
|
import akka.serialization.*;
|
||||||
import com.typesafe.config.*;
|
import com.typesafe.config.*;
|
||||||
|
|
||||||
|
|
@ -78,32 +79,118 @@ public class SerializationDocTestBase {
|
||||||
//#actorref-serializer
|
//#actorref-serializer
|
||||||
theActorSystem.shutdown();
|
theActorSystem.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#external-address
|
||||||
|
public static class ExternalAddressExt implements Extension {
|
||||||
|
private final ExtendedActorSystem system;
|
||||||
|
|
||||||
|
public ExternalAddressExt(ExtendedActorSystem system) {
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
@Test public void demonstrateTheProgrammaticAPI() {
|
public Address getAddressFor(Address remoteAddress) {
|
||||||
//#programmatic
|
final scala.Option<Address> optAddr = system.provider()
|
||||||
ActorSystem system = ActorSystem.create("example");
|
.getExternalAddressFor(remoteAddress);
|
||||||
|
if (optAddr.isDefined()) {
|
||||||
// Get the Serialization Extension
|
return optAddr.get();
|
||||||
Serialization serialization = SerializationExtension.get(system);
|
} else {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
// Have something to serialize
|
"cannot send to remote address " + remoteAddress);
|
||||||
String original = "woohoo";
|
}
|
||||||
|
}
|
||||||
// Find the Serializer for it
|
|
||||||
Serializer serializer = serialization.findSerializerFor(original);
|
|
||||||
|
|
||||||
// Turn it into bytes
|
|
||||||
byte[] bytes = serializer.toBinary(original);
|
|
||||||
|
|
||||||
// Turn it back into an object,
|
|
||||||
// the nulls are for the class manifest and for the classloader
|
|
||||||
String back = (String)serializer.fromBinary(bytes);
|
|
||||||
|
|
||||||
// Voilá!
|
|
||||||
assertEquals(original, back);
|
|
||||||
|
|
||||||
//#programmatic
|
|
||||||
system.shutdown();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ExternalAddress extends
|
||||||
|
AbstractExtensionId<ExternalAddressExt> implements ExtensionIdProvider {
|
||||||
|
public static final ExternalAddress ID = new ExternalAddress();
|
||||||
|
|
||||||
|
public ExternalAddress lookup() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExternalAddressExt createExtension(ExtendedActorSystem system) {
|
||||||
|
return new ExternalAddressExt(system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#external-address
|
||||||
|
|
||||||
|
public void demonstrateExternalAddress() {
|
||||||
|
// this is not meant to be run, only to be compiled
|
||||||
|
final ActorSystem system = ActorSystem.create();
|
||||||
|
final Address remoteAddr = new Address("", "");
|
||||||
|
// #external-address
|
||||||
|
final Address addr = ExternalAddress.ID.get(system).getAddressFor(remoteAddr);
|
||||||
|
// #external-address
|
||||||
|
}
|
||||||
|
|
||||||
|
//#external-address-default
|
||||||
|
public static class DefaultAddressExt implements Extension {
|
||||||
|
private final ExtendedActorSystem system;
|
||||||
|
|
||||||
|
public DefaultAddressExt(ExtendedActorSystem system) {
|
||||||
|
this.system = system;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
final ActorRefProvider provider = system.provider();
|
||||||
|
if (provider instanceof RemoteActorRefProvider) {
|
||||||
|
return ((RemoteActorRefProvider) provider).transport().address();
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("need RemoteActorRefProvider");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DefaultAddress extends
|
||||||
|
AbstractExtensionId<DefaultAddressExt> implements ExtensionIdProvider {
|
||||||
|
public static final DefaultAddress ID = new DefaultAddress();
|
||||||
|
|
||||||
|
public DefaultAddress lookup() {
|
||||||
|
return ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultAddressExt createExtension(ExtendedActorSystem system) {
|
||||||
|
return new DefaultAddressExt(system);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//#external-address-default
|
||||||
|
|
||||||
|
public void demonstrateDefaultAddress() {
|
||||||
|
// this is not meant to be run, only to be compiled
|
||||||
|
final ActorSystem system = ActorSystem.create();
|
||||||
|
final Address remoteAddr = new Address("", "");
|
||||||
|
// #external-address-default
|
||||||
|
final Address addr = DefaultAddress.ID.get(system).getAddress();
|
||||||
|
// #external-address-default
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void demonstrateTheProgrammaticAPI() {
|
||||||
|
// #programmatic
|
||||||
|
ActorSystem system = ActorSystem.create("example");
|
||||||
|
|
||||||
|
// Get the Serialization Extension
|
||||||
|
Serialization serialization = SerializationExtension.get(system);
|
||||||
|
|
||||||
|
// Have something to serialize
|
||||||
|
String original = "woohoo";
|
||||||
|
|
||||||
|
// Find the Serializer for it
|
||||||
|
Serializer serializer = serialization.findSerializerFor(original);
|
||||||
|
|
||||||
|
// Turn it into bytes
|
||||||
|
byte[] bytes = serializer.toBinary(original);
|
||||||
|
|
||||||
|
// Turn it back into an object,
|
||||||
|
// the nulls are for the class manifest and for the classloader
|
||||||
|
String back = (String) serializer.fromBinary(bytes);
|
||||||
|
|
||||||
|
// Voilá!
|
||||||
|
assertEquals(original, back);
|
||||||
|
|
||||||
|
// #programmatic
|
||||||
|
system.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,47 @@ you might want to know how to serialize and deserialize them properly, here's th
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||||
:include: imports,actorref-serializer
|
:include: imports,actorref-serializer
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``ActorPath.toStringWithAddress`` only differs from ``toString`` if the
|
||||||
|
address does not already have ``host`` and ``port`` components, i.e. it only
|
||||||
|
inserts address information for local addresses.
|
||||||
|
|
||||||
|
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,
|
||||||
|
durable mailbox, 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. An actor system is not limited to having just one remote
|
||||||
|
transport per se, which makes this question a bit more interesting.
|
||||||
|
|
||||||
|
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
|
||||||
|
:meth:`ActorRefProvider.getExternalAddressFor(remoteAddr)` to query the system
|
||||||
|
for the appropriate address to use when sending to ``remoteAddr``:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||||
|
:include: external-address
|
||||||
|
|
||||||
|
This requires that you know at least which type of address will be supported by
|
||||||
|
the system which will deserialize the resulting actor reference; if you have no
|
||||||
|
concrete address handy you can create a dummy one for the right protocol using
|
||||||
|
``new Address(protocol, "", "", 0)`` (assuming that the actual transport used is as
|
||||||
|
lenient as Akka’s RemoteActorRefProvider).
|
||||||
|
|
||||||
|
There is a possible simplification available if you are just using the default
|
||||||
|
:class:`NettyRemoteTransport` with the :meth:`RemoteActorRefProvider`, which is
|
||||||
|
enabled by the fact that this combination has just a single remote address:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/serialization/SerializationDocTestBase.java
|
||||||
|
:include: external-address-default
|
||||||
|
|
||||||
|
This solution has to be adapted once other providers are used (like the planned
|
||||||
|
extensions for clustering).
|
||||||
|
|
||||||
Deep serialization of Actors
|
Deep serialization of Actors
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,17 @@ package docs.serialization
|
||||||
|
|
||||||
import org.scalatest.matchers.MustMatchers
|
import org.scalatest.matchers.MustMatchers
|
||||||
import akka.testkit._
|
import akka.testkit._
|
||||||
import akka.actor.{ ActorRef, ActorSystem }
|
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
import akka.actor.{ ActorRef, ActorSystem }
|
||||||
import akka.serialization._
|
import akka.serialization._
|
||||||
import com.typesafe.config.ConfigFactory
|
import com.typesafe.config.ConfigFactory
|
||||||
|
|
||||||
//#imports
|
//#imports
|
||||||
|
import akka.actor.ExtensionKey
|
||||||
|
import akka.actor.ExtendedActorSystem
|
||||||
|
import akka.actor.Extension
|
||||||
|
import akka.actor.Address
|
||||||
|
import akka.remote.RemoteActorRefProvider
|
||||||
|
|
||||||
//#my-own-serializer
|
//#my-own-serializer
|
||||||
class MyOwnSerializer extends Serializer {
|
class MyOwnSerializer extends Serializer {
|
||||||
|
|
@ -176,5 +180,38 @@ class SerializationDocSpec extends AkkaSpec {
|
||||||
val deserializedActorRef = theActorSystem actorFor identifier
|
val deserializedActorRef = theActorSystem actorFor identifier
|
||||||
// Then just use the ActorRef
|
// Then just use the ActorRef
|
||||||
//#actorref-serializer
|
//#actorref-serializer
|
||||||
|
|
||||||
|
//#external-address
|
||||||
|
object ExternalAddress extends ExtensionKey[ExternalAddressExt]
|
||||||
|
|
||||||
|
class ExternalAddressExt(system: ExtendedActorSystem) extends Extension {
|
||||||
|
def addressFor(remoteAddr: Address): Address =
|
||||||
|
system.provider.getExternalAddressFor(remoteAddr) getOrElse
|
||||||
|
(throw new UnsupportedOperationException("cannot send to " + remoteAddr))
|
||||||
|
}
|
||||||
|
|
||||||
|
def serializeTo(ref: ActorRef, remote: Address): String =
|
||||||
|
ref.path.toStringWithAddress(ExternalAddress(theActorSystem).addressFor(remote))
|
||||||
|
//#external-address
|
||||||
|
}
|
||||||
|
|
||||||
|
"demonstrate how to do default Akka serialization of ActorRef" in {
|
||||||
|
val theActorSystem: ActorSystem = system
|
||||||
|
|
||||||
|
//#external-address-default
|
||||||
|
object ExternalAddress extends ExtensionKey[ExternalAddressExt]
|
||||||
|
|
||||||
|
class ExternalAddressExt(system: ExtendedActorSystem) extends Extension {
|
||||||
|
def addressForAkka: Address = system.provider match {
|
||||||
|
case r: RemoteActorRefProvider ⇒ r.transport.address
|
||||||
|
case _ ⇒
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"this method requires the RemoteActorRefProvider to be configured")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def serializeAkkaDefault(ref: ActorRef): String =
|
||||||
|
ref.path.toStringWithAddress(ExternalAddress(theActorSystem).addressForAkka)
|
||||||
|
//#external-address-default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,47 @@ you might want to know how to serialize and deserialize them properly, here's th
|
||||||
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||||
:include: imports,actorref-serializer
|
:include: imports,actorref-serializer
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``ActorPath.toStringWithAddress`` only differs from ``toString`` if the
|
||||||
|
address does not already have ``host`` and ``port`` components, i.e. it only
|
||||||
|
inserts address information for local addresses.
|
||||||
|
|
||||||
|
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,
|
||||||
|
durable mailbox, 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. An actor system is not limited to having just one remote
|
||||||
|
transport per se, which makes this question a bit more interesting.
|
||||||
|
|
||||||
|
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
|
||||||
|
:meth:`ActorRefProvider.getExternalAddressFor(remoteAddr)` to query the system
|
||||||
|
for the appropriate address to use when sending to ``remoteAddr``:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||||
|
:include: external-address
|
||||||
|
|
||||||
|
This requires that you know at least which type of address will be supported by
|
||||||
|
the system which will deserialize the resulting actor reference; if you have no
|
||||||
|
concrete address handy you can create a dummy one for the right protocol using
|
||||||
|
``Address(protocol, "", "", 0)`` (assuming that the actual transport used is as
|
||||||
|
lenient as Akka’s RemoteActorRefProvider).
|
||||||
|
|
||||||
|
There is a possible simplification available if you are just using the default
|
||||||
|
:class:`NettyRemoteTransport` with the :meth:`RemoteActorRefProvider`, which is
|
||||||
|
enabled by the fact that this combination has just a single remote address:
|
||||||
|
|
||||||
|
.. includecode:: code/akka/docs/serialization/SerializationDocSpec.scala
|
||||||
|
:include: external-address-default
|
||||||
|
|
||||||
|
This solution has to be adapted once other providers are used (like the planned
|
||||||
|
extensions for clustering).
|
||||||
|
|
||||||
Deep serialization of Actors
|
Deep serialization of Actors
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue