Reviewed and improved remote-actors doc
This commit is contained in:
parent
888af3479e
commit
1c29885f3d
4 changed files with 268 additions and 247 deletions
|
|
@ -354,7 +354,8 @@ trait RemoteServerModule extends RemoteModule {
|
|||
def registerByUuid(actorRef: ActorRef): Unit
|
||||
|
||||
/**
|
||||
* Register Remote Actor by a specific 'id' passed as argument.
|
||||
* Register Remote Actor by a specific 'id' passed as argument. The actor is registered by UUID rather than ID
|
||||
* when prefixing the handle with the “uuid:” protocol.
|
||||
* <p/>
|
||||
* NOTE: If you use this method to register your remote actor then you must unregister the actor by this ID yourself.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
Remote Actors (Java)
|
||||
====================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Akka supports starting interacting with UntypedActors and TypedActors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
|
|
@ -142,12 +146,6 @@ The default behavior is that the remote client will maintain a transaction log o
|
|||
|
||||
If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown.
|
||||
|
||||
You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried).
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
Object[] pending = Actors.remote().pendingMessages();
|
||||
|
||||
Running Remote Server in untrusted mode
|
||||
---------------------------------------
|
||||
|
||||
|
|
@ -253,21 +251,13 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen
|
|||
|
||||
The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash.
|
||||
|
||||
Remote Actors
|
||||
-------------
|
||||
|
||||
Akka has two types of remote actors:
|
||||
|
||||
* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server.
|
||||
* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc.
|
||||
|
||||
Client-managed Remote UntypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
The client creates the remote actor and "moves it" to the server.
|
||||
|
||||
When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
|
||||
Here is an example:
|
||||
|
|
@ -291,26 +281,31 @@ An UntypedActor can also start remote child Actors through one of the “spawn/l
|
|||
.. code-block:: java
|
||||
|
||||
...
|
||||
getContext().spawnRemote(MyActor.class, hostname, port);
|
||||
getContext().spawnRemote(MyActor.class, hostname, port, timeoutInMsForFutures);
|
||||
getContext().spawnLinkRemote(MyActor.class, hostname, port, timeoutInMsForFutures);
|
||||
...
|
||||
|
||||
Server-managed Remote UntypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------
|
||||
|
||||
Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote actors is really simple. 2 methods only:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.Actors;
|
||||
import akka.actor.UntypedActor;
|
||||
|
||||
class MyActor extends UntypedActor {
|
||||
public void onReceive(Object message) throws Exception {
|
||||
...
|
||||
}
|
||||
}
|
||||
Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class);
|
||||
Actors.remote().start("localhost", 2552).register("hello-service", Actors.actorOf(HelloWorldActor.class));
|
||||
|
||||
Actors created like this are automatically started.
|
||||
|
||||
|
|
@ -322,88 +317,6 @@ You can also register an actor by its UUID rather than ID or handle. This is don
|
|||
|
||||
server.unregister("uuid:" + actor.uuid);
|
||||
|
||||
Client side usage
|
||||
*****************
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
ActorRef actor = Actors.remote().actorFor("hello-service", "localhost", 2552);
|
||||
actor.sendOneWay("Hello");
|
||||
|
||||
There are many variations on the 'remote()#actorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = actorFor(className, hostname, port);
|
||||
... = actorFor(className, timeout, hostname, port);
|
||||
... = actorFor(uuid, className, hostname, port);
|
||||
... = actorFor(uuid, className, timeout, hostname, port);
|
||||
... // etc
|
||||
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
||||
Client-managed Remote TypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, , "localhost", 2552);
|
||||
|
||||
And if you want to specify the timeout:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552);
|
||||
|
||||
You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
new Component(
|
||||
Foo.class,
|
||||
FooImpl.class,
|
||||
new LifeCycle(new Permanent(), 1000),
|
||||
1000,
|
||||
new RemoteAddress("localhost", 2552))
|
||||
|
||||
Server-managed Remote TypedActor
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000);
|
||||
remote().registerTypedActor("user-service", typedActor);
|
||||
|
||||
Client side usage
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552);
|
||||
actor.registerUser(...);
|
||||
|
||||
There are variations on the 'remote()#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port);
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port);
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader);
|
||||
|
||||
Session bound server side setup
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -414,17 +327,19 @@ Session bound actors are useful if you need to keep state per session, e.g. user
|
|||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
import akka.japi.Creator;
|
||||
|
||||
class HelloWorldActor extends Actor {
|
||||
...
|
||||
}
|
||||
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
remote().registerPerSession("hello-service", new Creator[ActorRef]() {
|
||||
remote().registerPerSession("hello-service", new Creator<ActorRef>() {
|
||||
public ActorRef create() {
|
||||
return actorOf(HelloWorldActor.class);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Note that the second argument in registerPerSession is a Creator, it means that the create method will create a new ActorRef each invocation.
|
||||
It will be called to create an actor every time a session is established.
|
||||
|
|
@ -443,19 +358,22 @@ There are many variations on the 'remote()#actorFor' method. Here are some of th
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
... = actorFor(className, hostname, port);
|
||||
... = actorFor(className, timeout, hostname, port);
|
||||
... = actorFor(uuid, className, hostname, port);
|
||||
... = actorFor(uuid, className, timeout, hostname, port);
|
||||
... = remote().actorFor(className, hostname, port);
|
||||
... = remote().actorFor(className, timeout, hostname, port);
|
||||
... = remote().actorFor(uuid, className, hostname, port);
|
||||
... = remote().actorFor(uuid, className, timeout, hostname, port);
|
||||
... // etc
|
||||
|
||||
Automatic remote 'sender' reference management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
||||
Akka is automatically remote-enabling the sender Actor reference for you in order to allow the receiver to respond to the message using 'getContext().getSender().sendOneWay(msg);' or 'getContext().reply(msg);'. By default it is registering the sender reference in the remote server with the 'hostname' and 'port' from the akka.conf configuration file. The default is "localhost" and 2552 and if there is no remote server with this hostname and port then it creates and starts it.
|
||||
Automatic remote 'sender' reference management
|
||||
----------------------------------------------
|
||||
|
||||
The sender of a remote message will be reachable with a reply through the remote server on the node that the actor is residing, automatically.
|
||||
Please note that firewalled clients won't work right now. [2011-01-05]
|
||||
|
||||
Identifying remote actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
-------------------------
|
||||
|
||||
The 'id' field in the 'Actor' class is of importance since it is used as identifier for the remote actor. If you want to create a brand new actor every time you instantiate a remote actor then you have to set the 'id' field to a unique 'String' for each instance. If you want to reuse the same remote actor instance for each new remote actor (of the same class) you create then you don't have to do anything since the 'id' field by default is equal to the name of the actor class.
|
||||
|
||||
|
|
@ -463,18 +381,83 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.util.UUID;
|
||||
import akka.actor.UntypedActor;
|
||||
import com.eaio.uuid.UUID;
|
||||
|
||||
class MyActor extends UntypedActor {
|
||||
public MyActor() {
|
||||
getContext().setId(UUID.newUuid().toString());
|
||||
getContext().setId(new UUID().toString());
|
||||
}
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
...
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
Client-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
Remote Typed Actors are created through the 'TypedActor.newRemoteInstance' factory method.
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO) TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, "localhost", 2552);
|
||||
|
||||
And if you want to specify the timeout:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
MyPOJO remoteActor = (MyPOJO)TypedActor.newRemoteInstance(MyPOJO.class, MyPOJOImpl.class, timeout, "localhost", 2552);
|
||||
|
||||
You can also define the Typed Actor to be a client-managed-remote service by adding the ‘RemoteAddress’ configuration element in the declarative supervisor configuration:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
new Component(
|
||||
Foo.class,
|
||||
FooImpl.class,
|
||||
new LifeCycle(new Permanent(), 1000),
|
||||
1000,
|
||||
new RemoteAddress("localhost", 2552))
|
||||
|
||||
Server-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
remote().start("localhost", 2552);
|
||||
|
||||
RegistrationService typedActor = TypedActor.newInstance(RegistrationService.class, RegistrationServiceImpl.class, 2000);
|
||||
remote().registerTypedActor("user-service", typedActor);
|
||||
|
||||
|
||||
Client side usage
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import static akka.actor.Actors.*;
|
||||
RegistrationService actor = remote().typedActorFor(RegistrationService.class, "user-service", 5000L, "localhost", 2552);
|
||||
actor.registerUser(...);
|
||||
|
||||
There are variations on the 'remote()#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port);
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port);
|
||||
... = remote().typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader);
|
||||
|
||||
Data Compression Configuration
|
||||
------------------------------
|
||||
|
||||
|
|
@ -493,44 +476,55 @@ You can configure it like this:
|
|||
}
|
||||
}
|
||||
|
||||
Code provisioning
|
||||
-----------------
|
||||
|
||||
Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes.
|
||||
This is something that will be addressed soon. Until then, sorry for the inconvenience.
|
||||
|
||||
Subscribe to Remote Client events
|
||||
---------------------------------
|
||||
|
||||
Akka has a subscription API for remote client events. You can register an Actor as a listener and this actor will have to be able to process these events:
|
||||
|
||||
RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
.. code-block:: java
|
||||
|
||||
class RemoteClientError { Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientDisconnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientConnected { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientStarted { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientShutdown { RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
class RemoteClientWriteFailed { Object message; Throwable cause; RemoteClientModule client; InetSocketAddress remoteAddress; }
|
||||
|
||||
So a simple listener actor can look like this:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.remoteinterface.*;
|
||||
|
||||
class Listener extends UntypedActor {
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof RemoteClientError) {
|
||||
RemoteClientError event = (RemoteClientError)message;
|
||||
Exception cause = event.getCause();
|
||||
...
|
||||
RemoteClientError event = (RemoteClientError) message;
|
||||
Throwable cause = event.getCause();
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientConnected) {
|
||||
RemoteClientConnected event = (RemoteClientConnected)message;
|
||||
...
|
||||
RemoteClientConnected event = (RemoteClientConnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientDisconnected) {
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected)message;
|
||||
...
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientStarted) {
|
||||
RemoteClientStarted event = (RemoteClientStarted)message;
|
||||
...
|
||||
RemoteClientStarted event = (RemoteClientStarted) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientShutdown) {
|
||||
RemoteClientShutdown event = (RemoteClientShutdown)message;
|
||||
...
|
||||
RemoteClientShutdown event = (RemoteClientShutdown) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientWriteFailed) {
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed)message;
|
||||
...
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed) message;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -550,43 +544,45 @@ Subscribe to Remote Server events
|
|||
|
||||
Akka has a subscription API for the server events. You can register an Actor as a listener and this actor will have to be able to process these events:
|
||||
|
||||
RemoteServerStarted { RemoteServerModule server; }
|
||||
RemoteServerShutdown { RemoteServerModule server; }
|
||||
RemoteServerError { Throwable cause; RemoteServerModule server; }
|
||||
RemoteServerClientConnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerClientDisconnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerClientClosed { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
.. code-block:: java
|
||||
|
||||
class RemoteServerStarted { RemoteServerModule server; }
|
||||
class RemoteServerShutdown { RemoteServerModule server; }
|
||||
class RemoteServerError { Throwable cause; RemoteServerModule server; }
|
||||
class RemoteServerClientConnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerClientDisconnected { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerClientClosed { RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
class RemoteServerWriteFailed { Object request; Throwable cause; RemoteServerModule server; Option<InetSocketAddress> clientAddress; }
|
||||
|
||||
So a simple listener actor can look like this:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.remoteinterface.*;
|
||||
|
||||
class Listener extends UntypedActor {
|
||||
|
||||
public void onReceive(Object message) throws Exception {
|
||||
if (message instanceof RemoteServerError) {
|
||||
RemoteServerError event = (RemoteServerError)message;
|
||||
Exception cause = event.getCause();
|
||||
...
|
||||
} else if (message instanceof RemoteServerStarted) {
|
||||
RemoteServerStarted event = (RemoteServerStarted)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerShutdown) {
|
||||
RemoteServerShutdown event = (RemoteServerShutdown)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientConnected) {
|
||||
RemoteServerClientConnected event = (RemoteServerClientConnected)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientDisconnected) {
|
||||
RemoteServerClientDisconnected event = (RemoteServerClientDisconnected)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerClientClosed) {
|
||||
RemoteServerClientClosed event = (RemoteServerClientClosed)message;
|
||||
...
|
||||
} else if (message instanceof RemoteServerWriteFailed) {
|
||||
RemoteServerWriteFailed event = (RemoteServerWriteFailed)message;
|
||||
...
|
||||
if (message instanceof RemoteClientError) {
|
||||
RemoteClientError event = (RemoteClientError) message;
|
||||
Throwable cause = event.getCause();
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientConnected) {
|
||||
RemoteClientConnected event = (RemoteClientConnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientDisconnected) {
|
||||
RemoteClientDisconnected event = (RemoteClientDisconnected) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientStarted) {
|
||||
RemoteClientStarted event = (RemoteClientStarted) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientShutdown) {
|
||||
RemoteClientShutdown event = (RemoteClientShutdown) message;
|
||||
// ...
|
||||
} else if (message instanceof RemoteClientWriteFailed) {
|
||||
RemoteClientWriteFailed event = (RemoteClientWriteFailed) message;
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -608,10 +604,27 @@ Message Serialization
|
|||
|
||||
All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization.
|
||||
|
||||
Read more about that in the `Serialization section <serialization-java>`_.
|
||||
Here is one example, but full documentation can be found in the :ref:`serialization-java`.
|
||||
|
||||
Code provisioning
|
||||
-----------------
|
||||
Protobuf
|
||||
^^^^^^^^
|
||||
|
||||
Akka does currently not support automatic code provisioning but requires you to have the remote actor class files available on both the "client" the "server" nodes.
|
||||
This is something that will be addressed soon. Until then, sorry for the inconvenience.
|
||||
Protobuf message specification needs to be compiled with 'protoc' compiler.
|
||||
|
||||
::
|
||||
|
||||
message ProtobufPOJO {
|
||||
required uint64 id = 1;
|
||||
required string name = 2;
|
||||
required bool status = 3;
|
||||
}
|
||||
|
||||
Using the generated message builder to send the message to a remote actor:
|
||||
|
||||
.. code-block:: java
|
||||
|
||||
actor.sendOneWay(ProtobufPOJO.newBuilder()
|
||||
.setId(11)
|
||||
.setStatus(true)
|
||||
.setName("Coltrane")
|
||||
.build());
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
Remote Actors (Scala)
|
||||
=====================
|
||||
|
||||
.. sidebar:: Contents
|
||||
|
||||
.. contents:: :local:
|
||||
|
||||
Module stability: **SOLID**
|
||||
|
||||
Akka supports starting and interacting with Actors and Typed Actors on remote nodes using a very efficient and scalable NIO implementation built upon `JBoss Netty <http://jboss.org/netty>`_ and `Google Protocol Buffers <http://code.google.com/p/protobuf/>`_ .
|
||||
|
|
@ -74,6 +78,7 @@ Normally you should not have to start and stop the client connection explicitly
|
|||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor._
|
||||
import java.net.InetSocketAddress
|
||||
|
||||
remote.shutdownClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise
|
||||
remote.restartClientConnection(new InetSocketAddress("localhost", 6666)) //Returns true if successful, false otherwise
|
||||
|
|
@ -143,12 +148,6 @@ The default behavior is that the remote client will maintain a transaction log o
|
|||
|
||||
If you choose a capacity higher than 0, then a bounded queue will be used and if the limit of the queue is reached then a 'RemoteClientMessageBufferException' will be thrown.
|
||||
|
||||
You can also get an Array with all the messages that the remote client has failed to send. Since the remote client events passes you an instance of the RemoteClient you have an easy way to act upon failure and do something with these messages (while waiting for them to be retried).
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val pending: Array[Any] = Actor.remote.pendingMessages
|
||||
|
||||
Running Remote Server in untrusted mode
|
||||
---------------------------------------
|
||||
|
||||
|
|
@ -255,24 +254,16 @@ You can also generate the secure cookie by using the 'Crypt' object and its 'gen
|
|||
|
||||
The secure cookie is a cryptographically secure randomly generated byte array turned into a SHA-1 hash.
|
||||
|
||||
Remote Actors
|
||||
-------------
|
||||
|
||||
Akka has two types of remote actors:
|
||||
|
||||
* Client-initiated and managed. Here it is the client that creates the remote actor and "moves it" to the server.
|
||||
* Server-initiated and managed. Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
They are good for different use-cases. The client-initiated are great when you want to monitor an actor on another node since it allows you to link to it and supervise it using the regular supervision semantics. They also make RPC completely transparent. The server-initiated, on the other hand, are great when you have a service running on the server that you want clients to connect to, and you want full control over the actor on the server side for security reasons etc.
|
||||
|
||||
Client-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
When you define an actors as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
The client creates the remote actor and "moves it" to the server.
|
||||
|
||||
Actors can be made remote by calling remote().actorOf[MyActor](host, port)
|
||||
When you define an actor as being remote it is instantiated as on the remote host and your local actor becomes a proxy, it works as a handle to the remote actor. The real execution is always happening on the remote node.
|
||||
|
||||
Actors can be made remote by calling remote.actorOf[MyActor](host, port)
|
||||
|
||||
Here is an example:
|
||||
|
||||
|
|
@ -280,29 +271,30 @@ Here is an example:
|
|||
|
||||
import akka.actor.Actor
|
||||
|
||||
class MyActor extends RemoteActor() {
|
||||
class MyActor extends Actor {
|
||||
def receive = {
|
||||
case "hello" => self.reply("world")
|
||||
}
|
||||
}
|
||||
|
||||
val remote = Actor.remote().actorOf[MyActor]("192.68.23.769", 2552)
|
||||
val remoteActor = Actor.remote.actorOf[MyActor]("192.68.23.769", 2552)
|
||||
|
||||
An Actor can also start remote child Actors through one of the 'spawn/link' methods. These will start, link and make the Actor remote atomically.
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
...
|
||||
spawnRemote[MyActor](hostname, port)
|
||||
spawnLinkRemote[MyActor](hostname, port)
|
||||
self.spawnRemote[MyActor](hostname, port, timeout)
|
||||
self.spawnLinkRemote[MyActor](hostname, port, timeout)
|
||||
...
|
||||
|
||||
Server-managed Remote Actors
|
||||
----------------------------
|
||||
|
||||
Here it is the server that creates the remote actor and the client can ask for a handle to this actor.
|
||||
|
||||
Server side setup
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote actors is really simple. 2 methods only:
|
||||
|
||||
.. code-block:: scala
|
||||
|
|
@ -358,10 +350,10 @@ There are many variations on the 'remote#actorFor' method. Here are some of them
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
... = actorFor(className, hostname, port)
|
||||
... = actorFor(className, timeout, hostname, port)
|
||||
... = actorFor(uuid, className, hostname, port)
|
||||
... = actorFor(uuid, className, timeout, hostname, port)
|
||||
... = remote.actorFor(className, hostname, port)
|
||||
... = remote.actorFor(className, timeout, hostname, port)
|
||||
... = remote.actorFor(uuid, className, hostname, port)
|
||||
... = remote.actorFor(uuid, className, timeout, hostname, port)
|
||||
... // etc
|
||||
|
||||
All of these also have variations where you can pass in an explicit 'ClassLoader' which can be used when deserializing messages sent from the remote actor.
|
||||
|
|
@ -371,11 +363,16 @@ Running sample
|
|||
|
||||
Here is a complete running sample (also available `here <http://github.com/jboner/akka/blob/master/akka-core/src/test/scala/ServerInitiatedRemoteActorSample.scala>`_):
|
||||
|
||||
Paste in the code below into two sbt concole shells. Then run:
|
||||
|
||||
- ServerInitiatedRemoteActorServer.run() in one shell
|
||||
- ServerInitiatedRemoteActorClient.run() in the other shell
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.util.Logging
|
||||
import Actor._
|
||||
import akka.event.EventHandler
|
||||
|
||||
class HelloWorldActor extends Actor {
|
||||
def receive = {
|
||||
|
|
@ -385,27 +382,27 @@ Here is a complete running sample (also available `here <http://github.com/jbone
|
|||
|
||||
object ServerInitiatedRemoteActorServer {
|
||||
|
||||
def run = {
|
||||
def run() {
|
||||
remote.start("localhost", 2552)
|
||||
remote.register("hello-service", actorOf[HelloWorldActor])
|
||||
}
|
||||
|
||||
def main(args: Array[String]) = run
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
object ServerInitiatedRemoteActorClient extends Logging {
|
||||
object ServerInitiatedRemoteActorClient {
|
||||
|
||||
def run = {
|
||||
def run() {
|
||||
val actor = remote.actorFor("hello-service", "localhost", 2552)
|
||||
val result = actor !! "Hello"
|
||||
log.info("Result from Remote Actor: %s", result)
|
||||
EventHandler.info("Result from Remote Actor: %s", result)
|
||||
}
|
||||
|
||||
def main(args: Array[String]) = run
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
Automatic remote 'sender' reference management
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
----------------------------------------------
|
||||
|
||||
The sender of a remote message will be reachable with a reply through the remote server on the node that the actor is residing, automatically.
|
||||
Please note that firewalled clients won't work right now. [2011-01-05]
|
||||
|
|
@ -419,10 +416,10 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.util.UUID
|
||||
import akka.actor.newUuid
|
||||
|
||||
class MyActor extends Actor {
|
||||
self.id = UUID.newUuid.toString
|
||||
self.id = newUuid.toString
|
||||
def receive = {
|
||||
case "hello" => self.reply("world")
|
||||
}
|
||||
|
|
@ -430,11 +427,9 @@ Here is an example of overriding the 'id' field:
|
|||
|
||||
val actor = remote.actorOf[MyActor]("192.68.23.769", 2552)
|
||||
|
||||
Remote Typed Actors
|
||||
-------------------
|
||||
|
||||
Client-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Client-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
DEPRECATED AS OF 1.1
|
||||
|
||||
|
|
@ -458,13 +453,13 @@ You can also define an Typed Actor to be remote programmatically when creating i
|
|||
|
||||
... // use pojo as usual
|
||||
|
||||
Server-managed Remote Actors
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Server-managed Remote Typed Actors
|
||||
----------------------------------
|
||||
|
||||
WARNING: Remote TypedActors do not work with overloaded methods on your TypedActor, refrain from using overloading.
|
||||
|
||||
Server side setup
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The API for server managed remote typed actors is nearly the same as for untyped actor
|
||||
|
||||
|
|
@ -507,20 +502,20 @@ They are also useful if you need to perform some cleanup when a client disconnec
|
|||
Note that the second argument in registerTypedPerSessionActor is an implicit function. It will be called to create an actor every time a session is established.
|
||||
|
||||
Client side usage
|
||||
*****************
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
val actor = remote.typedActorFor(classOf[RegistrationService], "user-service", 5000L, "localhost", 2552)
|
||||
actor.registerUser(…)
|
||||
|
||||
There are variations on the 'RemoteClient#typedActorFor' method. Here are some of them:
|
||||
There are variations on the 'remote#typedActorFor' method. Here are some of them:
|
||||
|
||||
.. code-block:: scala
|
||||
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port)
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port)
|
||||
... = typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, hostname, port)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port)
|
||||
... = remote.typedActorFor(interfaceClazz, serviceIdOrClassName, timeout, hostname, port, classLoader)
|
||||
|
||||
Data Compression Configuration
|
||||
------------------------------
|
||||
|
|
@ -583,15 +578,19 @@ So a simple listener actor can look like this:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Actor._
|
||||
import akka.remoteinterface._
|
||||
|
||||
val listener = actorOf(new Actor {
|
||||
def receive = {
|
||||
case RemoteClientError(cause, client, address) => ... // act upon error
|
||||
case RemoteClientDisconnected(client, address) => ... // act upon disconnection
|
||||
case RemoteClientConnected(client, address) => ... // act upon connection
|
||||
case RemoteClientStarted(client, address) => ... // act upon client shutdown
|
||||
case RemoteClientShutdown(client, address) => ... // act upon client shutdown
|
||||
case RemoteClientWriteFailed(request, cause, client, address) => ... // act upon write failure
|
||||
case _ => //ignore other
|
||||
case RemoteClientError(cause, client, address) => //... act upon error
|
||||
case RemoteClientDisconnected(client, address) => //... act upon disconnection
|
||||
case RemoteClientConnected(client, address) => //... act upon connection
|
||||
case RemoteClientStarted(client, address) => //... act upon client shutdown
|
||||
case RemoteClientShutdown(client, address) => //... act upon client shutdown
|
||||
case RemoteClientWriteFailed(request, cause, client, address) => //... act upon write failure
|
||||
case _ => // ignore other
|
||||
}
|
||||
}).start()
|
||||
|
||||
|
|
@ -637,15 +636,19 @@ So a simple listener actor can look like this:
|
|||
|
||||
.. code-block:: scala
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.Actor._
|
||||
import akka.remoteinterface._
|
||||
|
||||
val listener = actorOf(new Actor {
|
||||
def receive = {
|
||||
case RemoteServerStarted(server) => ... // act upon server start
|
||||
case RemoteServerShutdown(server) => ... // act upon server shutdown
|
||||
case RemoteServerError(cause, server) => ... // act upon server error
|
||||
case RemoteServerClientConnected(server, clientAddress) => ... // act upon client connection
|
||||
case RemoteServerClientDisconnected(server, clientAddress) => ... // act upon client disconnection
|
||||
case RemoteServerClientClosed(server, clientAddress) => ... // act upon client connection close
|
||||
case RemoteServerWriteFailed(request, cause, server, clientAddress) => ... // act upon server write failure
|
||||
case RemoteServerStarted(server) => //... act upon server start
|
||||
case RemoteServerShutdown(server) => //... act upon server shutdown
|
||||
case RemoteServerError(cause, server) => //... act upon server error
|
||||
case RemoteServerClientConnected(server, clientAddress) => //... act upon client connection
|
||||
case RemoteServerClientDisconnected(server, clientAddress) => //... act upon client disconnection
|
||||
case RemoteServerClientClosed(server, clientAddress) => //... act upon client connection close
|
||||
case RemoteServerWriteFailed(request, cause, server, clientAddress) => //... act upon server write failure
|
||||
}
|
||||
}).start()
|
||||
|
||||
|
|
@ -662,7 +665,7 @@ Message Serialization
|
|||
|
||||
All messages that are sent to remote actors needs to be serialized to binary format to be able to travel over the wire to the remote node. This is done by letting your messages extend one of the traits in the 'akka.serialization.Serializable' object. If the messages don't implement any specific serialization trait then the runtime will try to use standard Java serialization.
|
||||
|
||||
Here are some examples, but full documentation can be found in the `Serialization section <serialization>`_.
|
||||
Here are some examples, but full documentation can be found in the :ref:`serialization-scala`.
|
||||
|
||||
Scala JSON
|
||||
^^^^^^^^^^
|
||||
|
|
@ -676,7 +679,7 @@ Protobuf
|
|||
|
||||
Protobuf message specification needs to be compiled with 'protoc' compiler.
|
||||
|
||||
.. code-block:: scala
|
||||
::
|
||||
|
||||
message ProtobufPOJO {
|
||||
required uint64 id = 1;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package akka.actor.remote
|
||||
|
||||
import akka.actor.{Actor, ActorRegistry}
|
||||
|
||||
import akka.actor.Actor
|
||||
import Actor._
|
||||
import akka.event.EventHandler
|
||||
|
||||
/*************************************
|
||||
Instructions how to run the sample:
|
||||
|
|
@ -19,14 +19,12 @@ Instructions how to run the sample:
|
|||
* Then paste in the code below into both shells.
|
||||
|
||||
Then run:
|
||||
* ServerInitiatedRemoteActorServer.run in one shell
|
||||
* ServerInitiatedRemoteActorClient.run in one shell
|
||||
* ServerInitiatedRemoteActorServer.run() in one shell
|
||||
* ServerInitiatedRemoteActorClient.run() in the other shell
|
||||
Have fun.
|
||||
*************************************/
|
||||
|
||||
class HelloWorldActor extends Actor {
|
||||
self.start()
|
||||
|
||||
def receive = {
|
||||
case "Hello" => self.reply("World")
|
||||
}
|
||||
|
|
@ -34,16 +32,22 @@ class HelloWorldActor extends Actor {
|
|||
|
||||
object ServerInitiatedRemoteActorServer {
|
||||
|
||||
def main(args: Array[String]) = {
|
||||
Actor.remote.start("localhost", 2552)
|
||||
Actor.remote.register("hello-service", actorOf[HelloWorldActor])
|
||||
def run() {
|
||||
remote.start("localhost", 2552)
|
||||
remote.register("hello-service", actorOf[HelloWorldActor])
|
||||
}
|
||||
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
object ServerInitiatedRemoteActorClient {
|
||||
def main(args: Array[String]) = {
|
||||
val actor = Actor.remote.actorFor("hello-service", "localhost", 2552)
|
||||
|
||||
def run() {
|
||||
val actor = remote.actorFor("hello-service", "localhost", 2552)
|
||||
val result = actor !! "Hello"
|
||||
EventHandler.info("Result from Remote Actor: %s", result)
|
||||
}
|
||||
|
||||
def main(args: Array[String]) { run() }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue