docs for ask/pipeTo patterns
This commit is contained in:
parent
020c6b61da
commit
1f99b425de
5 changed files with 119 additions and 72 deletions
|
|
@ -37,6 +37,17 @@ import akka.util.Duration;
|
||||||
import akka.actor.ActorTimeoutException;
|
import akka.actor.ActorTimeoutException;
|
||||||
//#import-gracefulStop
|
//#import-gracefulStop
|
||||||
|
|
||||||
|
//#import-askPipeTo
|
||||||
|
import static akka.pattern.Patterns.ask;
|
||||||
|
import static akka.pattern.Patterns.pipeTo;
|
||||||
|
import akka.dispatch.Future;
|
||||||
|
import akka.dispatch.Futures;
|
||||||
|
import akka.util.Duration;
|
||||||
|
import akka.util.Timeout;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
//#import-askPipeTo
|
||||||
|
|
||||||
import akka.actor.Props;
|
import akka.actor.Props;
|
||||||
import akka.actor.UntypedActor;
|
import akka.actor.UntypedActor;
|
||||||
import akka.actor.UntypedActorFactory;
|
import akka.actor.UntypedActorFactory;
|
||||||
|
|
@ -45,6 +56,8 @@ import akka.dispatch.MessageDispatcher;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import scala.Option;
|
import scala.Option;
|
||||||
import java.lang.Object;
|
import java.lang.Object;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import akka.pattern.Patterns;
|
import akka.pattern.Patterns;
|
||||||
|
|
||||||
|
|
@ -198,6 +211,43 @@ public class UntypedActorDocTestBase {
|
||||||
//#gracefulStop
|
//#gracefulStop
|
||||||
system.shutdown();
|
system.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Result {
|
||||||
|
final int x;
|
||||||
|
final String s;
|
||||||
|
public Result(int x, String s) {
|
||||||
|
this.x = x;
|
||||||
|
this.s = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void usePatternsAskPipeTo() {
|
||||||
|
ActorSystem system = ActorSystem.create("MySystem");
|
||||||
|
ActorRef actorA = system.actorOf(new Props(MyUntypedActor.class));
|
||||||
|
ActorRef actorB = system.actorOf(new Props(MyUntypedActor.class));
|
||||||
|
ActorRef actorC = system.actorOf(new Props(MyUntypedActor.class));
|
||||||
|
//#ask-pipeTo
|
||||||
|
final Timeout t = new Timeout(Duration.create(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
|
final ArrayList<Future<Object>> futures = new ArrayList<Future<Object>>();
|
||||||
|
futures.add(ask(actorA, "request")); // using `akka.actor.timeout` from config
|
||||||
|
futures.add(ask(actorB, "reqeest", t)); // using explicit timeout from above
|
||||||
|
|
||||||
|
final Future<Iterable<Object>> aggregate = Futures.sequence(futures, system.dispatcher());
|
||||||
|
|
||||||
|
final Future<Result> transformed = aggregate.map(new akka.japi.Function<Iterable<Object>, Result>() {
|
||||||
|
public Result apply(Iterable<Object> coll) {
|
||||||
|
final Iterator<Object> it = coll.iterator();
|
||||||
|
final String s = (String) it.next();
|
||||||
|
final int x = (Integer) it.next();
|
||||||
|
return new Result(x, s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pipeTo(transformed, actorC);
|
||||||
|
//#ask-pipeTo
|
||||||
|
}
|
||||||
|
|
||||||
public static class MyActor extends UntypedActor {
|
public static class MyActor extends UntypedActor {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -315,26 +315,37 @@ If invoked without the sender parameter the sender will be
|
||||||
Ask: Send-And-Receive-Future
|
Ask: Send-And-Receive-Future
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
Using ``?`` will send a message to the receiving Actor asynchronously and
|
The ``ask`` pattern involves actors as well as futures, hence it is offered as
|
||||||
will immediately return a :class:`Future` which will be completed with
|
a use pattern rather than a method on :class:`ActorRef`:
|
||||||
an ``akka.actor.AskTimeoutException`` after the specified timeout:
|
|
||||||
|
|
||||||
.. code-block:: java
|
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#import-askPipeTo
|
||||||
|
|
||||||
long timeoutMillis = 1000;
|
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#ask-pipeTo
|
||||||
Future future = actorRef.ask("Hello", timeoutMillis);
|
|
||||||
|
|
||||||
The receiving actor should reply to this message, which will complete the
|
This example demonstrates ``ask`` together with the ``pipeTo`` pattern on
|
||||||
future with the reply message as value; ``getSender.tell(result)``.
|
futures, because this is likely to be a common combination. Please note that
|
||||||
|
all of the above is completely non-blocking and asynchronous: ``ask`` produces
|
||||||
|
a :class:`Future`, two of which are composed into a new future using the
|
||||||
|
:meth:`Futures.sequence` and :meth:`map` methods and then ``pipeTo`` installs
|
||||||
|
an ``onComplete``-handler on the future to effect the submission of the
|
||||||
|
aggregated :class:`Result` to another actor.
|
||||||
|
|
||||||
|
Using ``ask`` will send a message to the receiving Actor as with ``tell``, and
|
||||||
|
the receiving actor must reply with ``getSender().tell(reply)`` in order to
|
||||||
|
complete the returned :class:`Future` with a value. The ``ask`` operation
|
||||||
|
involves creating an internal actor for handling this reply, which needs to
|
||||||
|
have a timeout after which it is destroyed in order not to leak resources; see
|
||||||
|
more below.
|
||||||
|
|
||||||
To complete the future with an exception you need send a Failure message to the sender.
|
To complete the future with an exception you need send a Failure message to the sender.
|
||||||
This is not done automatically when an actor throws an exception while processing a
|
This is *not done automatically* when an actor throws an exception while processing a
|
||||||
message.
|
message.
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#reply-exception
|
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java#reply-exception
|
||||||
|
|
||||||
If the actor does not complete the future, it will expire after the timeout period,
|
If the actor does not complete the future, it will expire after the timeout period,
|
||||||
specified as parameter to the ``ask`` method.
|
specified as parameter to the ``ask`` method; this will complete the
|
||||||
|
:class:`Future` with an :class:`AskTimeoutException`.
|
||||||
|
|
||||||
See :ref:`futures-java` for more information on how to await or query a
|
See :ref:`futures-java` for more information on how to await or query a
|
||||||
future.
|
future.
|
||||||
|
|
@ -353,15 +364,6 @@ Gives you a way to avoid blocking.
|
||||||
there is not yet a way to detect these illegal accesses at compile time. See also:
|
there is not yet a way to detect these illegal accesses at compile time. See also:
|
||||||
:ref:`jmm-shared-state`
|
:ref:`jmm-shared-state`
|
||||||
|
|
||||||
The future returned from the ``ask`` method can conveniently be passed around or
|
|
||||||
chained with further processing steps, but sometimes you just need the value,
|
|
||||||
even if that entails waiting for it (but keep in mind that waiting inside an
|
|
||||||
actor is prone to dead-locks, e.g. if obtaining the result depends on
|
|
||||||
processing another message on this actor).
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/UntypedActorDocTestBase.java
|
|
||||||
:include: import-future,using-ask
|
|
||||||
|
|
||||||
Forward message
|
Forward message
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -325,9 +325,9 @@ Send messages
|
||||||
Messages are sent to an Actor through one of the following methods.
|
Messages are sent to an Actor through one of the following methods.
|
||||||
|
|
||||||
* ``!`` means “fire-and-forget”, e.g. send a message asynchronously and return
|
* ``!`` means “fire-and-forget”, e.g. send a message asynchronously and return
|
||||||
immediately. Also know as ``tell``.
|
immediately. Also known as ``tell``.
|
||||||
* ``?`` sends a message asynchronously and returns a :class:`Future`
|
* ``?`` sends a message asynchronously and returns a :class:`Future`
|
||||||
representing a possible reply. Also know as ``ask``.
|
representing a possible reply. Also known as ``ask``.
|
||||||
|
|
||||||
Message ordering is guaranteed on a per-sender basis.
|
Message ordering is guaranteed on a per-sender basis.
|
||||||
|
|
||||||
|
|
@ -352,25 +352,34 @@ If invoked from an instance that is **not** an Actor the sender will be
|
||||||
Ask: Send-And-Receive-Future
|
Ask: Send-And-Receive-Future
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
Using ``?`` will send a message to the receiving Actor asynchronously and
|
The ``ask`` pattern involves actors as well as futures, hence it is offered as
|
||||||
will immediately return a :class:`Future` which will be completed with
|
a use pattern rather than a method on :class:`ActorRef`:
|
||||||
an ``akka.actor.AskTimeoutException`` after the specified timeout:
|
|
||||||
|
|
||||||
.. code-block:: scala
|
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#ask-pipeTo
|
||||||
|
|
||||||
val future = actor ? "hello"
|
This example demonstrates ``ask`` together with the ``pipeTo`` pattern on
|
||||||
|
futures, because this is likely to be a common combination. Please note that
|
||||||
|
all of the above is completely non-blocking and asynchronous: ``ask`` produces
|
||||||
|
a :class:`Future`, three of which are composed into a new future using the
|
||||||
|
for-comprehension and then ``pipeTo`` installs an ``onComplete``-handler on the
|
||||||
|
future to effect the submission of the aggregated :class:`Result` to another
|
||||||
|
actor.
|
||||||
|
|
||||||
The receiving actor should reply to this message, which will complete the
|
Using ``ask`` will send a message to the receiving Actor as with ``tell``, and
|
||||||
future with the reply message as value; ``sender ! result``.
|
the receiving actor must reply with ``sender ! reply`` in order to complete the
|
||||||
|
returned :class:`Future` with a value. The ``ask`` operation involves creating
|
||||||
|
an internal actor for handling this reply, which needs to have a timeout after
|
||||||
|
which it is destroyed in order not to leak resources; see more below.
|
||||||
|
|
||||||
To complete the future with an exception you need send a Failure message to the sender.
|
To complete the future with an exception you need send a Failure message to the sender.
|
||||||
This is not done automatically when an actor throws an exception while processing a
|
This is *not done automatically* when an actor throws an exception while processing a
|
||||||
message.
|
message.
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#reply-exception
|
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#reply-exception
|
||||||
|
|
||||||
If the actor does not complete the future, it will expire after the timeout period,
|
If the actor does not complete the future, it will expire after the timeout
|
||||||
which is taken from one of the following locations in order of precedence:
|
period, completing it with an :class:`AskTimeoutException`. The timeout is
|
||||||
|
taken from one of the following locations in order of precedence:
|
||||||
|
|
||||||
1. explicitly given timeout as in:
|
1. explicitly given timeout as in:
|
||||||
|
|
||||||
|
|
@ -380,6 +389,9 @@ which is taken from one of the following locations in order of precedence:
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#using-implicit-timeout
|
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#using-implicit-timeout
|
||||||
|
|
||||||
|
3. actor system’s default value from ``akka.actor.timeout`` setting for
|
||||||
|
:meth:`ask` methods
|
||||||
|
|
||||||
See :ref:`futures-scala` for more information on how to await or query a
|
See :ref:`futures-scala` for more information on how to await or query a
|
||||||
future.
|
future.
|
||||||
|
|
||||||
|
|
@ -398,23 +410,6 @@ Gives you a way to avoid blocking.
|
||||||
there is not yet a way to detect these illegal accesses at compile time.
|
there is not yet a way to detect these illegal accesses at compile time.
|
||||||
See also: :ref:`jmm-shared-state`
|
See also: :ref:`jmm-shared-state`
|
||||||
|
|
||||||
The future returned from the ``?`` method can conveniently be passed around or
|
|
||||||
chained with further processing steps, but sometimes you just need the value,
|
|
||||||
even if that entails waiting for it (but keep in mind that waiting inside an
|
|
||||||
actor is prone to dead-locks, e.g. if obtaining the result depends on
|
|
||||||
processing another message on this actor).
|
|
||||||
|
|
||||||
For this purpose, there is the method :meth:`Future.as[T]` which waits until
|
|
||||||
either the future is completed or its timeout expires, whichever comes first.
|
|
||||||
The result is then inspected and returned as :class:`Some[T]` if it was
|
|
||||||
normally completed and the answer’s runtime type matches the desired type; if
|
|
||||||
the future contains an exception or the value cannot be cast to the desired
|
|
||||||
type, it will throw the exception or a :class:`ClassCastException` (if you want
|
|
||||||
to get :obj:`None` in the latter case, use :meth:`Future.asSilently[T]`). In
|
|
||||||
case of a timeout, :obj:`None` is returned.
|
|
||||||
|
|
||||||
.. includecode:: code/akka/docs/actor/ActorDocSpec.scala#using-ask
|
|
||||||
|
|
||||||
Forward message
|
Forward message
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -212,28 +212,6 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
system.stop(myActor)
|
system.stop(myActor)
|
||||||
}
|
}
|
||||||
|
|
||||||
"using ask" in {
|
|
||||||
//#using-ask
|
|
||||||
import akka.pattern.ask
|
|
||||||
|
|
||||||
class MyActor extends Actor {
|
|
||||||
def receive = {
|
|
||||||
case x: String ⇒ sender ! x.toUpperCase
|
|
||||||
case n: Int ⇒ sender ! (n + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val myActor = system.actorOf(Props(new MyActor), name = "myactor")
|
|
||||||
implicit val timeout = system.settings.ActorTimeout
|
|
||||||
val future = ask(myActor, "hello")
|
|
||||||
for (x ← future) println(x) //Prints "hello"
|
|
||||||
|
|
||||||
val result: Future[Int] = for (x ← ask(myActor, 3).mapTo[Int]) yield { 2 * x }
|
|
||||||
//#using-ask
|
|
||||||
|
|
||||||
system.stop(myActor)
|
|
||||||
}
|
|
||||||
|
|
||||||
"using implicit timeout" in {
|
"using implicit timeout" in {
|
||||||
val myActor = system.actorOf(Props(new FirstActor))
|
val myActor = system.actorOf(Props(new FirstActor))
|
||||||
//#using-implicit-timeout
|
//#using-implicit-timeout
|
||||||
|
|
@ -331,6 +309,28 @@ class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||||
case e: ActorTimeoutException ⇒ // the actor wasn't stopped within 5 seconds
|
case e: ActorTimeoutException ⇒ // the actor wasn't stopped within 5 seconds
|
||||||
}
|
}
|
||||||
//#gracefulStop
|
//#gracefulStop
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"using pattern ask / pipeTo" in {
|
||||||
|
val actorA, actorB, actorC, actorD = system.actorOf(Props.empty)
|
||||||
|
//#ask-pipeTo
|
||||||
|
import akka.pattern.{ ask, pipeTo }
|
||||||
|
|
||||||
|
case class Result(x: Int, s: String, d: Double)
|
||||||
|
case object Request
|
||||||
|
|
||||||
|
implicit val timeout = Timeout(5 seconds) // needed for `?` below
|
||||||
|
|
||||||
|
val f: Future[Result] =
|
||||||
|
for {
|
||||||
|
x ← ask(actorA, Request).mapTo[Int] // call pattern directly
|
||||||
|
s ← actorB ask Request mapTo manifest[String] // call by implicit conversion
|
||||||
|
d ← actorC ? Request mapTo manifest[Double] // call by symbolic name
|
||||||
|
} yield Result(x, s, d)
|
||||||
|
|
||||||
|
f pipeTo actorD // .. or ..
|
||||||
|
pipeTo(f, actorD)
|
||||||
|
//#ask-pipeTo
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -252,7 +252,7 @@ class Gossiper(remote: Remote, system: ActorSystemImpl) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val t = remoteSettings.RemoteSystemDaemonAckTimeout
|
val t = remoteSettings.RemoteSystemDaemonAckTimeout
|
||||||
Await.result(connection ? (newGossip, t), t) match {
|
Await.result(connection.?(newGossip)(t), t) match {
|
||||||
case Success(receiver) ⇒ log.debug("Gossip sent to [{}] was successfully received", receiver)
|
case Success(receiver) ⇒ log.debug("Gossip sent to [{}] was successfully received", receiver)
|
||||||
case Failure(cause) ⇒ log.error(cause, cause.toString)
|
case Failure(cause) ⇒ log.error(cause, cause.toString)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue