* Add Circuit Breaker to akka.pattern for general use. Circuit breaker implementation as described by Michael T. Nygard in Release It!. Fixes #1734

* Uses finite state machine for three states: Closed, Open, Half-Open
    * Closed state allows calls through, and on sequential failures exceeding the max# set - transitions to Open state. Intervening successes cause the failure count to reset to 0
    * Open state throws a CircuitOpenException on every call until the reset timeout is reached which causes a transition to Half-Open state
    * Half-Open state will allow the next single call through, if it succeeds - transition to Closed state, if it fails - transition back to Open state, starting the reset timer again
  * Allow configuration for the call and reset timeouts, as well as the maximum number of sequential failures before opening
  * Supports async or synchronous call protection
  * Callbacks are supported for state entry into Closed, Open, Half-Open.  These are run in the supplied execution context
  * Both thrown exceptions and calls exceeding max call time are considered failures
  * Uses akka scheduler for timer events
  * Integrated into File-Based durable mailbox
  * Sample documented for other durable mailboxes
This commit is contained in:
Brian Scully 2012-06-01 08:24:47 -04:00
parent 85c263e077
commit 6a415f0e9b
15 changed files with 1266 additions and 25 deletions

View file

@ -50,6 +50,8 @@ import akka.dispatch.MailboxType
import akka.dispatch.MessageQueue
import akka.actor.mailbox.DurableMessageQueue
import akka.actor.mailbox.DurableMessageSerialization
import akka.pattern.CircuitBreaker
import akka.util.duration._
class MyMailboxType(systemSettings: ActorSystem.Settings, config: Config)
extends MailboxType {
@ -65,20 +67,23 @@ class MyMessageQueue(_owner: ActorContext)
extends DurableMessageQueue(_owner) with DurableMessageSerialization {
val storage = new QueueStorage
// A real-world implmentation would use configuration to set the last
// three parameters below
val breaker = CircuitBreaker(_owner.system.scheduler,5,30.seconds,1.minute)
def enqueue(receiver: ActorRef, envelope: Envelope) {
def enqueue(receiver: ActorRef, envelope: Envelope): Unit = breaker.withSyncCircuitBreaker {
val data: Array[Byte] = serialize(envelope)
storage.push(data)
}
def dequeue(): Envelope = {
def dequeue(): Envelope = breaker.withSyncCircuitBreaker {
val data: Option[Array[Byte]] = storage.pull()
data.map(deserialize).orNull
}
def hasMessages: Boolean = !storage.isEmpty
def hasMessages: Boolean = breaker.withSyncCircuitBreaker { !storage.isEmpty }
def numberOfMessages: Int = storage.size
def numberOfMessages: Int = breaker.withSyncCircuitBreaker { storage.size }
/**
* Called when the mailbox is disposed.