An actor is a computational entity that, in response to a message it receives, can concurrently:
- send a finite number of messages to other actors;
- create a finite number of new actors;
- designate the behavior to be used for the next message it receives.There is no assumed sequence to the above actions and they could be carried out in parallel.
playground. If you use Maven, place a dependency into your playground’s
x.y.zis a semantic version number and reflects the version of xoom-actors JAR file that you depend on. This number may be something such as
1.8.0. You will find the available versions, including the most recent version, available on one of the supported public repositories.
Pingerinterface with a single method definition, named
ping(), which takes a
Pongeras a parameter.
Pongerinterface the same way, but with a
pong()method that takes a
Pingeras a parameter.
Pingeris a client of
Pongeris a client of
Pongerinterface definitions, you will notice that both of these protocols extend the
Stoppableprotocol. Thus, they can both be stopped by other actors that have a
Stoppablereference to them. We use that capability from within
PingerActorto cause both actors to stop when the
stop()methods. That’s because the abstract base class,
stop()for them. You could override
stop()to find out when your actor is being stopped, but that’s not necessarily a good idea. What if you forgot to invoke the super’s
stop()? That would make you think that your actor was going to stop, but the actor would never shut down because the
Actorbase class behavior would never be run. If you want to know when you are being stopped, you can override one of the four life cycle methods instead of
beforeRestart(final Throwable reason)
afterRestart(final Throwable reason)
beforeResume(final Throwable reason)
Exception, it can take a number of actions. Your supervisor can tell your actor to resume, to stop, or to restart. If it tells your actor to resume, the
beforeResume()is invoked. When it tells your actor to restart, the
beforeRestart()is invoked first, and then the
afterRestart()is invoked. Since your actor has failed, it may have been left in an invalid state. In such cases, these three life cycle methods give your actor the opportunity to clean up after the problem that caused the
Exceptionand also reinitialize itself before reacting to its next available protocol message.
afterStop()method shows two additional perks of XOOM Actors. All actors have a personal address, which is available through your inherited
address()method. Also, all actors have a
Loggeravailable via its
logger()method. Any information that you log will be output asynchronously through a registered
Loggeractor, so your actor won't block while file output is performed.
Worldfor your actors to play in.
Worldis created. The
Worldis a major component of XOOM Actors. In a nutshell, a
Worldis the primary container within which actors live and play. Generally you would create only one
Worldper service instance. In DDD terms, a
Worldis the root of a Bounded Context. (Don't worry about the use of
pauseThisThread(); it is explained below.)
Worldis started, two actors are created, and a reference to their respective protocol is returned. Each actor is created by passing its protocol and the concrete actor type. You may also create actors by means of a
Definitionindicates the class of the actor that implements the protocol, such as
PingerActor.class, which implements the
Pinger.classprotocol. There are four ways to instantiate an actor by means of its constructor:
actorFor()as seen above, and also pass each constructor parameter following the implementation class type. This has the advantage of making the parameters visible, but they are passed as varargs each of type
Object, and are thus not type-safe.
Definitionobject as the second parameter to
Definitioncontains the class of the actor implementation and a possibly one or more constructor parameters, or it can pass
Definition.NoParameters. See the next code example for how to pass constructor parameters. This approach is also not type safe.
ActorInstantiator, a functional interface included in the
xoom-actorsSDK and runtime. Construct your specific
ActorInstantiatortype, pass any actor constructor parameters into the
ActorInstantiatorconstructor. When the actor is ready to be created by the runtime, the
ActorInstantiatormethod blah will be called. At that time call the specific actor's constructor when using
new. (See example provided below.)
PingerActortaking two parameters, a
int, using the
ActorInstantiator, which does not require the use of reflection:
PingerInstantiator, each of which is demonstrated separately in the follow example:
stop()for both actors, have been delivered.
io.vlingo.xoom.actors.testkit.TestUntilcomponent. This example also demonstrates how actors take constructor parameters. In the test method, create an instance of the
TestUntilto pass to the
PingerActorto take a
TestUntilinstance as a constructor parameter.
PingerActormust cause a
afterStop()method to signal to the test that the
afterStop()method causes the
until.happened()the test method will block. As soon as the
until.happened()causes its state to transition from
0, the test will unblock and the
Worldwill terminate. This enables the test to complete.
Worldobject as follows.
Worldwith normal runtime defaults in which
Actorinstances are created and run. For many uses of XOOM Actors the defaults are the easiest and safest way to use the
World. The following is a summary.
Worldby loading configurations from the file named
nameis used to name the
Worldwith your own name-value pairs using
java.util.Propertiesdefined in code. The details of the
xoom-actors.propertiesfile are discussed below.
Worldcan be started using fluent configuration.
Configurationare discussed below.
World, you should use the following to terminate the
World::terminate()method is currently a synchronous operation, but in the future will become asynchronous. When the
Worldterminates asynchronously there will be a
Completes<T>or callback construct to inform the client when the termination has completed.
Actorruntime may be configured by means of a file that adheres to the
java.util.Propertiesconventions. Each property is defined by a
=and then a
value. For the XOOM Actors toolkit the file must be named
xoom-actors.propertiesand be located in the runtime
classpath. The following shows how the standard
ConcurrentQueueMailboxcan be defined in this properties file.
\at the end of the line to be continued on the next line. To see example properties that can be used, you should review:
Worldprovides the following. All concrete
Actorinstance may obtain both their
Worldinstances as follows, which enables the
Actorto reach specific facilities offered by each.
Actoraddresses or a way to produce an address from a primitive or
Stringvalue. To do so, request it by means of the method
Worldruntime. Even if you load your runtime properties from the
xoom-actors.propertiesor your own
java.util.Propertiesdefinition, all of your runtime configurations are placed in the
Actorinstances that cannot be delivered for any reasons, such as the
Actorinstance has previously been stopped, are delivered to the special
DeadLetters. You may subscribe to receive
DeadLettersprotocol method to subscribe to its received messages. Your listener Actor must implement the
Loggerthat is provided to all
Actorinstances, use the following method.
Actoris a child of the default parent
Actormust be assigned to a overarching supervisor. The following provides a reference to the default supervisor of all newly created
Loggeris available through the
Worldinterface, there may also be a number of named
Loggerinstances. If you use non-default
Loggerinstances, they may be obtained via the following
Loggerinstances obtained through the standard XOOM Actors plugins are backed by actors, and are thus asynchronous by default.
Loggerimplementation is based on SLF4J. You may configure your
Loggerto your standards. The following is a simple example that outputs strictly to the console.
Loggerprotocol provides several facilities for logging application/service output and exceptions.
Worldinterface provides some additional facilities, but ones that are useful only for plugins. Those are documented below.
Worldyou are able to create
Actorinstance to run in it. As a reminder, there is nothing mysterious about actors. Actors are basically objects, but their behaviors are requested by sending messages asynchronously rather than through directly invoking their methods. The following shows you how to create an actor.
Worldis used to create a new instance of the
SimpleActortype implements the protocol defined by the interface named
Simple. Thus, actors provide protocol implementations, and they can implement multiple protocols.
p3. These parameters could be of any type, and each parameter is required to follow the convention that the constructor contract requires.
actorFor()returns, the requesting client is given a reference to the protocol that provides asynchronous access to the
Actor. This reference is used to send messages via methods on the protocol. The
Simpleprotocol is defined as follows.
simpleSay(). Messages are sent asynchronously to the concrete
Actorinstance, which in the case of the above example is an instance of
SimpleActor. This behavior is demonstrated by the following expression.
Actor. The reification is accomplished by creating a
Functionthat represents the method invocation and enqueuing the
Functioninstance on to the
Functionwill later be applied to the
Actorinstance when all previously enqueued messages have been processed and a thread becomes available to process the message at the front of the
Mailbox. Thus, the method invocation is temporally decoupled from the sending side and the receiving side.
Worlddoes not directly create an
Actor. Instead, the
actorFor()requests to the default
Stage. It is the
Stagethat provides the concrete implementation of
Stageis the abstraction within which
Actorinstances are maintained. A
Worldhas at least one
Stage, which is known as the default
Stage, and its name is
"__defaultStage". If you need to query the instance of the default stage, you use the following
Stagesupports two important behaviors,
actorFor()is a creational method, and there are several overloads supporting various parameter options. Using the method various implementations of the method
actorFor()will create a new
Actorinstance and answer one or more protocols. Once you have a reference to a protocol you are able to send messages to the given
Actorthat implements the protocol(s).
actorOf()method has a different purpose. Given an existing
Actorthat is contained within a given
Stage, you may use
actorOf()to find the existing
Actorby passing the
Completes<T>because it is an asynchronous operation, answering the requested protocol eventually.
Actormay obtain its containing
Stageinstances within the
World, but the default
Stageis automatically provided when a
Stageis responsible for holding the private root actor and the public root actor. These two instances act as default supervisors, which are responsible for protecting a
Worldfrom catastrophic failures. Supervision is explained in detail below.
Worldmust create some default operational
Actorinstances when it is started, it may be best to segregate your application/service
Actorinstances into another
Stage. It's simple to accomplish this. To obtain a reference to an existing
Stageby an other name, or to obtain or a newly created
Stageby name if non-existing, use one of the two following queries:
Stageinstance with the given
namedoes not yet exist, a new
Stagewith that name is created and returned.
Stageinstances. It's likely that the default
Stageand one application/service
Stageinstance will be sufficient. Yet, we don't set a limit on the number of
Stageinstances in case more than two would be useful.
Stagehas its own
Schedular, which may be used to produce time-lapsed events that are sent to an
Actorcan schedule itself or another
Actor, such as one or more of its children, for some timed event notification. In other words, it need not pass itself as the
100milliseconds of the registration and will repeat every
Actorscheduling itself for notifications must implement the
Scheduledprotocol, and it passes an
Actorenabled reference to that effect using the runtime method
Actorcan associate some specific data with which it will be notified on each event, which in this example is the
DataPackettype is only used for the example, and would be replaced with your own type, or you can pass
nullif the data is unused.
Scheduledobject you are provided a
Cancellableinstance. You may use this instance to cancel one-time or repeating occurrences.
Actorreceiving the timed event will be notified using the
intervalSignal()method of the
Scheduleris instantiated it creates a thread pool of 1 thread to manage all scheduling for a single
Stage. You can create a unique
Schedulerthat has its own thread pool in case the
Scheduleris already heavily used. Yet, note that if used as intended, the
Scheduledinterface registered with the
Scheduleris generally implemented by an
Actor(as explained above). Thus, in that case, the
Schedulerthread is not used to run the
ScheduledTaskinstances. That is, the
Schedulerhas a thread for tracking all
ScheduledTaskinstances under its care.
Scheduleritself. There seem to be two areas of potential latency that are related to the actor receiving the timed interval messages:
Schedulerwould be related to how many messages are already in the
Actor's mailbox before the
Scheduled#intervalSignal()is delivered. The more messages in the mailbox, the more latency to the arrival of the message of concern,
Scheduled#intervalSignal()messages arrive more rapidly than each previous one can be processed. In other words, are the full number of
intervalSignal()messages stacking up in the actor's mailbox? If so, consider increasing the time between timer intervals, or finding a way to increase performance of the computing done while handling each message, or both. If the actor's message handling is blocking on I/O you might want to use XOOM Streams to introduce backpressure and a "work stealing" approach to requesting intervals. Additionally, you could employ the
scheduleOnce()service and reschedule for one interval each time the actor's processing ends.
Stage's actor eviction service.
Actoris not limited to implementing a single protocol or
interface. It could have two or more. Consider again the
Schedulerexample (above). An Actor implementation that wants to schedule timed event signals will be more than a
Scheduledtype. Its primary purpose is to be another type of
Actor, and to its clients that protocol type is the focus. Consider this example.
Actoris to be a
DocumentProcessor. The fact that it is capable of being scheduled for timer signals is an internal concern only. The clients of this
Actorwill not know that it can be a
Scheduled, only that it is a
DocumentProcessorrequires it to schedule itself to receives timer signals. The
DocumentProcessorActormay be registered with the
Schedulerby passing a reference to itself as a
Scheduled. You saw this in the above example, but to make it stand out here is another one that isolates the use of the essential
Actortypes inherit the method
selfAs(Class<T>)to request itself as an
Actorthat supplies the protocol represented by the generic parameter
T. The Actor must implement the
this, for example to
scheduleOnce(), will cause
intervalSignal()notifications to be received as direct method invocations rather than as asynchronous messages. Even though you may think that this could be a good idea, it is definitely a very bad idea. You will eventually experience a race condition in your
Actorwhere a message delivered asynchronously on one thread will collide by accessing data simultaneously with the
Schedulerthread that makes the direct method invocations.
Actormay implement a number of protocols, such as when representing itself as protocols specifically supported by its children or other collaborators. This makes for loose coupling and least knowledge by design. Even so, care should be used to avoid creating an
Actorthat supports more protocols than is necessary to meet its primary responsibility.
Tis the type of the outcome. If your actor's message protocol that provides a return value answer does not return a
Completes<T>value, then the platform runtime will reject the protocol.