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:
Roland 2012-05-15 20:57:39 +02:00
parent d8bb688ece
commit 7d342e5c96
4 changed files with 233 additions and 27 deletions

View file

@ -7,6 +7,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
//#imports
import akka.actor.*;
import akka.remote.RemoteActorRefProvider;
import akka.serialization.*;
import com.typesafe.config.*;
@ -78,32 +79,118 @@ public class SerializationDocTestBase {
//#actorref-serializer
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() {
//#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();
public Address getAddressFor(Address remoteAddress) {
final scala.Option<Address> optAddr = system.provider()
.getExternalAddressFor(remoteAddress);
if (optAddr.isDefined()) {
return optAddr.get();
} else {
throw new UnsupportedOperationException(
"cannot send to remote address " + remoteAddress);
}
}
}
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();
}
}

View file

@ -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
: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 actors 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 systems 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 Akkas 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
----------------------------

View file

@ -5,13 +5,17 @@ package docs.serialization
import org.scalatest.matchers.MustMatchers
import akka.testkit._
import akka.actor.{ ActorRef, ActorSystem }
//#imports
import akka.actor.{ ActorRef, ActorSystem }
import akka.serialization._
import com.typesafe.config.ConfigFactory
//#imports
import akka.actor.ExtensionKey
import akka.actor.ExtendedActorSystem
import akka.actor.Extension
import akka.actor.Address
import akka.remote.RemoteActorRefProvider
//#my-own-serializer
class MyOwnSerializer extends Serializer {
@ -176,5 +180,38 @@ class SerializationDocSpec extends AkkaSpec {
val deserializedActorRef = theActorSystem actorFor identifier
// Then just use the ActorRef
//#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
}
}

View file

@ -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
: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 actors 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 systems 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 Akkas 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
----------------------------