public class TimerBasedThrottler extends java.lang.Object implements Actor, FSM<TimerBasedThrottler.State,TimerBasedThrottler.Data>
== Throttling ==
A throttler is an actor that is defined through a target actor and a rate
(of type Throttler.Rate
). You set or change the target and rate at any time through the
Throttler.SetTarget
and Throttler.SetRate
messages, respectively. When you send the throttler any other message msg
, it will
put the message msg
into an internal queue and eventually send all queued messages to the target, at
a speed that respects the given rate. If no target is currently defined then the messages will be queued
and will be delivered as soon as a target gets set.
A throttler understands actor messages of type
Throttler.SetTarget
, Throttler.SetRate
, in
addition to any other messages, which the throttler will consider as messages to be sent to
the target.
== Transparency ==
Notice that the throttler forward
s messages, i.e., the target will see the original message sender
(and not the throttler) as the sender of the message.
== Persistence == Throttlers usually use an internal queue to keep the messages that need to be sent to the target. You therefore cannot rely on the throttler's inbox size in order to learn how much messages are outstanding.
It is left to the implementation whether the internal queue is persisted over application restarts or actor failure.
== Processing messages ==
The target should process messages as fast as possible. If the target requires substantial time to
process messages, it should distribute its work to other actors (using for example something like
a BalancingDispatcher
), otherwise the resulting system will always work below
the threshold rate.
Example: Suppose the throttler has a rate of 3msg/s and the target requires 1s to process a message. This system will only process messages at a rate of 1msg/s: the target will receive messages at at most 3msg/s but as it handles them synchronously and each of them takes 1s, its inbox will grow and grow. In such a situation, the target should distribute its messages to a set of worker actors so that individual messages can be handled in parallel.
==Example== For example, if you set a rate like "3 messages in 1 second", the throttler will send the first three messages immediately to the target actor but will need to impose a delay before sending out further messages:
// A simple actor that prints whatever it receives
class Printer extends Actor {
def receive = {
case x => println(x)
}
}
val printer = system.actorOf(Props[Printer], "printer")
// The throttler for this example, setting the rate
val throttler = system.actorOf(Props(classOf[TimerBasedThrottler], 3 msgsPer 1.second))
// Set the target
throttler ! SetTarget(Some(printer))
// These three messages will be sent to the printer immediately
throttler ! "1"
throttler ! "2"
throttler ! "3"
// These two will wait at least until 1 second has passed
throttler ! "4"
throttler ! "5"
==Implementation notes==
This throttler implementation internally installs a timer that repeats every rate.durationInMillis
and enables rate.numberOfCalls
additional calls to take place. A TimerBasedThrottler
uses very few system resources, provided the rate's duration is not too
fine-grained (which would cause a lot of timer invocations); for example, it does not store the calling history
as other throttlers may need to do.
However, a TimerBasedThrottler
only provides ''weak guarantees'' on the rate (see also
this blog post):
- Only ''delivery'' times are taken into account: if, for example, the throttler is used to throttle
requests to an external web service then only the start times of the web requests are considered.
If a web request takes very long on the server then more than rate.numberOfCalls
-many requests
may be observed on the server in an interval of duration rate.durationInMillis()
.
- There may be intervals of duration rate.durationInMillis()
that contain more than rate.numberOfCalls
message deliveries: a TimerBasedThrottler
only makes guarantees for the intervals
of its ''own'' timer, namely that no more than rate.numberOfCalls
-many messages are delivered within such intervals. Other intervals on the
timeline may contain more calls.
For some applications, these guarantees may not be sufficient.
==Known issues==
- If you change the rate using SetRate(rate)
, the actual rate may in fact be higher for the
overlapping period (i.e., durationInMillis()
) of the new and old rate. Therefore,
changing the rate frequently is not recommended with the current implementation.
- The queue of messages to be delivered is not persisted in any way; actor or system failure will
cause the queued messages to be lost.
Throttler
Modifier and Type | Class and Description |
---|---|
static class |
TimerBasedThrottler.Active$ |
static class |
TimerBasedThrottler.Data |
static class |
TimerBasedThrottler.Data$ |
static class |
TimerBasedThrottler.Idle$ |
static class |
TimerBasedThrottler.Message |
static class |
TimerBasedThrottler.Message$ |
static interface |
TimerBasedThrottler.State |
static class |
TimerBasedThrottler.Tick$ |
FSM.$minus$greater$, FSM.CurrentState<S>, FSM.CurrentState$, FSM.Event<D>, FSM.Event$, FSM.Failure, FSM.Failure$, FSM.LogEntry<S,D>, FSM.LogEntry$, FSM.Normal$, FSM.NullFunction$, FSM.Reason, FSM.Shutdown$, FSM.SilentState<S,D>, FSM.State$, FSM.StateTimeout$, FSM.StopEvent<S,D>, FSM.StopEvent$, FSM.SubscribeTransitionCallBack, FSM.SubscribeTransitionCallBack$, FSM.TimeoutMarker, FSM.TimeoutMarker$, FSM.Timer, FSM.Timer$, FSM.TransformHelper, FSM.Transition<S>, FSM.Transition$, FSM.UnsubscribeTransitionCallBack, FSM.UnsubscribeTransitionCallBack$
Actor.emptyBehavior$, Actor.ignoringBehavior$
Constructor and Description |
---|
TimerBasedThrottler(Throttler.Rate rate) |
Modifier and Type | Method and Description |
---|---|
static FSM.$minus$greater$ |
$minus$greater() |
protected abstract static void |
akka$actor$Actor$_setter_$context_$eq(ActorContext x$1) |
protected abstract static void |
akka$actor$Actor$_setter_$self_$eq(ActorRef x$1) |
static void |
akka$actor$ActorLogging$$_log_$eq(LoggingAdapter x$1) |
static LoggingAdapter |
akka$actor$ActorLogging$$_log() |
protected abstract static void |
akka$actor$FSM$_setter_$$minus$greater_$eq(FSM.$minus$greater$ x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$akka$actor$FSM$$handleEventDefault_$eq(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$akka$actor$FSM$$stateFunctions_$eq(scala.collection.mutable.Map<S,scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>>> x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$akka$actor$FSM$$stateTimeouts_$eq(scala.collection.mutable.Map<S,scala.Option<scala.concurrent.duration.FiniteDuration>> x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$akka$actor$FSM$$timerGen_$eq(scala.collection.Iterator<java.lang.Object> x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$akka$actor$FSM$$timers_$eq(scala.collection.mutable.Map<java.lang.String,FSM.Timer> x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$Event_$eq(FSM.Event$ x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$StateTimeout_$eq(FSM.StateTimeout$ x$1) |
protected abstract static void |
akka$actor$FSM$_setter_$StopEvent_$eq(FSM.StopEvent$ x$1) |
static void |
akka$actor$FSM$$currentState_$eq(FSM.State<S,D> x$1) |
static FSM.State<S,D> |
akka$actor$FSM$$currentState() |
static void |
akka$actor$FSM$$generation_$eq(long x$1) |
static long |
akka$actor$FSM$$generation() |
static void |
akka$actor$FSM$$handleEvent_$eq(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> x$1) |
static scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> |
akka$actor$FSM$$handleEvent() |
static scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> |
akka$actor$FSM$$handleEventDefault() |
static void |
akka$actor$FSM$$nextState_$eq(FSM.State<S,D> x$1) |
static FSM.State<S,D> |
akka$actor$FSM$$nextState() |
static scala.collection.mutable.Map<S,scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>>> |
akka$actor$FSM$$stateFunctions() |
static scala.collection.mutable.Map<S,scala.Option<scala.concurrent.duration.FiniteDuration>> |
akka$actor$FSM$$stateTimeouts() |
static void |
akka$actor$FSM$$terminateEvent_$eq(scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> x$1) |
static scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> |
akka$actor$FSM$$terminateEvent() |
static void |
akka$actor$FSM$$timeoutFuture_$eq(scala.Option<Cancellable> x$1) |
static scala.Option<Cancellable> |
akka$actor$FSM$$timeoutFuture() |
static scala.collection.Iterator<java.lang.Object> |
akka$actor$FSM$$timerGen() |
static scala.collection.mutable.Map<java.lang.String,FSM.Timer> |
akka$actor$FSM$$timers() |
static void |
akka$actor$FSM$$transitionEvent_$eq(scala.collection.immutable.List<scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit>> x$1) |
static scala.collection.immutable.List<scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit>> |
akka$actor$FSM$$transitionEvent() |
protected abstract static void |
akka$routing$Listeners$_setter_$listeners_$eq(java.util.Set<ActorRef> x$1) |
static void |
applyState(FSM.State<S,D> nextState) |
protected static void |
aroundPostRestart(java.lang.Throwable reason) |
protected static void |
aroundPostStop() |
protected static void |
aroundPreRestart(java.lang.Throwable reason,
scala.Option<java.lang.Object> message) |
protected static void |
aroundPreStart() |
protected static void |
aroundReceive(scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> receive,
java.lang.Object msg) |
static void |
cancelTimer(java.lang.String name) |
ActorContext |
context()
Stores the context for this actor, including self, and sender.
|
static boolean |
debugEvent() |
FSM.Event$ |
Event() |
protected static void |
gossip(java.lang.Object msg,
ActorRef sender) |
protected static ActorRef |
gossip$default$2(java.lang.Object msg) |
static void |
initialize() |
static boolean |
isStateTimerActive() |
static boolean |
isTimerActive(java.lang.String name) |
protected static scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> |
listenerManagement() |
protected java.util.Set<ActorRef> |
listeners() |
static LoggingAdapter |
log() |
protected static void |
logTermination(FSM.Reason reason) |
static void |
makeTransition(FSM.State<S,D> nextState) |
static D |
nextStateData() |
static void |
onTermination(scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> terminationHandler) |
static void |
onTransition(scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit> transitionHandler) |
static void |
postRestart(java.lang.Throwable reason) |
static void |
postStop() |
static void |
preRestart(java.lang.Throwable reason,
scala.Option<java.lang.Object> message) |
static void |
preStart() |
static void |
processEvent(FSM.Event<D> event,
java.lang.Object source) |
Throttler.Rate |
rate() |
static scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> |
receive() |
ActorRef |
self()
The 'self' field holds the ActorRef for this actor.
|
static ActorRef |
sender() |
static void |
setStateTimeout(S state,
scala.Option<scala.concurrent.duration.FiniteDuration> timeout) |
static void |
setTimer(java.lang.String name,
java.lang.Object msg,
scala.concurrent.duration.FiniteDuration timeout,
boolean repeat) |
static boolean |
setTimer$default$4() |
static void |
startWith(S stateName,
D stateData,
scala.Option<scala.concurrent.duration.FiniteDuration> timeout) |
static scala.Option<scala.concurrent.duration.FiniteDuration> |
startWith$default$3() |
static D |
stateData() |
static S |
stateName() |
FSM.StateTimeout$ |
StateTimeout()
This case object is received in case of a state timeout.
|
static FSM.State<S,D> |
stay() |
static FSM.State<S,D> |
stop() |
static FSM.State<S,D> |
stop(FSM.Reason reason) |
static FSM.State<S,D> |
stop(FSM.Reason reason,
D stateData) |
FSM.StopEvent$ |
StopEvent() |
static SupervisorStrategy |
supervisorStrategy() |
static scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit> |
total2pf(scala.Function2<S,S,scala.runtime.BoxedUnit> transitionHandler) |
static FSM.TransformHelper |
transform(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> func) |
static void |
unhandled(java.lang.Object message) |
static void |
when(S stateName,
scala.concurrent.duration.FiniteDuration stateTimeout,
scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> stateFunction) |
static scala.concurrent.duration.FiniteDuration |
when$default$2() |
static void |
whenUnhandled(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> stateFunction) |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
applyState, cancelTimer, debugEvent, handleTransition, initialize, isStateTimerActive, isTimerActive, logTermination, makeTransition, nextStateData, onTermination, onTransition, postStop, processEvent, processMsg, receive, register, setStateTimeout, setTimer, startWith, stateData, stateName, stay, stop, stop, stop, terminate, total2pf, transform, when, whenUnhandled
aroundPostRestart, aroundPostStop, aroundPreRestart, aroundPreStart, aroundReceive, postRestart, preRestart, preStart, sender, supervisorStrategy, unhandled
gossip, listenerManagement
log
public TimerBasedThrottler(Throttler.Rate rate)
public static final ActorRef sender()
protected static void aroundReceive(scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> receive, java.lang.Object msg)
protected static void aroundPreStart()
protected static void aroundPostStop()
protected static void aroundPreRestart(java.lang.Throwable reason, scala.Option<java.lang.Object> message)
protected static void aroundPostRestart(java.lang.Throwable reason)
public static SupervisorStrategy supervisorStrategy()
public static void preStart() throws java.lang.Exception
java.lang.Exception
public static void preRestart(java.lang.Throwable reason, scala.Option<java.lang.Object> message) throws java.lang.Exception
java.lang.Exception
public static void postRestart(java.lang.Throwable reason) throws java.lang.Exception
java.lang.Exception
public static void unhandled(java.lang.Object message)
protected abstract static void akka$actor$Actor$_setter_$context_$eq(ActorContext x$1)
protected abstract static void akka$actor$Actor$_setter_$self_$eq(ActorRef x$1)
protected static scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> listenerManagement()
protected static void gossip(java.lang.Object msg, ActorRef sender)
protected static ActorRef gossip$default$2(java.lang.Object msg)
protected abstract static void akka$routing$Listeners$_setter_$listeners_$eq(java.util.Set<ActorRef> x$1)
public static LoggingAdapter akka$actor$ActorLogging$$_log()
public static void akka$actor$ActorLogging$$_log_$eq(LoggingAdapter x$1)
public static LoggingAdapter log()
public static FSM.$minus$greater$ $minus$greater()
public static final void when(S stateName, scala.concurrent.duration.FiniteDuration stateTimeout, scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> stateFunction)
public static final void startWith(S stateName, D stateData, scala.Option<scala.concurrent.duration.FiniteDuration> timeout)
public static final FSM.State<S,D> stay()
public static final FSM.State<S,D> stop()
public static final FSM.State<S,D> stop(FSM.Reason reason)
public static final FSM.State<S,D> stop(FSM.Reason reason, D stateData)
public static final FSM.TransformHelper transform(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> func)
public static final void setTimer(java.lang.String name, java.lang.Object msg, scala.concurrent.duration.FiniteDuration timeout, boolean repeat)
public static final void cancelTimer(java.lang.String name)
public static final boolean isTimerActive(java.lang.String name)
public static final void setStateTimeout(S state, scala.Option<scala.concurrent.duration.FiniteDuration> timeout)
public static final boolean isStateTimerActive()
public static final void onTransition(scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit> transitionHandler)
public static final scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit> total2pf(scala.Function2<S,S,scala.runtime.BoxedUnit> transitionHandler)
public static final void onTermination(scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> terminationHandler)
public static final void whenUnhandled(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> stateFunction)
public static final void initialize()
public static final S stateName()
public static final D stateData()
public static final D nextStateData()
public static boolean debugEvent()
public static FSM.State<S,D> akka$actor$FSM$$currentState()
public static void akka$actor$FSM$$currentState_$eq(FSM.State<S,D> x$1)
public static scala.Option<Cancellable> akka$actor$FSM$$timeoutFuture()
public static void akka$actor$FSM$$timeoutFuture_$eq(scala.Option<Cancellable> x$1)
public static FSM.State<S,D> akka$actor$FSM$$nextState()
public static void akka$actor$FSM$$nextState_$eq(FSM.State<S,D> x$1)
public static long akka$actor$FSM$$generation()
public static void akka$actor$FSM$$generation_$eq(long x$1)
public static scala.collection.mutable.Map<java.lang.String,FSM.Timer> akka$actor$FSM$$timers()
public static scala.collection.Iterator<java.lang.Object> akka$actor$FSM$$timerGen()
public static scala.collection.mutable.Map<S,scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>>> akka$actor$FSM$$stateFunctions()
public static scala.collection.mutable.Map<S,scala.Option<scala.concurrent.duration.FiniteDuration>> akka$actor$FSM$$stateTimeouts()
public static scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> akka$actor$FSM$$handleEventDefault()
public static scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> akka$actor$FSM$$handleEvent()
public static void akka$actor$FSM$$handleEvent_$eq(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> x$1)
public static scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> akka$actor$FSM$$terminateEvent()
public static void akka$actor$FSM$$terminateEvent_$eq(scala.PartialFunction<FSM.StopEvent<S,D>,scala.runtime.BoxedUnit> x$1)
public static scala.collection.immutable.List<scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit>> akka$actor$FSM$$transitionEvent()
public static void akka$actor$FSM$$transitionEvent_$eq(scala.collection.immutable.List<scala.PartialFunction<scala.Tuple2<S,S>,scala.runtime.BoxedUnit>> x$1)
public static scala.PartialFunction<java.lang.Object,scala.runtime.BoxedUnit> receive()
public static void processEvent(FSM.Event<D> event, java.lang.Object source)
public static void applyState(FSM.State<S,D> nextState)
public static void makeTransition(FSM.State<S,D> nextState)
public static void postStop()
protected static void logTermination(FSM.Reason reason)
public static final scala.concurrent.duration.FiniteDuration when$default$2()
public static final scala.Option<scala.concurrent.duration.FiniteDuration> startWith$default$3()
public static final boolean setTimer$default$4()
protected abstract static void akka$actor$FSM$_setter_$Event_$eq(FSM.Event$ x$1)
protected abstract static void akka$actor$FSM$_setter_$StopEvent_$eq(FSM.StopEvent$ x$1)
protected abstract static void akka$actor$FSM$_setter_$$minus$greater_$eq(FSM.$minus$greater$ x$1)
protected abstract static void akka$actor$FSM$_setter_$StateTimeout_$eq(FSM.StateTimeout$ x$1)
protected abstract static void akka$actor$FSM$_setter_$akka$actor$FSM$$timers_$eq(scala.collection.mutable.Map<java.lang.String,FSM.Timer> x$1)
protected abstract static void akka$actor$FSM$_setter_$akka$actor$FSM$$timerGen_$eq(scala.collection.Iterator<java.lang.Object> x$1)
protected abstract static void akka$actor$FSM$_setter_$akka$actor$FSM$$stateFunctions_$eq(scala.collection.mutable.Map<S,scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>>> x$1)
protected abstract static void akka$actor$FSM$_setter_$akka$actor$FSM$$stateTimeouts_$eq(scala.collection.mutable.Map<S,scala.Option<scala.concurrent.duration.FiniteDuration>> x$1)
protected abstract static void akka$actor$FSM$_setter_$akka$actor$FSM$$handleEventDefault_$eq(scala.PartialFunction<FSM.Event<D>,FSM.State<S,D>> x$1)
public FSM.Event$ Event()
Event
in interface FSM<TimerBasedThrottler.State,TimerBasedThrottler.Data>
public FSM.StopEvent$ StopEvent()
StopEvent
in interface FSM<TimerBasedThrottler.State,TimerBasedThrottler.Data>
public FSM.StateTimeout$ StateTimeout()
FSM
StateTimeout
in interface FSM<TimerBasedThrottler.State,TimerBasedThrottler.Data>
protected java.util.Set<ActorRef> listeners()
public ActorContext context()
Actor
forward
.
WARNING: Only valid within the Actor itself, so do not close over it and publish it to other threads!
ActorContext
is the Scala API. getContext
returns a
UntypedActorContext
, which is the Java API of the actor
context.
public final ActorRef self()
Actor
self ! message
public Throttler.Rate rate()