2018-10-29 17:19:37 +08:00
|
|
|
/*
|
2021-01-08 17:55:38 +01:00
|
|
|
* Copyright (C) 2015-2021 Lightbend Inc. <https://www.lightbend.com>
|
2015-05-08 14:02:55 +02:00
|
|
|
*/
|
2018-03-13 23:45:55 +09:00
|
|
|
|
2015-05-08 14:02:55 +02:00
|
|
|
package akka.pattern
|
|
|
|
|
|
2020-04-27 20:32:18 +08:00
|
|
|
import java.util.concurrent.ThreadLocalRandom
|
|
|
|
|
import java.util.concurrent.TimeoutException
|
|
|
|
|
|
2015-05-08 14:02:55 +02:00
|
|
|
import scala.concurrent.Promise
|
|
|
|
|
import scala.concurrent.duration._
|
2020-04-27 20:32:18 +08:00
|
|
|
|
2015-05-08 14:02:55 +02:00
|
|
|
import akka.actor.Actor
|
|
|
|
|
import akka.actor.ActorLogging
|
|
|
|
|
import akka.actor.Props
|
|
|
|
|
import akka.actor.Status.Failure
|
|
|
|
|
import akka.testkit.AkkaSpec
|
|
|
|
|
import akka.testkit.ImplicitSender
|
|
|
|
|
|
|
|
|
|
object CircuitBreakerStressSpec {
|
|
|
|
|
case object JobDone
|
|
|
|
|
case object GetResult
|
|
|
|
|
case class Result(doneCount: Int, timeoutCount: Int, failCount: Int, circCount: Int)
|
|
|
|
|
|
|
|
|
|
class StressActor(breaker: CircuitBreaker) extends Actor with ActorLogging with PipeToSupport {
|
|
|
|
|
import context.dispatcher
|
|
|
|
|
|
|
|
|
|
private var doneCount = 0
|
|
|
|
|
private var timeoutCount = 0
|
|
|
|
|
private var failCount = 0
|
|
|
|
|
private var circCount = 0
|
|
|
|
|
|
|
|
|
|
private def job = {
|
|
|
|
|
val promise = Promise[JobDone.type]()
|
|
|
|
|
|
|
|
|
|
context.system.scheduler.scheduleOnce(ThreadLocalRandom.current.nextInt(300).millisecond) {
|
|
|
|
|
promise.success(JobDone)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
promise.future
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def receive = {
|
2019-02-09 15:25:39 +01:00
|
|
|
case JobDone =>
|
2015-05-08 14:02:55 +02:00
|
|
|
doneCount += 1
|
|
|
|
|
breaker.withCircuitBreaker(job).pipeTo(self)
|
2019-02-09 15:25:39 +01:00
|
|
|
case Failure(_: CircuitBreakerOpenException) =>
|
2015-05-08 14:02:55 +02:00
|
|
|
circCount += 1
|
|
|
|
|
breaker.withCircuitBreaker(job).pipeTo(self)
|
2019-02-09 15:25:39 +01:00
|
|
|
case Failure(_: TimeoutException) =>
|
2015-05-08 14:02:55 +02:00
|
|
|
timeoutCount += 1
|
|
|
|
|
breaker.withCircuitBreaker(job).pipeTo(self)
|
2019-02-09 15:25:39 +01:00
|
|
|
case Failure(_) =>
|
2015-05-08 14:02:55 +02:00
|
|
|
failCount += 1
|
|
|
|
|
breaker.withCircuitBreaker(job).pipeTo(self)
|
2019-02-09 15:25:39 +01:00
|
|
|
case GetResult =>
|
2015-05-08 14:02:55 +02:00
|
|
|
sender() ! Result(doneCount, timeoutCount, failCount, circCount)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reproducer for issue #17415
|
|
|
|
|
class CircuitBreakerStressSpec extends AkkaSpec with ImplicitSender {
|
|
|
|
|
import CircuitBreakerStressSpec._
|
|
|
|
|
|
|
|
|
|
muteDeadLetters(classOf[AnyRef])(system)
|
|
|
|
|
|
|
|
|
|
"A CircuitBreaker" in {
|
|
|
|
|
val breaker = CircuitBreaker(system.scheduler, 5, 200.millisecond, 200.seconds)
|
|
|
|
|
val stressActors = Vector.fill(3) {
|
|
|
|
|
system.actorOf(Props(classOf[StressActor], breaker))
|
|
|
|
|
}
|
2019-02-09 15:25:39 +01:00
|
|
|
for (_ <- 0 to 1000; a <- stressActors) {
|
2015-05-08 14:02:55 +02:00
|
|
|
a ! JobDone
|
|
|
|
|
}
|
|
|
|
|
// let them work for a while
|
|
|
|
|
Thread.sleep(3000)
|
2019-02-09 15:25:39 +01:00
|
|
|
stressActors.foreach { a =>
|
2015-05-08 14:02:55 +02:00
|
|
|
a ! GetResult
|
|
|
|
|
val result = expectMsgType[Result]
|
|
|
|
|
result.failCount should be(0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|