#2168 - Exposing more Netty options in remtoe config

This commit is contained in:
Viktor Klang 2012-06-01 21:29:47 +02:00
parent 2940740c48
commit 12b9af25cf
9 changed files with 83 additions and 47 deletions

View file

@ -6,7 +6,7 @@ package akka.pattern
import akka.testkit._ import akka.testkit._
import akka.util.duration._ import akka.util.duration._
import org.scalatest.BeforeAndAfter import org.scalatest.BeforeAndAfter
import akka.dispatch.{Promise, Await, Future} import akka.dispatch.{ Promise, Await, Future }
class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter { class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
@ -17,8 +17,8 @@ class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
val halfOpenLatch = new TestLatch(1) val halfOpenLatch = new TestLatch(1)
val breaker = new CircuitBreaker(system.scheduler,5,100.millis.dilated,500.millis.dilated) val breaker = new CircuitBreaker(system.scheduler, 5, 100.millis.dilated, 500.millis.dilated)
.onHalfOpen(halfOpenLatch.countDown()) .onHalfOpen(halfOpenLatch.countDown())
} }
@ -28,30 +28,30 @@ class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
def unreliableCall(param: String) = { def unreliableCall(param: String) = {
param match { param match {
case "fail" => throw new RuntimeException("FAIL") case "fail" throw new RuntimeException("FAIL")
case _ => param case _ param
} }
} }
def openBreaker: Unit = { def openBreaker: Unit = {
for (i <- 1 to 5) for (i 1 to 5)
Await.result(breakers.breaker.withCircuitBreaker(Future(unreliableCall("fail"))) recoverWith { Await.result(breakers.breaker.withCircuitBreaker(Future(unreliableCall("fail"))) recoverWith {
case _ => Promise.successful("OK") case _ Promise.successful("OK")
}, 1.second.dilated) }, 1.second.dilated)
} }
"A circuit breaker being called by many threads" must { "A circuit breaker being called by many threads" must {
"allow many calls while in closed state with no errors" in { "allow many calls while in closed state with no errors" in {
val futures = for (i <- 1 to 100) yield breakers.breaker.withCircuitBreaker(Future {Thread.sleep(10); unreliableCall("succeed")}) val futures = for (i 1 to 100) yield breakers.breaker.withCircuitBreaker(Future { Thread.sleep(10); unreliableCall("succeed") })
val futureList = Future.sequence(futures) val futureList = Future.sequence(futures)
val result = Await.result(futureList, 1.second.dilated) val result = Await.result(futureList, 1.second.dilated)
result.size must be (100) result.size must be(100)
result.distinct.size must be (1) result.distinct.size must be(1)
result.distinct must contain ("succeed") result.distinct must contain("succeed")
} }
@ -59,19 +59,19 @@ class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
openBreaker openBreaker
val futures = for (i <- 1 to 100) yield breakers.breaker.withCircuitBreaker(Future { val futures = for (i 1 to 100) yield breakers.breaker.withCircuitBreaker(Future {
Thread.sleep(10); unreliableCall("success") Thread.sleep(10); unreliableCall("success")
}) recoverWith { }) recoverWith {
case _: CircuitBreakerOpenException => Promise.successful("CBO") case _: CircuitBreakerOpenException Promise.successful("CBO")
} }
val futureList = Future.sequence(futures) val futureList = Future.sequence(futures)
val result = Await.result(futureList, 1.second.dilated) val result = Await.result(futureList, 1.second.dilated)
result.size must be (100) result.size must be(100)
result.distinct.size must be (1) result.distinct.size must be(1)
result.distinct must contain ("CBO") result.distinct must contain("CBO")
} }
"allow a single call through in half-open state" in { "allow a single call through in half-open state" in {
@ -79,20 +79,20 @@ class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
Await.ready(breakers.halfOpenLatch, 2.seconds.dilated) Await.ready(breakers.halfOpenLatch, 2.seconds.dilated)
val futures = for (i <- 1 to 100) yield breakers.breaker.withCircuitBreaker(Future { val futures = for (i 1 to 100) yield breakers.breaker.withCircuitBreaker(Future {
Thread.sleep(10); unreliableCall("succeed") Thread.sleep(10); unreliableCall("succeed")
}) recoverWith { }) recoverWith {
case _: CircuitBreakerOpenException => Promise.successful("CBO") case _: CircuitBreakerOpenException Promise.successful("CBO")
} }
val futureList = Future.sequence(futures) val futureList = Future.sequence(futures)
val result = Await.result(futureList, 1.second.dilated) val result = Await.result(futureList, 1.second.dilated)
result.size must be (100) result.size must be(100)
result.distinct.size must be (2) result.distinct.size must be(2)
result.distinct must contain ("succeed") result.distinct must contain("succeed")
result.distinct must contain ("CBO") result.distinct must contain("CBO")
} }
"recover and reset the breaker after the reset timeout" in { "recover and reset the breaker after the reset timeout" in {
@ -102,19 +102,19 @@ class CircuitBreakerMTSpec extends AkkaSpec with BeforeAndAfter {
Await.ready(breakers.breaker.withCircuitBreaker(Future(unreliableCall("succeed"))), 1.second.dilated) Await.ready(breakers.breaker.withCircuitBreaker(Future(unreliableCall("succeed"))), 1.second.dilated)
val futures = for (i <- 1 to 100) yield breakers.breaker.withCircuitBreaker(Future { val futures = for (i 1 to 100) yield breakers.breaker.withCircuitBreaker(Future {
Thread.sleep(10); unreliableCall("succeed") Thread.sleep(10); unreliableCall("succeed")
}) recoverWith { }) recoverWith {
case _: CircuitBreakerOpenException => Promise.successful("CBO") case _: CircuitBreakerOpenException Promise.successful("CBO")
} }
val futureList = Future.sequence(futures) val futureList = Future.sequence(futures)
val result = Await.result(futureList, 1.second.dilated) val result = Await.result(futureList, 1.second.dilated)
result.size must be (100) result.size must be(100)
result.distinct.size must be (1) result.distinct.size must be(1)
result.distinct must contain ("succeed") result.distinct must contain("succeed")
} }
} }

View file

@ -142,7 +142,7 @@ class CircuitBreaker(scheduler: Scheduler, maxFailures: Int, callTimeout: Durati
catch { catch {
case NonFatal(t) Promise.failed(t)(CircuitBreaker.syncExecutionContext) case NonFatal(t) Promise.failed(t)(CircuitBreaker.syncExecutionContext)
} }
}),callTimeout) }), callTimeout)
} }
/** /**

View file

@ -5,7 +5,7 @@
package docs.circuitbreaker package docs.circuitbreaker
//#imports1 //#imports1
import akka.util.duration._ // small d is important here import akka.util.duration._ // small d is important here
import akka.pattern.CircuitBreaker import akka.pattern.CircuitBreaker
import akka.actor.Actor import akka.actor.Actor
import akka.dispatch.Future import akka.dispatch.Future
@ -13,7 +13,7 @@ import akka.event.Logging
//#imports1 //#imports1
class CircuitBreakerDocSpec { } class CircuitBreakerDocSpec {}
//#circuit-breaker-initialization //#circuit-breaker-initialization
class DangerousActor extends Actor { class DangerousActor extends Actor {
@ -26,18 +26,18 @@ class DangerousActor extends Actor {
def notifyMeOnOpen = def notifyMeOnOpen =
log.warning("My CircuitBreaker is now open, and will not close for one minute") log.warning("My CircuitBreaker is now open, and will not close for one minute")
//#circuit-breaker-initialization //#circuit-breaker-initialization
//#circuit-breaker-usage //#circuit-breaker-usage
def dangerousCall: String = "This really isn't that dangerous of a call after all" def dangerousCall: String = "This really isn't that dangerous of a call after all"
def receive = { def receive = {
case "is my middle name" => case "is my middle name"
sender ! breaker.withCircuitBreaker(Future(dangerousCall)) sender ! breaker.withCircuitBreaker(Future(dangerousCall))
case "block for me" => case "block for me"
sender ! breaker.withSyncCircuitBreaker(dangerousCall) sender ! breaker.withSyncCircuitBreaker(dangerousCall)
} }
//#circuit-breaker-usage //#circuit-breaker-usage
} }

View file

@ -69,21 +69,21 @@ class MyMessageQueue(_owner: ActorContext)
val storage = new QueueStorage val storage = new QueueStorage
// A real-world implmentation would use configuration to set the last // A real-world implmentation would use configuration to set the last
// three parameters below // three parameters below
val breaker = CircuitBreaker(_owner.system.scheduler,5,30.seconds,1.minute) val breaker = CircuitBreaker(_owner.system.scheduler, 5, 30.seconds, 1.minute)
def enqueue(receiver: ActorRef, envelope: Envelope): Unit = breaker.withSyncCircuitBreaker { def enqueue(receiver: ActorRef, envelope: Envelope): Unit = breaker.withSyncCircuitBreaker {
val data: Array[Byte] = serialize(envelope) val data: Array[Byte] = serialize(envelope)
storage.push(data) storage.push(data)
} }
def dequeue(): Envelope = breaker.withSyncCircuitBreaker { def dequeue(): Envelope = breaker.withSyncCircuitBreaker {
val data: Option[Array[Byte]] = storage.pull() val data: Option[Array[Byte]] = storage.pull()
data.map(deserialize).orNull data.map(deserialize).orNull
} }
def hasMessages: Boolean = breaker.withSyncCircuitBreaker { !storage.isEmpty } def hasMessages: Boolean = breaker.withSyncCircuitBreaker { !storage.isEmpty }
def numberOfMessages: Int = breaker.withSyncCircuitBreaker { storage.size } def numberOfMessages: Int = breaker.withSyncCircuitBreaker { storage.size }
/** /**
* Called when the mailbox is disposed. * Called when the mailbox is disposed.

View file

@ -133,6 +133,15 @@ akka {
# (I) Maximum total size of all channels, 0 for off # (I) Maximum total size of all channels, 0 for off
max-total-memory-size = 0b max-total-memory-size = 0b
# (I&O) Sets the high water mark for the in and outbound sockets, set to 0b for platform default
write-buffer-high-water-mark = 0b
# (I&O) Sets the send buffer size of the Sockets, set to 0b for platform default
send-buffer-size = 0b
# (I&O) Sets the receive buffer size of the Sockets, set to 0b for platform default
receive-buffer-size = 0b
# (O) Time between reconnect attempts for active clients # (O) Time between reconnect attempts for active clients
reconnect-delay = 5s reconnect-delay = 5s

View file

@ -147,6 +147,12 @@ private[akka] class ActiveRemoteClient private[akka] (
b.setOption("tcpNoDelay", true) b.setOption("tcpNoDelay", true)
b.setOption("keepAlive", true) b.setOption("keepAlive", true)
b.setOption("connectTimeoutMillis", settings.ConnectionTimeout.toMillis) b.setOption("connectTimeoutMillis", settings.ConnectionTimeout.toMillis)
if (settings.ReceiveBufferSize.isDefined)
b.setOption("receiveBufferSize", settings.ReceiveBufferSize.get)
if (settings.SendBufferSize.isDefined)
b.setOption("sendBufferSize", settings.SendBufferSize.get)
if (settings.WriteBufferHighWaterMark.isDefined)
b.setOption("writeBufferHighWaterMark", settings.WriteBufferHighWaterMark.get)
settings.OutboundLocalAddress.foreach(s b.setOption("localAddress", new InetSocketAddress(s, 0))) settings.OutboundLocalAddress.foreach(s b.setOption("localAddress", new InetSocketAddress(s, 0)))
bootstrap = b bootstrap = b

View file

@ -45,6 +45,12 @@ private[akka] class NettyRemoteServer(val netty: NettyRemoteTransport) {
b.setOption("tcpNoDelay", true) b.setOption("tcpNoDelay", true)
b.setOption("child.keepAlive", true) b.setOption("child.keepAlive", true)
b.setOption("reuseAddress", true) b.setOption("reuseAddress", true)
if (settings.ReceiveBufferSize.isDefined)
b.setOption("receiveBufferSize", settings.ReceiveBufferSize.get)
if (settings.SendBufferSize.isDefined)
b.setOption("sendBufferSize", settings.SendBufferSize.get)
if (settings.WriteBufferHighWaterMark.isDefined)
b.setOption("writeBufferHighWaterMark", settings.WriteBufferHighWaterMark.get)
b b
} }

View file

@ -37,8 +37,20 @@ private[akka] class NettySettings(config: Config, val systemName: String) {
val WriteTimeout: Duration = Duration(getMilliseconds("write-timeout"), MILLISECONDS) val WriteTimeout: Duration = Duration(getMilliseconds("write-timeout"), MILLISECONDS)
val AllTimeout: Duration = Duration(getMilliseconds("all-timeout"), MILLISECONDS) val AllTimeout: Duration = Duration(getMilliseconds("all-timeout"), MILLISECONDS)
val ReconnectDelay: Duration = Duration(getMilliseconds("reconnect-delay"), MILLISECONDS) val ReconnectDelay: Duration = Duration(getMilliseconds("reconnect-delay"), MILLISECONDS)
val MessageFrameSize: Int = getBytes("message-frame-size").toInt val MessageFrameSize: Int = getBytes("message-frame-size").toInt
private[this] def optionSize(s: String): Option[Int] = getBytes(s).toInt match {
case 0 None
case x if x < 0
throw new ConfigurationException("Setting '%s' must be 0 or positive (and fit in an Int)" format s)
case other Some(other)
}
val WriteBufferHighWaterMark: Option[Int] = optionSize("write-buffer-high-water-mark")
val SendBufferSize: Option[Int] = optionSize("send-buffer-size")
val ReceiveBufferSize: Option[Int] = optionSize("receive-buffer-size")
val Hostname: String = getString("hostname") match { val Hostname: String = getString("hostname") match {
case "" InetAddress.getLocalHost.getHostAddress case "" InetAddress.getLocalHost.getHostAddress
case value value case value value

View file

@ -56,6 +56,9 @@ class RemoteConfigSpec extends AkkaSpec(
WriteTimeout must be(10 seconds) WriteTimeout must be(10 seconds)
AllTimeout must be(0 millis) AllTimeout must be(0 millis)
ReconnectionTimeWindow must be(10 minutes) ReconnectionTimeWindow must be(10 minutes)
WriteBufferHighWaterMark must be(None)
SendBufferSize must be(None)
ReceiveBufferSize must be(None)
} }
} }