Post revision changes:
- Added reference to the circuit breaker pattern function in the CircuitBreakerActor scaladoc - made `final` classes `OpenCircuitException` and `CircuitBreakerActor` - Renamed `CircuitBreakerActorBuilder` into `CircuitBreakerActorPropsBuilder` and renamed the `propsForTarget` method into `props`
This commit is contained in:
parent
fd5b872a43
commit
b1980992f9
5 changed files with 48 additions and 41 deletions
|
|
@ -11,6 +11,11 @@ import akka.util.Timeout
|
|||
import scala.concurrent.{ ExecutionContext, Future }
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
/**
|
||||
* This is an Actor which implements the circuit breaker pattern,
|
||||
* you may also be interested in the raw circuit breaker [[akka.pattern.CircuitBreaker]]
|
||||
*
|
||||
*/
|
||||
object CircuitBreakerActor {
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +64,7 @@ object CircuitBreakerActor {
|
|||
|
||||
final case class CircuitBreakerStateData(failureCount: Int = 0, firstHalfOpenMessageSent: Boolean = false)
|
||||
|
||||
final case class CircuitBreakerActorBuilder(
|
||||
final case class CircuitBreakerActorPropsBuilder(
|
||||
maxFailures: Int, callTimeout: Timeout, resetTimeout: Timeout,
|
||||
circuitEventListener: Option[ActorRef] = None,
|
||||
failureDetector: Any ⇒ Boolean = { _ ⇒ false },
|
||||
|
|
@ -70,11 +75,11 @@ object CircuitBreakerActor {
|
|||
*
|
||||
* @param target the target actor ref
|
||||
*/
|
||||
def propsForTarget(target: ActorRef) = CircuitBreakerActor.props(target, maxFailures, callTimeout, resetTimeout, circuitEventListener, failureDetector, openCircuitFailureConverter)
|
||||
def props(target: ActorRef) = CircuitBreakerActor.props(target, maxFailures, callTimeout, resetTimeout, circuitEventListener, failureDetector, openCircuitFailureConverter)
|
||||
|
||||
}
|
||||
|
||||
class OpenCircuitException extends Exception("Circuit Open so unable to complete operation")
|
||||
final class OpenCircuitException extends Exception("Circuit Open so unable to complete operation")
|
||||
|
||||
/**
|
||||
* Extends [[scala.concurrent.Future]] with the method failForOpenCircuitWith to handle
|
||||
|
|
@ -106,7 +111,7 @@ object CircuitBreakerInternalEvents {
|
|||
import CircuitBreakerActor._
|
||||
import CircuitBreakerInternalEvents._
|
||||
|
||||
class CircuitBreakerActor(
|
||||
final class CircuitBreakerActor(
|
||||
target: ActorRef,
|
||||
maxFailures: Int,
|
||||
callTimeout: Timeout,
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import scala.language.postfixOps
|
|||
|
||||
class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
||||
|
||||
val baseCircuitBreakerBuilder =
|
||||
CircuitBreakerActorBuilder(
|
||||
val baseCircuitBreakerPropsBuilder =
|
||||
CircuitBreakerActorPropsBuilder(
|
||||
maxFailures = 2,
|
||||
callTimeout = 200 millis,
|
||||
resetTimeout = 1 second,
|
||||
|
|
@ -29,7 +29,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
|
||||
def circuitBreaker: ActorRef
|
||||
|
||||
def defaultCircuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
def defaultCircuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
def receiverRespondsWithFailureToRequest(request: Any) = {
|
||||
sender.send(circuitBreaker, request)
|
||||
|
|
@ -45,9 +45,11 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
sender.expectMsg(reply)
|
||||
}
|
||||
|
||||
def waitForCircuitBreakerToReceiveSelfNotificationMessage = Thread.sleep(baseCircuitBreakerBuilder.resetTimeout.duration.toMillis / 4)
|
||||
def circuitBreakerToReceivesSelfNotificationMessage =
|
||||
receiver.expectNoMsg(baseCircuitBreakerPropsBuilder.resetTimeout.duration / 4)
|
||||
|
||||
def waitForResetTimeoutToExpire = Thread.sleep(baseCircuitBreakerBuilder.resetTimeout.duration.toMillis + 100)
|
||||
def resetTimeoutToExpires =
|
||||
receiver.expectNoMsg(baseCircuitBreakerPropsBuilder.resetTimeout.duration + 100.millis)
|
||||
|
||||
def messageIsRejectedWithOpenCircuitNotification(message: Any) = {
|
||||
sender.send(circuitBreaker, message)
|
||||
|
|
@ -63,7 +65,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
val sender = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender.send(circuitBreaker, "test message")
|
||||
|
||||
|
|
@ -74,7 +76,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
|
||||
val sender = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender.send(circuitBreaker, "test message")
|
||||
|
||||
|
|
@ -87,7 +89,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"forward further messages before receiving the response of the first one" in {
|
||||
val sender = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender.send(circuitBreaker, "test message1")
|
||||
sender.send(circuitBreaker, "test message2")
|
||||
|
|
@ -102,7 +104,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
val sender1 = TestProbe()
|
||||
val sender2 = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender1.send(circuitBreaker, "test message1")
|
||||
sender2.send(circuitBreaker, "test message2")
|
||||
|
|
@ -120,7 +122,7 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"return failed responses too" in {
|
||||
val sender = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender.send(circuitBreaker, "request")
|
||||
|
||||
|
|
@ -133,11 +135,11 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"enter open state after reaching the threshold of failed responses" in new CircuitBreakerScenario {
|
||||
val circuitBreaker = defaultCircuitBreaker
|
||||
|
||||
(1 to baseCircuitBreakerBuilder.maxFailures) foreach { index ⇒
|
||||
(1 to baseCircuitBreakerPropsBuilder.maxFailures) foreach { index ⇒
|
||||
receiverRespondsWithFailureToRequest(s"request$index")
|
||||
}
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
sender.send(circuitBreaker, "request in open state")
|
||||
receiver.expectNoMsg
|
||||
|
|
@ -146,11 +148,11 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"respond with a CircuitOpenFailure message when in open state " in new CircuitBreakerScenario {
|
||||
val circuitBreaker = defaultCircuitBreaker
|
||||
|
||||
(1 to baseCircuitBreakerBuilder.maxFailures) foreach { index ⇒
|
||||
(1 to baseCircuitBreakerPropsBuilder.maxFailures) foreach { index ⇒
|
||||
receiverRespondsWithFailureToRequest(s"request$index")
|
||||
}
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
sender.send(circuitBreaker, "request in open state")
|
||||
sender.expectMsg(CircuitOpenFailure("request in open state"))
|
||||
|
|
@ -158,15 +160,15 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
|
||||
"respond with the converted CircuitOpenFailure if a converter is provided" in new CircuitBreakerScenario {
|
||||
val circuitBreaker = system.actorOf(
|
||||
baseCircuitBreakerBuilder
|
||||
baseCircuitBreakerPropsBuilder
|
||||
.copy(openCircuitFailureConverter = { failureMsg ⇒ s"NOT SENT: ${failureMsg.failedMsg}" })
|
||||
.propsForTarget(receiver.ref))
|
||||
.props(receiver.ref))
|
||||
|
||||
(1 to baseCircuitBreakerBuilder.maxFailures) foreach { index ⇒
|
||||
(1 to baseCircuitBreakerPropsBuilder.maxFailures) foreach { index ⇒
|
||||
receiverRespondsWithFailureToRequest(s"request$index")
|
||||
}
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
sender.send(circuitBreaker, "request in open state")
|
||||
sender.expectMsg("NOT SENT: request in open state")
|
||||
|
|
@ -175,12 +177,12 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"enter open state after reaching the threshold of timed-out responses" in {
|
||||
val sender = TestProbe()
|
||||
val receiver = TestProbe()
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerBuilder.propsForTarget(receiver.ref))
|
||||
val circuitBreaker = system.actorOf(baseCircuitBreakerPropsBuilder.props(target = receiver.ref))
|
||||
|
||||
sender.send(circuitBreaker, "request1")
|
||||
sender.send(circuitBreaker, "request2")
|
||||
|
||||
Thread.sleep(baseCircuitBreakerBuilder.callTimeout.duration.toMillis + 100)
|
||||
Thread.sleep(baseCircuitBreakerPropsBuilder.callTimeout.duration.toMillis + 100)
|
||||
|
||||
receiver.expectMsg("request1")
|
||||
receiver.reply("this should be timed out 1")
|
||||
|
|
@ -203,14 +205,14 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
receiverRespondsWithFailureToRequest("request1")
|
||||
receiverRespondsWithFailureToRequest("request2")
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
Then("Messages are ignored")
|
||||
messageIsRejectedWithOpenCircuitNotification("IGNORED SINCE IN OPEN STATE1")
|
||||
messageIsRejectedWithOpenCircuitNotification("IGNORED SINCE IN OPEN STATE2")
|
||||
|
||||
When("ENTERING HALF OPEN STATE")
|
||||
waitForResetTimeoutToExpire
|
||||
resetTimeoutToExpires
|
||||
|
||||
Then("First message should be forwarded, following ones ignored if the failure persist")
|
||||
sender.send(circuitBreaker, "First message in half-open state, should be forwarded")
|
||||
|
|
@ -231,12 +233,12 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
receiverRespondsWithFailureToRequest("request1")
|
||||
receiverRespondsWithFailureToRequest("request2")
|
||||
|
||||
waitForResetTimeoutToExpire
|
||||
resetTimeoutToExpires
|
||||
|
||||
And("Receiving a successful response")
|
||||
receiverRespondsToRequestWith("First message in half-open state, should be forwarded", "This should close the circuit")
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
Then("circuit is re-closed")
|
||||
sender.send(circuitBreaker, "request1")
|
||||
|
|
@ -255,12 +257,12 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
receiverRespondsWithFailureToRequest("request1")
|
||||
receiverRespondsWithFailureToRequest("request2")
|
||||
|
||||
waitForResetTimeoutToExpire
|
||||
resetTimeoutToExpires
|
||||
|
||||
And("Receiving a failure response")
|
||||
receiverRespondsWithFailureToRequest("First message in half-open state, should be forwarded")
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
Then("circuit is opened again")
|
||||
sender.send(circuitBreaker, "this should be ignored")
|
||||
|
|
@ -272,21 +274,21 @@ class CircuitBreakerActorSpec extends AkkaSpec() with GivenWhenThen {
|
|||
"Notify an event status change listener when changing state" in new CircuitBreakerScenario {
|
||||
Given("A circuit breaker actor pointing to a test probe")
|
||||
override val circuitBreaker = system.actorOf(
|
||||
baseCircuitBreakerBuilder
|
||||
baseCircuitBreakerPropsBuilder
|
||||
.copy(circuitEventListener = Some(eventListener.ref))
|
||||
.propsForTarget(receiver.ref))
|
||||
.props(target = receiver.ref))
|
||||
|
||||
When("Entering OPEN state")
|
||||
receiverRespondsWithFailureToRequest("request1")
|
||||
receiverRespondsWithFailureToRequest("request2")
|
||||
|
||||
waitForCircuitBreakerToReceiveSelfNotificationMessage
|
||||
circuitBreakerToReceivesSelfNotificationMessage
|
||||
|
||||
Then("An event is sent")
|
||||
eventListener.expectMsg(CircuitOpen(circuitBreaker))
|
||||
|
||||
When("Entering HALF OPEN state")
|
||||
waitForResetTimeoutToExpire
|
||||
resetTimeoutToExpires
|
||||
|
||||
Then("An event is sent")
|
||||
eventListener.expectMsg(CircuitHalfOpen(circuitBreaker))
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package akka.contrib.circuitbreaker.sample
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, ActorRef }
|
||||
import akka.contrib.circuitbreaker.CircuitBreakerActor.{ CircuitBreakerActorBuilder, CircuitOpenFailure }
|
||||
import akka.contrib.circuitbreaker.CircuitBreakerActor.{ CircuitBreakerActorPropsBuilder, CircuitOpenFailure }
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ class CircuitBreaker(potentiallyFailingService: ActorRef) extends Actor with Act
|
|||
|
||||
val serviceCircuitBreaker =
|
||||
context.actorOf(
|
||||
CircuitBreakerActorBuilder(maxFailures = 3, callTimeout = 2.seconds, resetTimeout = 30.seconds)
|
||||
CircuitBreakerActorPropsBuilder(maxFailures = 3, callTimeout = 2.seconds, resetTimeout = 30.seconds)
|
||||
.copy(
|
||||
failureDetector = {
|
||||
_ match {
|
||||
|
|
@ -19,7 +19,7 @@ class CircuitBreaker(potentiallyFailingService: ActorRef) extends Actor with Act
|
|||
case _ ⇒ false
|
||||
}
|
||||
})
|
||||
.propsForTarget(potentiallyFailingService),
|
||||
.props(potentiallyFailingService),
|
||||
"serviceCircuitBreaker")
|
||||
|
||||
override def receive: Receive = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package akka.contrib.circuitbreaker.sample
|
||||
|
||||
import akka.actor.{ Actor, ActorLogging, ActorRef }
|
||||
import akka.contrib.circuitbreaker.CircuitBreakerActor.CircuitBreakerActorBuilder
|
||||
import akka.contrib.circuitbreaker.CircuitBreakerActor.CircuitBreakerActorPropsBuilder
|
||||
import akka.util.Timeout
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
|
@ -16,7 +16,7 @@ class CircuitBreakerAsk(potentiallyFailingService: ActorRef) extends Actor with
|
|||
|
||||
val serviceCircuitBreaker =
|
||||
context.actorOf(
|
||||
CircuitBreakerActorBuilder(maxFailures = 3, callTimeout = askTimeout, resetTimeout = 30.seconds)
|
||||
CircuitBreakerActorPropsBuilder(maxFailures = 3, callTimeout = askTimeout, resetTimeout = 30.seconds)
|
||||
.copy(
|
||||
failureDetector = {
|
||||
_ match {
|
||||
|
|
@ -28,7 +28,7 @@ class CircuitBreakerAsk(potentiallyFailingService: ActorRef) extends Actor with
|
|||
openCircuitFailureConverter = { failure ⇒
|
||||
Left(s"Circuit open when processing ${failure.failedMsg}")
|
||||
})
|
||||
.propsForTarget(potentiallyFailingService),
|
||||
.props(potentiallyFailingService),
|
||||
"serviceCircuitBreaker")
|
||||
|
||||
import context.dispatcher
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class CircuitBreakerAskWithFailure(potentiallyFailingService: ActorRef) extends
|
|||
|
||||
val serviceCircuitBreaker =
|
||||
context.actorOf(
|
||||
CircuitBreakerActorBuilder(maxFailures = 3, callTimeout = askTimeout, resetTimeout = 30.seconds).propsForTarget(potentiallyFailingService),
|
||||
CircuitBreakerActorPropsBuilder(maxFailures = 3, callTimeout = askTimeout, resetTimeout = 30.seconds).props(potentiallyFailingService),
|
||||
"serviceCircuitBreaker")
|
||||
|
||||
import context.dispatcher
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue