rewrite mailbox selection logic, see #3342
- add “mailbox-requirement” key to dispatcher section - split out mailbox section, add akka.actor.default-mailbox - rewrite findMarker method and use it for Props.create() and getting the required mailbox of an actor - add ProducesMessageQueue trait for MailboxType so that requirements can be checked before trying to create the actor for real - verify actor as well as dispatcher requirements for message queue before creation, even in remote-deployed case - change MessageDispatcher constructor to take a Configurator, add that to migration guide
This commit is contained in:
parent
51ed174432
commit
f317aaf711
37 changed files with 843 additions and 419 deletions
|
|
@ -47,6 +47,14 @@ public class ActorCreationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static interface I<T> extends Creator<UntypedActor> {}
|
||||||
|
static class F implements I<Object> {
|
||||||
|
@Override
|
||||||
|
public UntypedActor create() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRightCreator() {
|
public void testRightCreator() {
|
||||||
final Props p = Props.create(new C());
|
final Props p = Props.create(new C());
|
||||||
|
|
@ -65,4 +73,10 @@ public class ActorCreationTest {
|
||||||
assertEquals(UntypedActor.class, p.actorClass());
|
assertEquals(UntypedActor.class, p.actorClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuperinterface() {
|
||||||
|
final Props p = Props.create(new F());
|
||||||
|
assertEquals(UntypedActor.class, p.actorClass());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,13 @@ object ActorMailboxSpec {
|
||||||
mailbox-type = "akka.dispatch.BoundedMailbox"
|
mailbox-type = "akka.dispatch.BoundedMailbox"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requiring-bounded-dispatcher {
|
||||||
|
mailbox-capacity = 1000
|
||||||
|
mailbox-push-timeout-time = 10s
|
||||||
|
mailbox-type = "akka.dispatch.BoundedMailbox"
|
||||||
|
mailbox-requirement = "akka.dispatch.BoundedMessageQueueSemantics"
|
||||||
|
}
|
||||||
|
|
||||||
unbounded-mailbox {
|
unbounded-mailbox {
|
||||||
mailbox-type = "akka.dispatch.UnboundedMailbox"
|
mailbox-type = "akka.dispatch.UnboundedMailbox"
|
||||||
}
|
}
|
||||||
|
|
@ -80,10 +87,32 @@ object ActorMailboxSpec {
|
||||||
dispatcher = bounded-dispatcher
|
dispatcher = bounded-dispatcher
|
||||||
mailbox = unbounded-mailbox
|
mailbox = unbounded-mailbox
|
||||||
}
|
}
|
||||||
|
/bounded-deque-requirements-configured {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
|
mailbox = akka.actor.mailbox.bounded-deque-based
|
||||||
|
}
|
||||||
|
/bounded-deque-require-unbounded-configured {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
|
mailbox = akka.actor.mailbox.unbounded-deque-based
|
||||||
|
}
|
||||||
|
/bounded-deque-require-unbounded-unconfigured {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
|
}
|
||||||
|
/bounded-deque-requirements-configured-props-disp {
|
||||||
|
mailbox = akka.actor.mailbox.bounded-deque-based
|
||||||
|
}
|
||||||
|
/bounded-deque-require-unbounded-configured-props-disp {
|
||||||
|
mailbox = akka.actor.mailbox.unbounded-deque-based
|
||||||
|
}
|
||||||
|
/bounded-deque-requirements-configured-props-mail {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
|
}
|
||||||
|
/bounded-deque-require-unbounded-configured-props-mail {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
|
}
|
||||||
|
/bounded-deque-require-unbounded-unconfigured-props-mail {
|
||||||
|
dispatcher = requiring-bounded-dispatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
akka.actor.mailbox.requirements {
|
|
||||||
"akka.dispatch.BoundedMessageQueueSemantics" = bounded-mailbox
|
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
@ -97,10 +126,16 @@ object ActorMailboxSpec {
|
||||||
|
|
||||||
class StashQueueReportingActor extends QueueReportingActor with Stash
|
class StashQueueReportingActor extends QueueReportingActor with Stash
|
||||||
|
|
||||||
val UnboundedMailboxTypes = Seq(classOf[QueueBasedMessageQueue], classOf[UnboundedMessageQueueSemantics])
|
val UnboundedMailboxTypes = Seq(classOf[UnboundedMessageQueueSemantics])
|
||||||
val BoundedMailboxTypes = Seq(classOf[QueueBasedMessageQueue], classOf[BoundedMessageQueueSemantics])
|
val BoundedMailboxTypes = Seq(classOf[BoundedMessageQueueSemantics])
|
||||||
val UnboundedDeqMailboxTypes = Seq(classOf[QueueBasedMessageQueue], classOf[DequeBasedMessageQueue],
|
val UnboundedDeqMailboxTypes = Seq(
|
||||||
|
classOf[DequeBasedMessageQueueSemantics],
|
||||||
|
classOf[UnboundedMessageQueueSemantics],
|
||||||
classOf[UnboundedDequeBasedMessageQueueSemantics])
|
classOf[UnboundedDequeBasedMessageQueueSemantics])
|
||||||
|
val BoundedDeqMailboxTypes = Seq(
|
||||||
|
classOf[DequeBasedMessageQueueSemantics],
|
||||||
|
classOf[BoundedMessageQueueSemantics],
|
||||||
|
classOf[BoundedDequeBasedMessageQueueSemantics])
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with DefaultTimeout with ImplicitSender {
|
class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with DefaultTimeout with ImplicitSender {
|
||||||
|
|
@ -122,7 +157,7 @@ class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with Defau
|
||||||
checkMailboxQueue(Props[QueueReportingActor], "default-default", UnboundedMailboxTypes)
|
checkMailboxQueue(Props[QueueReportingActor], "default-default", UnboundedMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
"get an unbounded dequeu message queue when it is only configured on the props" in {
|
"get an unbounded deque message queue when it is only configured on the props" in {
|
||||||
checkMailboxQueue(Props[QueueReportingActor].withMailbox("akka.actor.mailbox.unbounded-deque-based"),
|
checkMailboxQueue(Props[QueueReportingActor].withMailbox("akka.actor.mailbox.unbounded-deque-based"),
|
||||||
"default-override-from-props", UnboundedDeqMailboxTypes)
|
"default-override-from-props", UnboundedDeqMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
@ -132,7 +167,7 @@ class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with Defau
|
||||||
"default-override-from-trait", BoundedMailboxTypes)
|
"default-override-from-trait", BoundedMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
"get an unbounded dequeu message queue when it's only mixed with Stash" in {
|
"get an unbounded deque message queue when it's only mixed with Stash" in {
|
||||||
checkMailboxQueue(Props[StashQueueReportingActor],
|
checkMailboxQueue(Props[StashQueueReportingActor],
|
||||||
"default-override-from-stash", UnboundedDeqMailboxTypes)
|
"default-override-from-stash", UnboundedDeqMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
@ -141,14 +176,12 @@ class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with Defau
|
||||||
checkMailboxQueue(Props[QueueReportingActor], "default-bounded", BoundedMailboxTypes)
|
checkMailboxQueue(Props[QueueReportingActor], "default-bounded", BoundedMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
"get an unbounded dequeu message queue when it's configured as mailbox" in {
|
"get an unbounded deque message queue when it's configured as mailbox" in {
|
||||||
checkMailboxQueue(Props[QueueReportingActor], "default-unbounded-deque", UnboundedDeqMailboxTypes)
|
checkMailboxQueue(Props[QueueReportingActor], "default-unbounded-deque", UnboundedDeqMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
"fail to create actor when an unbounded dequeu message queue is configured as mailbox overriding RequestMailbox" in {
|
"fail to create actor when an unbounded dequeu message queue is configured as mailbox overriding RequestMailbox" in {
|
||||||
filterEvents(EventFilter[ActorInitializationException]()) {
|
intercept[IllegalArgumentException](system.actorOf(Props[BoundedQueueReportingActor], "default-unbounded-deque-override-trait"))
|
||||||
verifyActorTermination(system.actorOf(Props[BoundedQueueReportingActor], "default-unbounded-deque-override-trait"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"get an unbounded message queue when defined in dispatcher" in {
|
"get an unbounded message queue when defined in dispatcher" in {
|
||||||
|
|
@ -156,9 +189,7 @@ class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with Defau
|
||||||
}
|
}
|
||||||
|
|
||||||
"fail to create actor when an unbounded message queue is defined in dispatcher overriding RequestMailbox" in {
|
"fail to create actor when an unbounded message queue is defined in dispatcher overriding RequestMailbox" in {
|
||||||
filterEvents(EventFilter[ActorInitializationException]()) {
|
intercept[IllegalArgumentException](system.actorOf(Props[BoundedQueueReportingActor], "unbounded-default-override-trait"))
|
||||||
verifyActorTermination(system.actorOf(Props[BoundedQueueReportingActor], "unbounded-default-override-trait"))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
"get a bounded message queue when it's configured as mailbox overriding unbounded in dispatcher" in {
|
"get a bounded message queue when it's configured as mailbox overriding unbounded in dispatcher" in {
|
||||||
|
|
@ -183,5 +214,84 @@ class ActorMailboxSpec extends AkkaSpec(ActorMailboxSpec.mailboxConf) with Defau
|
||||||
"bounded-unbounded-override-props", UnboundedMailboxTypes)
|
"bounded-unbounded-override-props", UnboundedMailboxTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"get a bounded deque-based message queue if configured and required" in {
|
||||||
|
checkMailboxQueue(Props[StashQueueReportingActor], "bounded-deque-requirements-configured", BoundedDeqMailboxTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a unbounded deque-based message queue if configured and required" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(Props[StashQueueReportingActor], "bounded-deque-require-unbounded-configured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a bounded deque-based message queue if not configured" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(Props[StashQueueReportingActor], "bounded-deque-require-unbounded-unconfigured"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"get a bounded deque-based message queue if configured and required with Props" in {
|
||||||
|
checkMailboxQueue(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher")
|
||||||
|
.withMailbox("akka.actor.mailbox.bounded-deque-based"),
|
||||||
|
"bounded-deque-requirements-configured-props",
|
||||||
|
BoundedDeqMailboxTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a unbounded deque-based message queue if configured and required with Props" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher")
|
||||||
|
.withMailbox("akka.actor.mailbox.unbounded-deque-based"),
|
||||||
|
"bounded-deque-require-unbounded-configured-props"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a bounded deque-based message queue if not configured with Props" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher"),
|
||||||
|
"bounded-deque-require-unbounded-unconfigured-props"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"get a bounded deque-based message queue if configured and required with Props (dispatcher)" in {
|
||||||
|
checkMailboxQueue(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher"),
|
||||||
|
"bounded-deque-requirements-configured-props-disp",
|
||||||
|
BoundedDeqMailboxTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a unbounded deque-based message queue if configured and required with Props (dispatcher)" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher"),
|
||||||
|
"bounded-deque-require-unbounded-configured-props-disp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a bounded deque-based message queue if not configured with Props (dispatcher)" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withDispatcher("requiring-bounded-dispatcher"),
|
||||||
|
"bounded-deque-require-unbounded-unconfigured-props-disp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"get a bounded deque-based message queue if configured and required with Props (mailbox)" in {
|
||||||
|
checkMailboxQueue(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withMailbox("akka.actor.mailbox.bounded-deque-based"),
|
||||||
|
"bounded-deque-requirements-configured-props-mail",
|
||||||
|
BoundedDeqMailboxTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a unbounded deque-based message queue if configured and required with Props (mailbox)" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor]
|
||||||
|
.withMailbox("akka.actor.mailbox.unbounded-deque-based"),
|
||||||
|
"bounded-deque-require-unbounded-configured-props-mail"))
|
||||||
|
}
|
||||||
|
|
||||||
|
"fail with a bounded deque-based message queue if not configured with Props (mailbox)" in {
|
||||||
|
intercept[IllegalArgumentException](system.actorOf(
|
||||||
|
Props[StashQueueReportingActor],
|
||||||
|
"bounded-deque-require-unbounded-unconfigured-props-mail"))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,12 +84,10 @@ object ActorSystemSpec {
|
||||||
|
|
||||||
class SlowDispatcher(_config: Config, _prerequisites: DispatcherPrerequisites) extends MessageDispatcherConfigurator(_config, _prerequisites) {
|
class SlowDispatcher(_config: Config, _prerequisites: DispatcherPrerequisites) extends MessageDispatcherConfigurator(_config, _prerequisites) {
|
||||||
private val instance = new Dispatcher(
|
private val instance = new Dispatcher(
|
||||||
prerequisites,
|
this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType,
|
|
||||||
mailBoxTypeConfigured,
|
|
||||||
configureExecutor(),
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
|
||||||
val doneIt = new Switch
|
val doneIt = new Switch
|
||||||
|
|
|
||||||
|
|
@ -269,7 +269,7 @@ class IOActorSpec extends AkkaSpec with DefaultTimeout {
|
||||||
case Failure(e) if check(n, e) ⇒
|
case Failure(e) if check(n, e) ⇒
|
||||||
if (delay.isDefined) {
|
if (delay.isDefined) {
|
||||||
executor match {
|
executor match {
|
||||||
case m: MessageDispatcher ⇒ m.prerequisites.scheduler.scheduleOnce(delay.get)(run(n + 1))
|
case m: MessageDispatcher ⇒ m.configurator.prerequisites.scheduler.scheduleOnce(delay.get)(run(n + 1))
|
||||||
case _ ⇒ // Thread.sleep, ignore, or other?
|
case _ ⇒ // Thread.sleep, ignore, or other?
|
||||||
}
|
}
|
||||||
} else run(n + 1)
|
} else run(n + 1)
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,10 @@ object SupervisorHierarchySpec {
|
||||||
extends DispatcherConfigurator(config, prerequisites) {
|
extends DispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance: MessageDispatcher =
|
private val instance: MessageDispatcher =
|
||||||
new Dispatcher(prerequisites,
|
new Dispatcher(this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType,
|
|
||||||
mailBoxTypeConfigured,
|
|
||||||
configureExecutor(),
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -529,12 +529,10 @@ object DispatcherModelSpec {
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends MessageDispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance: MessageDispatcher =
|
private val instance: MessageDispatcher =
|
||||||
new Dispatcher(prerequisites,
|
new Dispatcher(this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType,
|
|
||||||
mailBoxTypeConfigured,
|
|
||||||
configureExecutor(),
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) with MessageDispatcherInterceptor
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS)) with MessageDispatcherInterceptor
|
||||||
|
|
||||||
|
|
@ -600,20 +598,17 @@ object BalancingDispatcherModelSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
class BalancingMessageDispatcherInterceptorConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
|
class BalancingMessageDispatcherInterceptorConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends BalancingDispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance: MessageDispatcher =
|
override protected def create(mailboxType: MailboxType): BalancingDispatcher =
|
||||||
new BalancingDispatcher(prerequisites,
|
new BalancingDispatcher(this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType,
|
mailboxType,
|
||||||
mailBoxTypeConfigured,
|
|
||||||
configureExecutor(),
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS),
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS),
|
||||||
config.getBoolean("attempt-teamwork")) with MessageDispatcherInterceptor
|
config.getBoolean("attempt-teamwork")) with MessageDispatcherInterceptor
|
||||||
|
|
||||||
override def dispatcher(): MessageDispatcher = instance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,6 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
|
||||||
{
|
{
|
||||||
c.getString("type") must equal("Dispatcher")
|
c.getString("type") must equal("Dispatcher")
|
||||||
c.getString("executor") must equal("fork-join-executor")
|
c.getString("executor") must equal("fork-join-executor")
|
||||||
c.getInt("mailbox-capacity") must equal(-1)
|
|
||||||
c.getMilliseconds("mailbox-push-timeout-time") must equal(10 * 1000)
|
|
||||||
c.getString("mailbox-type") must be("")
|
|
||||||
c.getMilliseconds("shutdown-timeout") must equal(1 * 1000)
|
c.getMilliseconds("shutdown-timeout") must equal(1 * 1000)
|
||||||
c.getInt("throughput") must equal(5)
|
c.getInt("throughput") must equal(5)
|
||||||
c.getMilliseconds("throughput-deadline-time") must equal(0)
|
c.getMilliseconds("throughput-deadline-time") must equal(0)
|
||||||
|
|
@ -134,6 +131,18 @@ class ConfigSpec extends AkkaSpec(ConfigFactory.defaultReference(ActorSystem.fin
|
||||||
io.getInt("default-backlog") must be(ioExtSettings.defaultBacklog)
|
io.getInt("default-backlog") must be(ioExtSettings.defaultBacklog)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
val c = config.getConfig("akka.actor.default-mailbox")
|
||||||
|
|
||||||
|
// general mailbox config
|
||||||
|
|
||||||
|
{
|
||||||
|
c.getInt("mailbox-capacity") must equal(1000)
|
||||||
|
c.getMilliseconds("mailbox-push-timeout-time") must equal(10 * 1000)
|
||||||
|
c.getString("mailbox-type") must be("akka.dispatch.UnboundedMailbox")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ object CustomMailboxSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyMailbox(owner: ActorRef) extends QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
class MyMailbox(owner: ActorRef) extends UnboundedQueueBasedMessageQueue {
|
||||||
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ object CallingThreadDispatcherModelSpec {
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends MessageDispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance: MessageDispatcher =
|
private val instance: MessageDispatcher =
|
||||||
new CallingThreadDispatcher(prerequisites, UnboundedMailbox(), false) with MessageDispatcherInterceptor {
|
new CallingThreadDispatcher(this) with MessageDispatcherInterceptor {
|
||||||
override def id: String = config.getString("id")
|
override def id: String = config.getString("id")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -281,30 +281,34 @@ akka {
|
||||||
# Throughput deadline for Dispatcher, set to 0 or negative for no deadline
|
# Throughput deadline for Dispatcher, set to 0 or negative for no deadline
|
||||||
throughput-deadline-time = 0ms
|
throughput-deadline-time = 0ms
|
||||||
|
|
||||||
# If negative (or zero) then an unbounded mailbox is used (default)
|
|
||||||
# If positive then a bounded mailbox is used and the capacity is set using
|
|
||||||
# the property
|
|
||||||
# NOTE: setting a mailbox to 'blocking' can be a bit dangerous, could lead
|
|
||||||
# to deadlock, use with care
|
|
||||||
# The following mailbox-push-timeout-time is only used for type=Dispatcher
|
|
||||||
# and only if mailbox-capacity > 0
|
|
||||||
mailbox-capacity = -1
|
|
||||||
|
|
||||||
# Specifies the timeout to add a new message to a mailbox that is full -
|
|
||||||
# negative number means infinite timeout. It is only used for type=Dispatcher
|
|
||||||
# and only if mailbox-capacity > 0
|
|
||||||
mailbox-push-timeout-time = 10s
|
|
||||||
|
|
||||||
# FQCN of the MailboxType, if not specified the default bounded or unbounded
|
|
||||||
# mailbox is used. The Class of the FQCN must have a public constructor with
|
|
||||||
# (akka.actor.ActorSystem.Settings, com.typesafe.config.Config) parameters.
|
|
||||||
mailbox-type = ""
|
|
||||||
|
|
||||||
# For BalancingDispatcher: If the balancing dispatcher should attempt to
|
# For BalancingDispatcher: If the balancing dispatcher should attempt to
|
||||||
# schedule idle actors using the same dispatcher when a message comes in,
|
# schedule idle actors using the same dispatcher when a message comes in,
|
||||||
# and the dispatchers ExecutorService is not fully busy already.
|
# and the dispatchers ExecutorService is not fully busy already.
|
||||||
attempt-teamwork = on
|
attempt-teamwork = on
|
||||||
|
|
||||||
|
# If this dispatcher requires a specific type of mailbox, specify the
|
||||||
|
# fully-qualified class name here; the actually created mailbox will
|
||||||
|
# be a subtype of this type. The empty string signifies no requirement.
|
||||||
|
mailbox-requirement = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
default-mailbox {
|
||||||
|
# FQCN of the MailboxType. The Class of the FQCN must have a public constructor with
|
||||||
|
# (akka.actor.ActorSystem.Settings, com.typesafe.config.Config) parameters.
|
||||||
|
mailbox-type = "akka.dispatch.UnboundedMailbox"
|
||||||
|
|
||||||
|
# If the mailbox is bounded then it uses this setting to determine its
|
||||||
|
# capacity. The provided value must be positive.
|
||||||
|
# NOTICE:
|
||||||
|
# Up to version 2.1 the mailbox type was determined based on this setting;
|
||||||
|
# this is no longer the case, the type must explicitly name a bounded mailbox.
|
||||||
|
mailbox-capacity = 1000
|
||||||
|
|
||||||
|
# If the mailbox is bounded then this is the timeout for enqueueing
|
||||||
|
# in case the mailbox is full. Negative values signify infinite
|
||||||
|
# timeout, which should be avoided as it bears the risk of dead-lock.
|
||||||
|
mailbox-push-timeout-time = 10s
|
||||||
|
|
||||||
# For Actor with Stash: The default capacity of the stash.
|
# For Actor with Stash: The default capacity of the stash.
|
||||||
# If negative (or zero) then an unbounded stash is used (default)
|
# If negative (or zero) then an unbounded stash is used (default)
|
||||||
# If positive then a bounded stash is used and the capacity is set using
|
# If positive then a bounded stash is used and the capacity is set using
|
||||||
|
|
@ -322,10 +326,16 @@ akka {
|
||||||
requirements {
|
requirements {
|
||||||
"akka.dispatch.UnboundedMessageQueueSemantics" =
|
"akka.dispatch.UnboundedMessageQueueSemantics" =
|
||||||
akka.actor.mailbox.unbounded-queue-based
|
akka.actor.mailbox.unbounded-queue-based
|
||||||
"akka.dispatch.DequeBasedMessageQueue" =
|
"akka.dispatch.BoundedMessageQueueSemantics" =
|
||||||
|
akka.actor.mailbox.bounded-queue-based
|
||||||
|
"akka.dispatch.DequeBasedMessageQueueSemantics" =
|
||||||
akka.actor.mailbox.unbounded-deque-based
|
akka.actor.mailbox.unbounded-deque-based
|
||||||
"akka.dispatch.UnboundedDequeBasedMessageQueueSemantics" =
|
"akka.dispatch.UnboundedDequeBasedMessageQueueSemantics" =
|
||||||
akka.actor.mailbox.unbounded-deque-based
|
akka.actor.mailbox.unbounded-deque-based
|
||||||
|
"akka.dispatch.BoundedDequeBasedMessageQueueSemantics" =
|
||||||
|
akka.actor.mailbox.bounded-deque-based
|
||||||
|
"akka.dispatch.MultipleConsumerSemantics" =
|
||||||
|
akka.actor.mailbox.unbounded-queue-based
|
||||||
}
|
}
|
||||||
|
|
||||||
unbounded-queue-based {
|
unbounded-queue-based {
|
||||||
|
|
@ -335,12 +345,26 @@ akka {
|
||||||
mailbox-type = "akka.dispatch.UnboundedMailbox"
|
mailbox-type = "akka.dispatch.UnboundedMailbox"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bounded-queue-based {
|
||||||
|
# FQCN of the MailboxType, The Class of the FQCN must have a public
|
||||||
|
# constructor with (akka.actor.ActorSystem.Settings,
|
||||||
|
# com.typesafe.config.Config) parameters.
|
||||||
|
mailbox-type = "akka.dispatch.BoundedMailbox"
|
||||||
|
}
|
||||||
|
|
||||||
unbounded-deque-based {
|
unbounded-deque-based {
|
||||||
# FQCN of the MailboxType, The Class of the FQCN must have a public
|
# FQCN of the MailboxType, The Class of the FQCN must have a public
|
||||||
# constructor with (akka.actor.ActorSystem.Settings,
|
# constructor with (akka.actor.ActorSystem.Settings,
|
||||||
# com.typesafe.config.Config) parameters.
|
# com.typesafe.config.Config) parameters.
|
||||||
mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|
mailbox-type = "akka.dispatch.UnboundedDequeBasedMailbox"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bounded-deque-based {
|
||||||
|
# FQCN of the MailboxType, The Class of the FQCN must have a public
|
||||||
|
# constructor with (akka.actor.ActorSystem.Settings,
|
||||||
|
# com.typesafe.config.Config) parameters.
|
||||||
|
mailbox-type = "akka.dispatch.BoundedDequeBasedMailbox"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug {
|
debug {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import scala.concurrent.ExecutionContext
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import scala.concurrent.forkjoin.ThreadLocalRandom
|
import scala.concurrent.forkjoin.ThreadLocalRandom
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
import akka.dispatch.MessageDispatcher
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The actor context - the view of the actor cell from the actor.
|
* The actor context - the view of the actor cell from the actor.
|
||||||
|
|
@ -338,6 +339,7 @@ private[akka] class ActorCell(
|
||||||
val system: ActorSystemImpl,
|
val system: ActorSystemImpl,
|
||||||
val self: InternalActorRef,
|
val self: InternalActorRef,
|
||||||
val props: Props,
|
val props: Props,
|
||||||
|
val dispatcher: MessageDispatcher,
|
||||||
val parent: InternalActorRef)
|
val parent: InternalActorRef)
|
||||||
extends UntypedActorContext with Cell
|
extends UntypedActorContext with Cell
|
||||||
with dungeon.ReceiveTimeout
|
with dungeon.ReceiveTimeout
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,8 @@ private[akka] case object Nobody extends MinimalActorRef {
|
||||||
private[akka] class LocalActorRef private[akka] (
|
private[akka] class LocalActorRef private[akka] (
|
||||||
_system: ActorSystemImpl,
|
_system: ActorSystemImpl,
|
||||||
_props: Props,
|
_props: Props,
|
||||||
|
_dispatcher: MessageDispatcher,
|
||||||
|
_mailboxType: MailboxType,
|
||||||
_supervisor: InternalActorRef,
|
_supervisor: InternalActorRef,
|
||||||
override val path: ActorPath)
|
override val path: ActorPath)
|
||||||
extends ActorRefWithCell with LocalRef {
|
extends ActorRefWithCell with LocalRef {
|
||||||
|
|
@ -285,11 +287,11 @@ private[akka] class LocalActorRef private[akka] (
|
||||||
* actorCell before we call init and start, since we can start using "this"
|
* actorCell before we call init and start, since we can start using "this"
|
||||||
* object from another thread as soon as we run init.
|
* object from another thread as soon as we run init.
|
||||||
*/
|
*/
|
||||||
private val actorCell: ActorCell = newActorCell(_system, this, _props, _supervisor)
|
private val actorCell: ActorCell = newActorCell(_system, this, _props, _dispatcher, _supervisor)
|
||||||
actorCell.init(sendSupervise = true)
|
actorCell.init(sendSupervise = true, _mailboxType)
|
||||||
|
|
||||||
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
|
protected def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, dispatcher: MessageDispatcher, supervisor: InternalActorRef): ActorCell =
|
||||||
new ActorCell(system, ref, props, supervisor)
|
new ActorCell(system, ref, props, dispatcher, supervisor)
|
||||||
|
|
||||||
protected def actorContext: ActorContext = actorCell
|
protected def actorContext: ActorContext = actorCell
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,12 @@ import akka.util.{ Switch, Helpers }
|
||||||
import akka.japi.Util.immutableSeq
|
import akka.japi.Util.immutableSeq
|
||||||
import akka.util.Collections.EmptyImmutableSeq
|
import akka.util.Collections.EmptyImmutableSeq
|
||||||
import scala.util.{ Success, Failure }
|
import scala.util.{ Success, Failure }
|
||||||
|
import scala.util.control.NonFatal
|
||||||
import java.util.concurrent.atomic.AtomicLong
|
import java.util.concurrent.atomic.AtomicLong
|
||||||
import scala.concurrent.{ ExecutionContext, Future, Promise }
|
import scala.concurrent.{ ExecutionContext, Future, Promise }
|
||||||
import scala.annotation.implicitNotFound
|
import scala.annotation.implicitNotFound
|
||||||
import akka.ConfigurationException
|
import akka.ConfigurationException
|
||||||
|
import akka.dispatch.Mailboxes
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for all ActorRef providers to implement.
|
* Interface for all ActorRef providers to implement.
|
||||||
|
|
@ -559,10 +561,16 @@ private[akka] class LocalActorRefProvider private[akka] (
|
||||||
*/
|
*/
|
||||||
protected def systemGuardianStrategy: SupervisorStrategy = SupervisorStrategy.defaultStrategy
|
protected def systemGuardianStrategy: SupervisorStrategy = SupervisorStrategy.defaultStrategy
|
||||||
|
|
||||||
|
private lazy val defaultDispatcher = system.dispatchers.defaultGlobalDispatcher
|
||||||
|
|
||||||
|
private lazy val defaultMailbox = system.mailboxes.lookup(Mailboxes.DefaultMailboxId)
|
||||||
|
|
||||||
override lazy val rootGuardian: LocalActorRef =
|
override lazy val rootGuardian: LocalActorRef =
|
||||||
new LocalActorRef(
|
new LocalActorRef(
|
||||||
system,
|
system,
|
||||||
Props(classOf[LocalActorRefProvider.Guardian], rootGuardianStrategy),
|
Props(classOf[LocalActorRefProvider.Guardian], rootGuardianStrategy),
|
||||||
|
defaultDispatcher,
|
||||||
|
defaultMailbox,
|
||||||
theOneWhoWalksTheBubblesOfSpaceTime,
|
theOneWhoWalksTheBubblesOfSpaceTime,
|
||||||
rootPath) {
|
rootPath) {
|
||||||
override def getParent: InternalActorRef = this
|
override def getParent: InternalActorRef = this
|
||||||
|
|
@ -581,7 +589,7 @@ private[akka] class LocalActorRefProvider private[akka] (
|
||||||
val cell = rootGuardian.underlying
|
val cell = rootGuardian.underlying
|
||||||
cell.reserveChild("user")
|
cell.reserveChild("user")
|
||||||
val ref = new LocalActorRef(system, Props(classOf[LocalActorRefProvider.Guardian], guardianStrategy),
|
val ref = new LocalActorRef(system, Props(classOf[LocalActorRefProvider.Guardian], guardianStrategy),
|
||||||
rootGuardian, rootPath / "user")
|
defaultDispatcher, defaultMailbox, rootGuardian, rootPath / "user")
|
||||||
cell.initChild(ref)
|
cell.initChild(ref)
|
||||||
ref.start()
|
ref.start()
|
||||||
ref
|
ref
|
||||||
|
|
@ -592,7 +600,7 @@ private[akka] class LocalActorRefProvider private[akka] (
|
||||||
cell.reserveChild("system")
|
cell.reserveChild("system")
|
||||||
val ref = new LocalActorRef(
|
val ref = new LocalActorRef(
|
||||||
system, Props(classOf[LocalActorRefProvider.SystemGuardian], systemGuardianStrategy, guardian),
|
system, Props(classOf[LocalActorRefProvider.SystemGuardian], systemGuardianStrategy, guardian),
|
||||||
rootGuardian, rootPath / "system")
|
defaultDispatcher, defaultMailbox, rootGuardian, rootPath / "system")
|
||||||
cell.initChild(ref)
|
cell.initChild(ref)
|
||||||
ref.start()
|
ref.start()
|
||||||
ref
|
ref
|
||||||
|
|
@ -712,19 +720,41 @@ private[akka] class LocalActorRefProvider private[akka] (
|
||||||
if (!system.dispatchers.hasDispatcher(props2.dispatcher))
|
if (!system.dispatchers.hasDispatcher(props2.dispatcher))
|
||||||
throw new ConfigurationException(s"Dispatcher [${props2.dispatcher}] not configured for path $path")
|
throw new ConfigurationException(s"Dispatcher [${props2.dispatcher}] not configured for path $path")
|
||||||
|
|
||||||
if (async) new RepointableActorRef(system, props2, supervisor, path).initialize(async)
|
try {
|
||||||
else new LocalActorRef(system, props2, supervisor, path)
|
val dispatcher = system.dispatchers.lookup(props2.dispatcher)
|
||||||
|
val mailboxType = system.mailboxes.getMailboxType(props2, dispatcher.configurator.config)
|
||||||
|
|
||||||
|
if (async) new RepointableActorRef(system, props2, dispatcher, mailboxType, supervisor, path).initialize(async)
|
||||||
|
else new LocalActorRef(system, props2, dispatcher, mailboxType, supervisor, path)
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) ⇒ throw new IllegalArgumentException(
|
||||||
|
s"configuration problem while creating [$path] with dispatcher [${props2.dispatcher}] and mailbox [${props2.mailbox}]", e)
|
||||||
|
}
|
||||||
|
|
||||||
case router ⇒
|
case router ⇒
|
||||||
val lookup = if (lookupDeploy) deployer.lookup(path) else None
|
val lookup = if (lookupDeploy) deployer.lookup(path) else None
|
||||||
val fromProps = Iterator(props.deploy.copy(routerConfig = props.deploy.routerConfig withFallback router))
|
val fromProps = Iterator(props.deploy.copy(routerConfig = props.deploy.routerConfig withFallback router))
|
||||||
val d = fromProps ++ deploy.iterator ++ lookup.iterator reduce ((a, b) ⇒ b withFallback a)
|
val d = fromProps ++ deploy.iterator ++ lookup.iterator reduce ((a, b) ⇒ b withFallback a)
|
||||||
val p = props.withRouter(d.routerConfig)
|
val p = props.withRouter(d.routerConfig)
|
||||||
|
|
||||||
if (!system.dispatchers.hasDispatcher(p.dispatcher))
|
if (!system.dispatchers.hasDispatcher(p.dispatcher))
|
||||||
throw new ConfigurationException(s"Dispatcher [${p.dispatcher}] not configured for routees of $path")
|
throw new ConfigurationException(s"Dispatcher [${p.dispatcher}] not configured for routees of $path")
|
||||||
if (!system.dispatchers.hasDispatcher(d.routerConfig.routerDispatcher))
|
if (!system.dispatchers.hasDispatcher(d.routerConfig.routerDispatcher))
|
||||||
throw new ConfigurationException(s"Dispatcher [${p.dispatcher}] not configured for router of $path")
|
throw new ConfigurationException(s"Dispatcher [${p.dispatcher}] not configured for router of $path")
|
||||||
new RoutedActorRef(system, p, supervisor, path).initialize(async)
|
|
||||||
|
val routerProps =
|
||||||
|
Props(p.deploy.copy(dispatcher = p.routerConfig.routerDispatcher),
|
||||||
|
classOf[RoutedActorCell.RouterCreator], Vector(p.routerConfig))
|
||||||
|
val routerDispatcher = system.dispatchers.lookup(p.routerConfig.routerDispatcher)
|
||||||
|
val routerMailbox = system.mailboxes.getMailboxType(routerProps, routerDispatcher.configurator.config)
|
||||||
|
|
||||||
|
val routeeProps = p.withRouter(NoRouter)
|
||||||
|
// the RouteeProvider uses context.actorOf() to create the routees, which does not allow us to pass
|
||||||
|
// these through, but obtain them here for early verification
|
||||||
|
val routeeDispatcher = system.dispatchers.lookup(p.dispatcher)
|
||||||
|
val routeeMailbox = system.mailboxes.getMailboxType(routeeProps, routeeDispatcher.configurator.config)
|
||||||
|
|
||||||
|
new RoutedActorRef(system, routerProps, routerDispatcher, routerMailbox, routeeProps, supervisor, path).initialize(async)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -559,13 +559,13 @@ private[akka] class ActorSystemImpl(val name: String, applicationConfig: Config,
|
||||||
def hasSystemMessages = false
|
def hasSystemMessages = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mailboxes: Mailboxes = new Mailboxes(settings, eventStream, dynamicAccess)
|
||||||
|
|
||||||
val dispatchers: Dispatchers = new Dispatchers(settings, DefaultDispatcherPrerequisites(
|
val dispatchers: Dispatchers = new Dispatchers(settings, DefaultDispatcherPrerequisites(
|
||||||
threadFactory, eventStream, deadLetterMailbox, scheduler, dynamicAccess, settings))
|
threadFactory, eventStream, deadLetterMailbox, scheduler, dynamicAccess, settings, mailboxes))
|
||||||
|
|
||||||
val dispatcher: ExecutionContext = dispatchers.defaultGlobalDispatcher
|
val dispatcher: ExecutionContext = dispatchers.defaultGlobalDispatcher
|
||||||
|
|
||||||
val mailboxes: Mailboxes = new Mailboxes(settings, eventStream, dynamicAccess)
|
|
||||||
|
|
||||||
val internalCallingThreadExecutionContext: ExecutionContext =
|
val internalCallingThreadExecutionContext: ExecutionContext =
|
||||||
dynamicAccess.getObjectFor[ExecutionContext]("scala.concurrent.Future$InternalCallbackExecutor$").getOrElse(
|
dynamicAccess.getObjectFor[ExecutionContext]("scala.concurrent.Future$InternalCallbackExecutor$").getOrElse(
|
||||||
new ExecutionContext with BatchingExecutor {
|
new ExecutionContext with BatchingExecutor {
|
||||||
|
|
|
||||||
|
|
@ -91,11 +91,11 @@ object Props {
|
||||||
@deprecated("use Props.withDispatcher and friends", "2.2")
|
@deprecated("use Props.withDispatcher and friends", "2.2")
|
||||||
def apply(
|
def apply(
|
||||||
creator: () ⇒ Actor = Props.defaultCreator,
|
creator: () ⇒ Actor = Props.defaultCreator,
|
||||||
dispatcher: String = Dispatchers.DefaultDispatcherId,
|
dispatcher: String = Deploy.NoDispatcherGiven,
|
||||||
routerConfig: RouterConfig = Props.defaultRoutedProps,
|
routerConfig: RouterConfig = Props.defaultRoutedProps,
|
||||||
deploy: Deploy = Props.defaultDeploy): Props = {
|
deploy: Deploy = Props.defaultDeploy): Props = {
|
||||||
|
|
||||||
val d1 = if (dispatcher != Dispatchers.DefaultDispatcherId) deploy.copy(dispatcher = dispatcher) else deploy
|
val d1 = if (dispatcher != Deploy.NoDispatcherGiven) deploy.copy(dispatcher = dispatcher) else deploy
|
||||||
val d2 = if (routerConfig != Props.defaultRoutedProps) d1.copy(routerConfig = routerConfig) else d1
|
val d2 = if (routerConfig != Props.defaultRoutedProps) d1.copy(routerConfig = routerConfig) else d1
|
||||||
val p = Props(classOf[CreatorFunctionConsumer], creator)
|
val p = Props(classOf[CreatorFunctionConsumer], creator)
|
||||||
if (d2 != Props.defaultDeploy) p.withDeploy(d2) else p
|
if (d2 != Props.defaultDeploy) p.withDeploy(d2) else p
|
||||||
|
|
@ -125,22 +125,17 @@ object Props {
|
||||||
def create[T <: Actor](creator: Creator[T]): Props = {
|
def create[T <: Actor](creator: Creator[T]): Props = {
|
||||||
if ((creator.getClass.getModifiers & Modifier.STATIC) == 0)
|
if ((creator.getClass.getModifiers & Modifier.STATIC) == 0)
|
||||||
throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static or top-level")
|
throw new IllegalArgumentException("cannot use non-static local Creator to create actors; make it static or top-level")
|
||||||
val cc = classOf[Creator[_]]
|
|
||||||
val ac = classOf[Actor]
|
val ac = classOf[Actor]
|
||||||
@tailrec def findType(c: Class[_]): Class[_] = {
|
val actorClass = Reflect.findMarker(creator.getClass, classOf[Creator[_]]) match {
|
||||||
c.getGenericInterfaces collectFirst {
|
case t: ParameterizedType ⇒
|
||||||
case t: ParameterizedType if cc.isAssignableFrom(t.getRawType.asInstanceOf[Class[_]]) ⇒
|
|
||||||
t.getActualTypeArguments.head match {
|
t.getActualTypeArguments.head match {
|
||||||
case c: Class[_] ⇒ c // since T <: Actor
|
case c: Class[_] ⇒ c // since T <: Actor
|
||||||
case v: TypeVariable[_] ⇒
|
case v: TypeVariable[_] ⇒
|
||||||
v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac ⇒ c } getOrElse ac
|
v.getBounds collectFirst { case c: Class[_] if ac.isAssignableFrom(c) && c != ac ⇒ c } getOrElse ac
|
||||||
}
|
case x ⇒ throw new IllegalArgumentException(s"unsupported type found in Creator argument [$x]")
|
||||||
} match {
|
|
||||||
case Some(x) ⇒ x
|
|
||||||
case None ⇒ findType(c.getSuperclass)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
apply(defaultDeploy, classOf[CreatorConsumer], findType(creator.getClass) :: creator :: Nil)
|
apply(defaultDeploy, classOf[CreatorConsumer], actorClass :: creator :: Nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,9 +240,9 @@ final case class Props(deploy: Deploy, clazz: Class[_], args: immutable.Seq[Any]
|
||||||
* Convenience method for extracting the mailbox information from the
|
* Convenience method for extracting the mailbox information from the
|
||||||
* contained [[Deploy]] instance.
|
* contained [[Deploy]] instance.
|
||||||
*/
|
*/
|
||||||
def mailbox: Option[String] = deploy.mailbox match {
|
def mailbox: String = deploy.mailbox match {
|
||||||
case NoMailboxGiven ⇒ None
|
case NoMailboxGiven ⇒ Mailboxes.DefaultMailboxId
|
||||||
case x ⇒ Some(x)
|
case x ⇒ x
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ import util.Try
|
||||||
private[akka] class RepointableActorRef(
|
private[akka] class RepointableActorRef(
|
||||||
val system: ActorSystemImpl,
|
val system: ActorSystemImpl,
|
||||||
val props: Props,
|
val props: Props,
|
||||||
|
val dispatcher: MessageDispatcher,
|
||||||
|
val mailboxType: MailboxType,
|
||||||
val supervisor: InternalActorRef,
|
val supervisor: InternalActorRef,
|
||||||
val path: ActorPath)
|
val path: ActorPath)
|
||||||
extends ActorRefWithCell with RepointableRef {
|
extends ActorRefWithCell with RepointableRef {
|
||||||
|
|
@ -111,7 +113,7 @@ private[akka] class RepointableActorRef(
|
||||||
* unstarted cell. The cell must be fully functional.
|
* unstarted cell. The cell must be fully functional.
|
||||||
*/
|
*/
|
||||||
def newCell(old: UnstartedCell): Cell =
|
def newCell(old: UnstartedCell): Cell =
|
||||||
new ActorCell(system, this, props, supervisor).init(sendSupervise = false)
|
new ActorCell(system, this, props, dispatcher, supervisor).init(sendSupervise = false, mailboxType)
|
||||||
|
|
||||||
def start(): Unit = ()
|
def start(): Unit = ()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package akka.actor
|
package akka.actor
|
||||||
|
|
||||||
import akka.dispatch.{ UnboundedDequeBasedMessageQueueSemantics, RequiresMessageQueue, Envelope, DequeBasedMessageQueue }
|
import akka.dispatch.{ UnboundedDequeBasedMessageQueueSemantics, RequiresMessageQueue, Envelope, DequeBasedMessageQueueSemantics }
|
||||||
import akka.AkkaException
|
import akka.AkkaException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -47,7 +47,7 @@ import akka.AkkaException
|
||||||
* any trait/class that overrides the `preRestart` callback. This means it's not possible to write
|
* any trait/class that overrides the `preRestart` callback. This means it's not possible to write
|
||||||
* `Actor with MyActor with Stash` if `MyActor` overrides `preRestart`.
|
* `Actor with MyActor with Stash` if `MyActor` overrides `preRestart`.
|
||||||
*/
|
*/
|
||||||
trait Stash extends UnrestrictedStash with RequiresMessageQueue[DequeBasedMessageQueue]
|
trait Stash extends UnrestrictedStash with RequiresMessageQueue[DequeBasedMessageQueueSemantics]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `UnboundedStash` trait is a version of `Stash` that enforces an unbounded stash for you actor.
|
* The `UnboundedStash` trait is a version of `Stash` that enforces an unbounded stash for you actor.
|
||||||
|
|
@ -64,16 +64,16 @@ trait UnrestrictedStash extends Actor {
|
||||||
*/
|
*/
|
||||||
private val capacity: Int = {
|
private val capacity: Int = {
|
||||||
val dispatcher = context.system.settings.config.getConfig(context.props.dispatcher)
|
val dispatcher = context.system.settings.config.getConfig(context.props.dispatcher)
|
||||||
val config = dispatcher.withFallback(context.system.settings.config.getConfig("akka.actor.default-dispatcher"))
|
val config = dispatcher.withFallback(context.system.settings.config.getConfig("akka.actor.default-mailbox"))
|
||||||
config.getInt("stash-capacity")
|
config.getInt("stash-capacity")
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The actor's deque-based message queue.
|
/* The actor's deque-based message queue.
|
||||||
* `mailbox.queue` is the underlying `Deque`.
|
* `mailbox.queue` is the underlying `Deque`.
|
||||||
*/
|
*/
|
||||||
private val mailbox: DequeBasedMessageQueue = {
|
private val mailbox: DequeBasedMessageQueueSemantics = {
|
||||||
context.asInstanceOf[ActorCell].mailbox.messageQueue match {
|
context.asInstanceOf[ActorCell].mailbox.messageQueue match {
|
||||||
case queue: DequeBasedMessageQueue ⇒ queue
|
case queue: DequeBasedMessageQueueSemantics ⇒ queue
|
||||||
case other ⇒ throw ActorInitializationException(self, s"DequeBasedMailbox required, got: ${other.getClass.getName}\n" +
|
case other ⇒ throw ActorInitializationException(self, s"DequeBasedMailbox required, got: ${other.getClass.getName}\n" +
|
||||||
"""An (unbounded) deque-based mailbox can be configured as follows:
|
"""An (unbounded) deque-based mailbox can be configured as follows:
|
||||||
| my-custom-mailbox {
|
| my-custom-mailbox {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import akka.actor._
|
||||||
import akka.serialization.SerializationExtension
|
import akka.serialization.SerializationExtension
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.util.control.Exception.Catcher
|
import scala.util.control.Exception.Catcher
|
||||||
|
import akka.dispatch.MailboxType
|
||||||
|
import akka.dispatch.ProducesMessageQueue
|
||||||
|
|
||||||
private[akka] trait Dispatch { this: ActorCell ⇒
|
private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
|
|
||||||
|
|
@ -30,8 +32,6 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
|
|
||||||
final def numberOfMessages: Int = mailbox.numberOfMessages
|
final def numberOfMessages: Int = mailbox.numberOfMessages
|
||||||
|
|
||||||
val dispatcher: MessageDispatcher = system.dispatchers.lookup(props.dispatcher)
|
|
||||||
|
|
||||||
final def isTerminated: Boolean = mailbox.isClosed
|
final def isTerminated: Boolean = mailbox.isClosed
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,24 +39,29 @@ private[akka] trait Dispatch { this: ActorCell ⇒
|
||||||
* reasonably different from the previous UID of a possible actor with the same path,
|
* reasonably different from the previous UID of a possible actor with the same path,
|
||||||
* which can be achieved by using ThreadLocalRandom.current.nextInt().
|
* which can be achieved by using ThreadLocalRandom.current.nextInt().
|
||||||
*/
|
*/
|
||||||
final def init(sendSupervise: Boolean): this.type = {
|
final def init(sendSupervise: Boolean, mailboxType: MailboxType): this.type = {
|
||||||
/*
|
/*
|
||||||
* Create the mailbox and enqueue the Create() message to ensure that
|
* Create the mailbox and enqueue the Create() message to ensure that
|
||||||
* this is processed before anything else.
|
* this is processed before anything else.
|
||||||
*/
|
*/
|
||||||
val mbox = dispatcher.createMailbox(this)
|
val mbox = dispatcher.createMailbox(this, mailboxType)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The mailboxType was calculated taking into account what the MailboxType
|
||||||
|
* has promised to produce. If that was more than the default, then we need
|
||||||
|
* to reverify here because the dispatcher may well have screwed it up.
|
||||||
|
*/
|
||||||
// we need to delay the failure to the point of actor creation so we can handle
|
// we need to delay the failure to the point of actor creation so we can handle
|
||||||
// it properly in the normal way
|
// it properly in the normal way
|
||||||
val actorClass = props.actorClass
|
val actorClass = props.actorClass
|
||||||
val createMessage = if (system.mailboxes.hasRequiredType(actorClass)) {
|
val createMessage = mailboxType match {
|
||||||
Create(system.mailboxes.getRequiredType(actorClass).flatMap {
|
case _: ProducesMessageQueue[_] if system.mailboxes.hasRequiredType(actorClass) ⇒
|
||||||
case c if !c.isAssignableFrom(mbox.messageQueue.getClass) ⇒
|
val req = system.mailboxes.getRequiredType(actorClass)
|
||||||
Some(ActorInitializationException(self, s"Actor [${self}] requires mailbox type [${c}]" +
|
if (req isInstance mbox.messageQueue) Create(None)
|
||||||
s" got [${mbox.messageQueue.getClass}]"))
|
else Create(Some(ActorInitializationException(self,
|
||||||
case _ ⇒ None
|
s"Actor [$self] requires mailbox type [$req] got [${mbox.messageQueue.getClass}]")))
|
||||||
})
|
case _ ⇒ Create(None)
|
||||||
} else Create(None)
|
}
|
||||||
|
|
||||||
swapMailbox(mbox)
|
swapMailbox(mbox)
|
||||||
mailbox.setActor(this)
|
mailbox.setActor(this)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ import scala.concurrent.duration.Duration
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
|
import scala.util.Try
|
||||||
|
import scala.util.Failure
|
||||||
|
import akka.util.Reflect
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
|
||||||
final case class Envelope private (val message: Any, val sender: ActorRef)
|
final case class Envelope private (val message: Any, val sender: ActorRef)
|
||||||
|
|
||||||
|
|
@ -79,11 +83,11 @@ private[akka] object MessageDispatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) extends AbstractMessageDispatcher with BatchingExecutor with ExecutionContext {
|
abstract class MessageDispatcher(val configurator: MessageDispatcherConfigurator) extends AbstractMessageDispatcher with BatchingExecutor with ExecutionContext {
|
||||||
|
|
||||||
import MessageDispatcher._
|
import MessageDispatcher._
|
||||||
import AbstractMessageDispatcher.{ inhabitantsOffset, shutdownScheduleOffset }
|
import AbstractMessageDispatcher.{ inhabitantsOffset, shutdownScheduleOffset }
|
||||||
import prerequisites._
|
import configurator.prerequisites._
|
||||||
|
|
||||||
@volatile private[this] var _inhabitantsDoNotCallMeDirectly: Long = _ // DO NOT TOUCH!
|
@volatile private[this] var _inhabitantsDoNotCallMeDirectly: Long = _ // DO NOT TOUCH!
|
||||||
@volatile private[this] var _shutdownScheduleDoNotCallMeDirectly: Int = _ // DO NOT TOUCH!
|
@volatile private[this] var _shutdownScheduleDoNotCallMeDirectly: Int = _ // DO NOT TOUCH!
|
||||||
|
|
@ -109,18 +113,7 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
||||||
/**
|
/**
|
||||||
* Creates and returns a mailbox for the given actor.
|
* Creates and returns a mailbox for the given actor.
|
||||||
*/
|
*/
|
||||||
protected[akka] def createMailbox(actor: Cell): Mailbox
|
protected[akka] def createMailbox(actor: Cell, mailboxType: MailboxType): Mailbox
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds out the mailbox type for an actor based on configuration, props and requirements.
|
|
||||||
*/
|
|
||||||
protected[akka] def getMailboxType(actor: Cell, mailboxType: MailboxType, mailboxTypeConfigured: Boolean): MailboxType =
|
|
||||||
actor.props.mailbox.flatMap(id ⇒ actor.system.mailboxes.lookup(id)) match {
|
|
||||||
case Some(x) ⇒ x
|
|
||||||
case None if mailboxTypeConfigured ⇒ mailboxType
|
|
||||||
case None ⇒ actor.system.mailboxes.getRequiredType(actor.props.actorClass).
|
|
||||||
flatMap(c ⇒ actor.system.mailboxes.lookupByQueueType(c)).getOrElse(mailboxType)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Identifier of this dispatcher, corresponds to the full key
|
* Identifier of this dispatcher, corresponds to the full key
|
||||||
|
|
@ -156,8 +149,8 @@ abstract class MessageDispatcher(val prerequisites: DispatcherPrerequisites) ext
|
||||||
}
|
}
|
||||||
|
|
||||||
override def reportFailure(t: Throwable): Unit = t match {
|
override def reportFailure(t: Throwable): Unit = t match {
|
||||||
case e: LogEventException ⇒ prerequisites.eventStream.publish(e.event)
|
case e: LogEventException ⇒ eventStream.publish(e.event)
|
||||||
case _ ⇒ prerequisites.eventStream.publish(Error(t, getClass.getName, getClass, t.getMessage))
|
case _ ⇒ eventStream.publish(Error(t, getClass.getName, getClass, t.getMessage))
|
||||||
}
|
}
|
||||||
|
|
||||||
@tailrec
|
@tailrec
|
||||||
|
|
@ -321,37 +314,6 @@ abstract class MessageDispatcherConfigurator(val config: Config, val prerequisit
|
||||||
*/
|
*/
|
||||||
def dispatcher(): MessageDispatcher
|
def dispatcher(): MessageDispatcher
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a factory for the [[akka.dispatch.Mailbox]] given the configuration.
|
|
||||||
* Default implementation instantiate the [[akka.dispatch.MailboxType]] specified
|
|
||||||
* as FQCN in mailbox-type config property. If mailbox-type is unspecified (empty)
|
|
||||||
* then [[akka.dispatch.UnboundedMailbox]] is used when capacity is < 1,
|
|
||||||
* otherwise [[akka.dispatch.BoundedMailbox]].
|
|
||||||
*/
|
|
||||||
def mailboxType(): MailboxType = {
|
|
||||||
config.getString("mailbox-type") match {
|
|
||||||
case "" ⇒
|
|
||||||
if (config.getInt("mailbox-capacity") < 1) UnboundedMailbox()
|
|
||||||
else new BoundedMailbox(prerequisites.settings, config)
|
|
||||||
case "unbounded" ⇒ UnboundedMailbox()
|
|
||||||
case "bounded" ⇒ new BoundedMailbox(prerequisites.settings, config)
|
|
||||||
case fqcn ⇒
|
|
||||||
val args = List(classOf[ActorSystem.Settings] -> prerequisites.settings, classOf[Config] -> config)
|
|
||||||
prerequisites.dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
|
|
||||||
case exception ⇒
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
("Cannot instantiate MailboxType [%s], defined in [%s], " +
|
|
||||||
"make sure it has constructor with [akka.actor.ActorSystem.Settings, com.typesafe.config.Config] parameters")
|
|
||||||
.format(fqcn, config.getString("id")), exception)
|
|
||||||
}).get
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Was the mailbox type configured or derived?
|
|
||||||
*/
|
|
||||||
def mailBoxTypeConfigured: Boolean = config.getString("mailbox-type") != Deploy.NoMailboxGiven
|
|
||||||
|
|
||||||
def configureExecutor(): ExecutorServiceConfigurator = {
|
def configureExecutor(): ExecutorServiceConfigurator = {
|
||||||
config.getString("executor") match {
|
config.getString("executor") match {
|
||||||
case null | "" | "fork-join-executor" ⇒ new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
|
case null | "" | "fork-join-executor" ⇒ new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,15 @@ import scala.concurrent.duration.FiniteDuration
|
||||||
* @see akka.dispatch.Dispatchers
|
* @see akka.dispatch.Dispatchers
|
||||||
*/
|
*/
|
||||||
class BalancingDispatcher(
|
class BalancingDispatcher(
|
||||||
_prerequisites: DispatcherPrerequisites,
|
_configurator: MessageDispatcherConfigurator,
|
||||||
_id: String,
|
_id: String,
|
||||||
throughput: Int,
|
throughput: Int,
|
||||||
throughputDeadlineTime: Duration,
|
throughputDeadlineTime: Duration,
|
||||||
mailboxType: MailboxType,
|
_mailboxType: MailboxType,
|
||||||
_mailBoxTypeConfigured: Boolean,
|
|
||||||
_executorServiceFactoryProvider: ExecutorServiceFactoryProvider,
|
_executorServiceFactoryProvider: ExecutorServiceFactoryProvider,
|
||||||
_shutdownTimeout: FiniteDuration,
|
_shutdownTimeout: FiniteDuration,
|
||||||
attemptTeamWork: Boolean)
|
attemptTeamWork: Boolean)
|
||||||
extends Dispatcher(_prerequisites, _id, throughput, throughputDeadlineTime, mailboxType, _mailBoxTypeConfigured, _executorServiceFactoryProvider, _shutdownTimeout) {
|
extends Dispatcher(_configurator, _id, throughput, throughputDeadlineTime, _executorServiceFactoryProvider, _shutdownTimeout) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
|
|
@ -51,7 +50,7 @@ class BalancingDispatcher(
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
private[akka] val messageQueue: MessageQueue = mailboxType.create(None, None)
|
private[akka] val messageQueue: MessageQueue = _mailboxType.create(None, None)
|
||||||
|
|
||||||
private class SharingMailbox(val system: ActorSystemImpl, _messageQueue: MessageQueue)
|
private class SharingMailbox(val system: ActorSystemImpl, _messageQueue: MessageQueue)
|
||||||
extends Mailbox(_messageQueue) with DefaultSystemMessageQueue {
|
extends Mailbox(_messageQueue) with DefaultSystemMessageQueue {
|
||||||
|
|
@ -69,7 +68,8 @@ class BalancingDispatcher(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected[akka] override def createMailbox(actor: akka.actor.Cell): Mailbox = new SharingMailbox(actor.systemImpl, messageQueue)
|
protected[akka] override def createMailbox(actor: akka.actor.Cell, mailboxType: MailboxType): Mailbox =
|
||||||
|
new SharingMailbox(actor.systemImpl, messageQueue)
|
||||||
|
|
||||||
protected[akka] override def register(actor: ActorCell): Unit = {
|
protected[akka] override def register(actor: ActorCell): Unit = {
|
||||||
super.register(actor)
|
super.register(actor)
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,15 @@ import java.lang.reflect.ParameterizedType
|
||||||
* Larger values (or zero or negative) increase throughput, smaller values increase fairness
|
* Larger values (or zero or negative) increase throughput, smaller values increase fairness
|
||||||
*/
|
*/
|
||||||
class Dispatcher(
|
class Dispatcher(
|
||||||
_prerequisites: DispatcherPrerequisites,
|
_configurator: MessageDispatcherConfigurator,
|
||||||
val id: String,
|
val id: String,
|
||||||
val throughput: Int,
|
val throughput: Int,
|
||||||
val throughputDeadlineTime: Duration,
|
val throughputDeadlineTime: Duration,
|
||||||
val mailboxType: MailboxType,
|
|
||||||
val mailboxTypeConfigured: Boolean,
|
|
||||||
executorServiceFactoryProvider: ExecutorServiceFactoryProvider,
|
executorServiceFactoryProvider: ExecutorServiceFactoryProvider,
|
||||||
val shutdownTimeout: FiniteDuration)
|
val shutdownTimeout: FiniteDuration)
|
||||||
extends MessageDispatcher(_prerequisites) {
|
extends MessageDispatcher(_configurator) {
|
||||||
|
|
||||||
|
import configurator.prerequisites._
|
||||||
|
|
||||||
private class LazyExecutorServiceDelegate(factory: ExecutorServiceFactory) extends ExecutorServiceDelegate {
|
private class LazyExecutorServiceDelegate(factory: ExecutorServiceFactory) extends ExecutorServiceDelegate {
|
||||||
lazy val executor: ExecutorService = factory.createExecutorService
|
lazy val executor: ExecutorService = factory.createExecutorService
|
||||||
|
|
@ -46,7 +46,7 @@ class Dispatcher(
|
||||||
}
|
}
|
||||||
|
|
||||||
@volatile private var executorServiceDelegate: LazyExecutorServiceDelegate =
|
@volatile private var executorServiceDelegate: LazyExecutorServiceDelegate =
|
||||||
new LazyExecutorServiceDelegate(executorServiceFactoryProvider.createExecutorServiceFactory(id, prerequisites.threadFactory))
|
new LazyExecutorServiceDelegate(executorServiceFactoryProvider.createExecutorServiceFactory(id, threadFactory))
|
||||||
|
|
||||||
protected final def executorService: ExecutorServiceDelegate = executorServiceDelegate
|
protected final def executorService: ExecutorServiceDelegate = executorServiceDelegate
|
||||||
|
|
||||||
|
|
@ -80,7 +80,7 @@ class Dispatcher(
|
||||||
executorService execute invocation
|
executorService execute invocation
|
||||||
} catch {
|
} catch {
|
||||||
case e2: RejectedExecutionException ⇒
|
case e2: RejectedExecutionException ⇒
|
||||||
prerequisites.eventStream.publish(Error(e, getClass.getName, getClass, "executeTask was rejected twice!"))
|
eventStream.publish(Error(e, getClass.getName, getClass, "executeTask was rejected twice!"))
|
||||||
throw e2
|
throw e2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -89,8 +89,8 @@ class Dispatcher(
|
||||||
/**
|
/**
|
||||||
* INTERNAL API
|
* INTERNAL API
|
||||||
*/
|
*/
|
||||||
protected[akka] def createMailbox(actor: akka.actor.Cell): Mailbox = {
|
protected[akka] def createMailbox(actor: akka.actor.Cell, mailboxType: MailboxType): Mailbox = {
|
||||||
new Mailbox(getMailboxType(actor, mailboxType, mailboxTypeConfigured).create(Some(actor.self), Some(actor.system))) with DefaultSystemMessageQueue
|
new Mailbox(mailboxType.create(Some(actor.self), Some(actor.system))) with DefaultSystemMessageQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -125,7 +125,7 @@ class Dispatcher(
|
||||||
} catch { //Retry once
|
} catch { //Retry once
|
||||||
case e: RejectedExecutionException ⇒
|
case e: RejectedExecutionException ⇒
|
||||||
mbox.setAsIdle()
|
mbox.setAsIdle()
|
||||||
prerequisites.eventStream.publish(Error(e, getClass.getName, getClass, "registerForExecution was rejected twice!"))
|
eventStream.publish(Error(e, getClass.getName, getClass, "registerForExecution was rejected twice!"))
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import akka.event.Logging.Warning
|
||||||
import akka.event.EventStream
|
import akka.event.EventStream
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import akka.ConfigurationException
|
import akka.ConfigurationException
|
||||||
|
import akka.actor.Deploy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
|
* DispatcherPrerequisites represents useful contextual pieces when constructing a MessageDispatcher
|
||||||
|
|
@ -22,6 +23,7 @@ trait DispatcherPrerequisites {
|
||||||
def scheduler: Scheduler
|
def scheduler: Scheduler
|
||||||
def dynamicAccess: DynamicAccess
|
def dynamicAccess: DynamicAccess
|
||||||
def settings: ActorSystem.Settings
|
def settings: ActorSystem.Settings
|
||||||
|
def mailboxes: Mailboxes
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,7 +35,8 @@ private[akka] case class DefaultDispatcherPrerequisites(
|
||||||
val deadLetterMailbox: Mailbox,
|
val deadLetterMailbox: Mailbox,
|
||||||
val scheduler: Scheduler,
|
val scheduler: Scheduler,
|
||||||
val dynamicAccess: DynamicAccess,
|
val dynamicAccess: DynamicAccess,
|
||||||
val settings: ActorSystem.Settings) extends DispatcherPrerequisites
|
val settings: ActorSystem.Settings,
|
||||||
|
val mailboxes: Mailboxes) extends DispatcherPrerequisites
|
||||||
|
|
||||||
object Dispatchers {
|
object Dispatchers {
|
||||||
/**
|
/**
|
||||||
|
|
@ -101,7 +104,7 @@ class Dispatchers(val settings: ActorSystem.Settings, val prerequisites: Dispatc
|
||||||
}
|
}
|
||||||
|
|
||||||
//INTERNAL API
|
//INTERNAL API
|
||||||
private def config(id: String): Config = {
|
private[akka] def config(id: String): Config = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
def simpleName = id.substring(id.lastIndexOf('.') + 1)
|
def simpleName = id.substring(id.lastIndexOf('.') + 1)
|
||||||
idConfig(id)
|
idConfig(id)
|
||||||
|
|
@ -172,12 +175,10 @@ class DispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisi
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends MessageDispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance = new Dispatcher(
|
private val instance = new Dispatcher(
|
||||||
prerequisites,
|
this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType,
|
|
||||||
mailBoxTypeConfigured,
|
|
||||||
configureExecutor(),
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS))
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS))
|
||||||
|
|
||||||
|
|
@ -187,20 +188,51 @@ class DispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisi
|
||||||
override def dispatcher(): MessageDispatcher = instance
|
override def dispatcher(): MessageDispatcher = instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object BalancingDispatcherConfigurator {
|
||||||
|
private val defaultRequirement =
|
||||||
|
ConfigFactory.parseString("mailbox-requirement = akka.dispatch.MultipleConsumerSemantics")
|
||||||
|
def amendConfig(config: Config): Config =
|
||||||
|
if (config.getString("mailbox-requirement") != "") config
|
||||||
|
else defaultRequirement.withFallback(config)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configurator for creating [[akka.dispatch.BalancingDispatcher]].
|
* Configurator for creating [[akka.dispatch.BalancingDispatcher]].
|
||||||
* Returns the same dispatcher instance for for each invocation
|
* Returns the same dispatcher instance for for each invocation
|
||||||
* of the `dispatcher()` method.
|
* of the `dispatcher()` method.
|
||||||
*/
|
*/
|
||||||
class BalancingDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
|
class BalancingDispatcherConfigurator(_config: Config, _prerequisites: DispatcherPrerequisites)
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends MessageDispatcherConfigurator(BalancingDispatcherConfigurator.amendConfig(_config), _prerequisites) {
|
||||||
|
|
||||||
private val instance = new BalancingDispatcher(
|
private val instance = {
|
||||||
prerequisites,
|
val mailboxes = prerequisites.mailboxes
|
||||||
|
val id = config.getString("id")
|
||||||
|
val requirement = mailboxes.getMailboxRequirement(config)
|
||||||
|
if (!classOf[MultipleConsumerSemantics].isAssignableFrom(requirement))
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"BalancingDispatcher must have 'mailbox-requirement' which implements akka.dispatch.MultipleConsumerSemantics; " +
|
||||||
|
s"dispatcher [$id] has [$requirement]")
|
||||||
|
val conf = config.withFallback(prerequisites.settings.config.getConfig(Mailboxes.DefaultMailboxId))
|
||||||
|
val mailboxType =
|
||||||
|
if (conf.getString("mailbox-type") != Deploy.NoMailboxGiven) mailboxes.lookupByQueueType(requirement)
|
||||||
|
else {
|
||||||
|
val mt = mailboxes.lookup(conf.getString("mailbox-type"))
|
||||||
|
if (!requirement.isAssignableFrom(mailboxes.getProducedMessageQueueType(mt)))
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"BalancingDispatcher [$id] has 'mailbox-type' [${mt.getClass}] which is incompatible with 'mailbox-requirement' [$requirement]")
|
||||||
|
mt
|
||||||
|
}
|
||||||
|
create(mailboxType)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected def create(mailboxType: MailboxType): BalancingDispatcher =
|
||||||
|
new BalancingDispatcher(
|
||||||
|
this,
|
||||||
config.getString("id"),
|
config.getString("id"),
|
||||||
config.getInt("throughput"),
|
config.getInt("throughput"),
|
||||||
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
Duration(config.getNanoseconds("throughput-deadline-time"), TimeUnit.NANOSECONDS),
|
||||||
mailboxType, mailBoxTypeConfigured, configureExecutor(),
|
mailboxType,
|
||||||
|
configureExecutor(),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS),
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS),
|
||||||
config.getBoolean("attempt-teamwork"))
|
config.getBoolean("attempt-teamwork"))
|
||||||
|
|
||||||
|
|
@ -233,7 +265,7 @@ class PinnedDispatcherConfigurator(config: Config, prerequisites: DispatcherPrer
|
||||||
*/
|
*/
|
||||||
override def dispatcher(): MessageDispatcher =
|
override def dispatcher(): MessageDispatcher =
|
||||||
new PinnedDispatcher(
|
new PinnedDispatcher(
|
||||||
prerequisites, null, config.getString("id"), mailboxType, mailBoxTypeConfigured,
|
this, null, config.getString("id"),
|
||||||
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS), threadPoolConfig)
|
Duration(config.getMilliseconds("shutdown-timeout"), TimeUnit.MILLISECONDS), threadPoolConfig)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ trait MessageQueue {
|
||||||
def cleanUp(owner: ActorRef, deadLetters: MessageQueue): Unit
|
def cleanUp(owner: ActorRef, deadLetters: MessageQueue): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
class NodeMessageQueue extends AbstractNodeQueue[Envelope] with MessageQueue {
|
class NodeMessageQueue extends AbstractNodeQueue[Envelope] with MessageQueue with UnboundedMessageQueueSemantics {
|
||||||
|
|
||||||
final def enqueue(receiver: ActorRef, handle: Envelope): Unit = add(handle)
|
final def enqueue(receiver: ActorRef, handle: Envelope): Unit = add(handle)
|
||||||
|
|
||||||
|
|
@ -418,10 +418,16 @@ private[akka] trait DefaultSystemMessageQueue { self: Mailbox ⇒
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a marker trait for message queues which support multiple consumers,
|
||||||
|
* as is required by the BalancingDispatcher.
|
||||||
|
*/
|
||||||
|
trait MultipleConsumerSemantics
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A QueueBasedMessageQueue is a MessageQueue backed by a java.util.Queue.
|
* A QueueBasedMessageQueue is a MessageQueue backed by a java.util.Queue.
|
||||||
*/
|
*/
|
||||||
trait QueueBasedMessageQueue extends MessageQueue {
|
trait QueueBasedMessageQueue extends MessageQueue with MultipleConsumerSemantics {
|
||||||
def queue: Queue[Envelope]
|
def queue: Queue[Envelope]
|
||||||
def numberOfMessages = queue.size
|
def numberOfMessages = queue.size
|
||||||
def hasMessages = !queue.isEmpty
|
def hasMessages = !queue.isEmpty
|
||||||
|
|
@ -440,7 +446,9 @@ trait QueueBasedMessageQueue extends MessageQueue {
|
||||||
* UnboundedMessageQueueSemantics adds unbounded semantics to a QueueBasedMessageQueue,
|
* UnboundedMessageQueueSemantics adds unbounded semantics to a QueueBasedMessageQueue,
|
||||||
* i.e. a non-blocking enqueue and dequeue.
|
* i.e. a non-blocking enqueue and dequeue.
|
||||||
*/
|
*/
|
||||||
trait UnboundedMessageQueueSemantics extends QueueBasedMessageQueue {
|
trait UnboundedMessageQueueSemantics
|
||||||
|
|
||||||
|
trait UnboundedQueueBasedMessageQueue extends QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
||||||
def enqueue(receiver: ActorRef, handle: Envelope): Unit = queue add handle
|
def enqueue(receiver: ActorRef, handle: Envelope): Unit = queue add handle
|
||||||
def dequeue(): Envelope = queue.poll()
|
def dequeue(): Envelope = queue.poll()
|
||||||
}
|
}
|
||||||
|
|
@ -449,8 +457,11 @@ trait UnboundedMessageQueueSemantics extends QueueBasedMessageQueue {
|
||||||
* BoundedMessageQueueSemantics adds bounded semantics to a QueueBasedMessageQueue,
|
* BoundedMessageQueueSemantics adds bounded semantics to a QueueBasedMessageQueue,
|
||||||
* i.e. blocking enqueue with timeout.
|
* i.e. blocking enqueue with timeout.
|
||||||
*/
|
*/
|
||||||
trait BoundedMessageQueueSemantics extends QueueBasedMessageQueue {
|
trait BoundedMessageQueueSemantics {
|
||||||
def pushTimeOut: Duration
|
def pushTimeOut: Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
trait BoundedQueueBasedMessageQueue extends QueueBasedMessageQueue with BoundedMessageQueueSemantics {
|
||||||
override def queue: BlockingQueue[Envelope]
|
override def queue: BlockingQueue[Envelope]
|
||||||
|
|
||||||
def enqueue(receiver: ActorRef, handle: Envelope): Unit =
|
def enqueue(receiver: ActorRef, handle: Envelope): Unit =
|
||||||
|
|
@ -466,16 +477,23 @@ trait BoundedMessageQueueSemantics extends QueueBasedMessageQueue {
|
||||||
/**
|
/**
|
||||||
* DequeBasedMessageQueue refines QueueBasedMessageQueue to be backed by a java.util.Deque.
|
* DequeBasedMessageQueue refines QueueBasedMessageQueue to be backed by a java.util.Deque.
|
||||||
*/
|
*/
|
||||||
trait DequeBasedMessageQueue extends QueueBasedMessageQueue {
|
trait DequeBasedMessageQueueSemantics {
|
||||||
def queue: Deque[Envelope]
|
|
||||||
def enqueueFirst(receiver: ActorRef, handle: Envelope): Unit
|
def enqueueFirst(receiver: ActorRef, handle: Envelope): Unit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait UnboundedDequeBasedMessageQueueSemantics extends DequeBasedMessageQueueSemantics with UnboundedMessageQueueSemantics
|
||||||
|
|
||||||
|
trait BoundedDequeBasedMessageQueueSemantics extends DequeBasedMessageQueueSemantics with BoundedMessageQueueSemantics
|
||||||
|
|
||||||
|
trait DequeBasedMessageQueue extends QueueBasedMessageQueue with DequeBasedMessageQueueSemantics {
|
||||||
|
def queue: Deque[Envelope]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnboundedDequeBasedMessageQueueSemantics adds unbounded semantics to a DequeBasedMessageQueue,
|
* UnboundedDequeBasedMessageQueueSemantics adds unbounded semantics to a DequeBasedMessageQueue,
|
||||||
* i.e. a non-blocking enqueue and dequeue.
|
* i.e. a non-blocking enqueue and dequeue.
|
||||||
*/
|
*/
|
||||||
trait UnboundedDequeBasedMessageQueueSemantics extends DequeBasedMessageQueue {
|
trait UnboundedDequeBasedMessageQueue extends DequeBasedMessageQueue with UnboundedDequeBasedMessageQueueSemantics {
|
||||||
def enqueue(receiver: ActorRef, handle: Envelope): Unit = queue add handle
|
def enqueue(receiver: ActorRef, handle: Envelope): Unit = queue add handle
|
||||||
def enqueueFirst(receiver: ActorRef, handle: Envelope): Unit = queue addFirst handle
|
def enqueueFirst(receiver: ActorRef, handle: Envelope): Unit = queue addFirst handle
|
||||||
def dequeue(): Envelope = queue.poll()
|
def dequeue(): Envelope = queue.poll()
|
||||||
|
|
@ -485,7 +503,7 @@ trait UnboundedDequeBasedMessageQueueSemantics extends DequeBasedMessageQueue {
|
||||||
* BoundedMessageQueueSemantics adds bounded semantics to a DequeBasedMessageQueue,
|
* BoundedMessageQueueSemantics adds bounded semantics to a DequeBasedMessageQueue,
|
||||||
* i.e. blocking enqueue with timeout.
|
* i.e. blocking enqueue with timeout.
|
||||||
*/
|
*/
|
||||||
trait BoundedDequeBasedMessageQueueSemantics extends DequeBasedMessageQueue {
|
trait BoundedDequeBasedMessageQueue extends DequeBasedMessageQueue with BoundedDequeBasedMessageQueueSemantics {
|
||||||
def pushTimeOut: Duration
|
def pushTimeOut: Duration
|
||||||
override def queue: BlockingDeque[Envelope]
|
override def queue: BlockingDeque[Envelope]
|
||||||
|
|
||||||
|
|
@ -523,15 +541,21 @@ trait MailboxType {
|
||||||
def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue
|
def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait ProducesMessageQueue[T <: MessageQueue]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnboundedMailbox is the default unbounded MailboxType used by Akka Actors.
|
* UnboundedMailbox is the default unbounded MailboxType used by Akka Actors.
|
||||||
*/
|
*/
|
||||||
case class UnboundedMailbox() extends MailboxType {
|
case class UnboundedMailbox() extends MailboxType with ProducesMessageQueue[UnboundedMailbox.MessageQueue] {
|
||||||
|
|
||||||
def this(settings: ActorSystem.Settings, config: Config) = this()
|
def this(settings: ActorSystem.Settings, config: Config) = this()
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new ConcurrentLinkedQueue[Envelope]() with QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
new UnboundedMailbox.MessageQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
object UnboundedMailbox {
|
||||||
|
class MessageQueue extends ConcurrentLinkedQueue[Envelope] with UnboundedQueueBasedMessageQueue {
|
||||||
final def queue: Queue[Envelope] = this
|
final def queue: Queue[Envelope] = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -541,17 +565,18 @@ case class UnboundedMailbox() extends MailboxType {
|
||||||
* the only drawback is that you can't have multiple consumers,
|
* the only drawback is that you can't have multiple consumers,
|
||||||
* which rules out using it with BalancingDispatcher for instance.
|
* which rules out using it with BalancingDispatcher for instance.
|
||||||
*/
|
*/
|
||||||
case class SingleConsumerOnlyUnboundedMailbox() extends MailboxType {
|
case class SingleConsumerOnlyUnboundedMailbox() extends MailboxType with ProducesMessageQueue[NodeMessageQueue] {
|
||||||
|
|
||||||
def this(settings: ActorSystem.Settings, config: Config) = this()
|
def this(settings: ActorSystem.Settings, config: Config) = this()
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue = new NodeMessageQueue()
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue = new NodeMessageQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BoundedMailbox is the default bounded MailboxType used by Akka Actors.
|
* BoundedMailbox is the default bounded MailboxType used by Akka Actors.
|
||||||
*/
|
*/
|
||||||
case class BoundedMailbox( final val capacity: Int, final val pushTimeOut: FiniteDuration) extends MailboxType {
|
case class BoundedMailbox(val capacity: Int, val pushTimeOut: FiniteDuration)
|
||||||
|
extends MailboxType with ProducesMessageQueue[BoundedMailbox.MessageQueue] {
|
||||||
|
|
||||||
def this(settings: ActorSystem.Settings, config: Config) = this(config.getInt("mailbox-capacity"),
|
def this(settings: ActorSystem.Settings, config: Config) = this(config.getInt("mailbox-capacity"),
|
||||||
Duration(config.getNanoseconds("mailbox-push-timeout-time"), TimeUnit.NANOSECONDS))
|
Duration(config.getNanoseconds("mailbox-push-timeout-time"), TimeUnit.NANOSECONDS))
|
||||||
|
|
@ -560,9 +585,13 @@ case class BoundedMailbox( final val capacity: Int, final val pushTimeOut: Finit
|
||||||
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new LinkedBlockingQueue[Envelope](capacity) with QueueBasedMessageQueue with BoundedMessageQueueSemantics {
|
new BoundedMailbox.MessageQueue(capacity, pushTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
object BoundedMailbox {
|
||||||
|
class MessageQueue(capacity: Int, final val pushTimeOut: FiniteDuration)
|
||||||
|
extends LinkedBlockingQueue[Envelope](capacity) with BoundedQueueBasedMessageQueue {
|
||||||
final def queue: BlockingQueue[Envelope] = this
|
final def queue: BlockingQueue[Envelope] = this
|
||||||
final val pushTimeOut = BoundedMailbox.this.pushTimeOut
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -570,10 +599,16 @@ case class BoundedMailbox( final val capacity: Int, final val pushTimeOut: Finit
|
||||||
* UnboundedPriorityMailbox is an unbounded mailbox that allows for prioritization of its contents.
|
* UnboundedPriorityMailbox is an unbounded mailbox that allows for prioritization of its contents.
|
||||||
* Extend this class and provide the Comparator in the constructor.
|
* Extend this class and provide the Comparator in the constructor.
|
||||||
*/
|
*/
|
||||||
class UnboundedPriorityMailbox( final val cmp: Comparator[Envelope], final val initialCapacity: Int) extends MailboxType {
|
class UnboundedPriorityMailbox(val cmp: Comparator[Envelope], val initialCapacity: Int)
|
||||||
|
extends MailboxType with ProducesMessageQueue[UnboundedPriorityMailbox.MessageQueue] {
|
||||||
def this(cmp: Comparator[Envelope]) = this(cmp, 11)
|
def this(cmp: Comparator[Envelope]) = this(cmp, 11)
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new PriorityBlockingQueue[Envelope](initialCapacity, cmp) with QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
new UnboundedPriorityMailbox.MessageQueue(initialCapacity, cmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
object UnboundedPriorityMailbox {
|
||||||
|
class MessageQueue(initialCapacity: Int, cmp: Comparator[Envelope])
|
||||||
|
extends PriorityBlockingQueue[Envelope](initialCapacity, cmp) with UnboundedQueueBasedMessageQueue {
|
||||||
final def queue: Queue[Envelope] = this
|
final def queue: Queue[Envelope] = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -582,27 +617,37 @@ class UnboundedPriorityMailbox( final val cmp: Comparator[Envelope], final val i
|
||||||
* BoundedPriorityMailbox is a bounded mailbox that allows for prioritization of its contents.
|
* BoundedPriorityMailbox is a bounded mailbox that allows for prioritization of its contents.
|
||||||
* Extend this class and provide the Comparator in the constructor.
|
* Extend this class and provide the Comparator in the constructor.
|
||||||
*/
|
*/
|
||||||
class BoundedPriorityMailbox( final val cmp: Comparator[Envelope], final val capacity: Int, final val pushTimeOut: Duration) extends MailboxType {
|
class BoundedPriorityMailbox( final val cmp: Comparator[Envelope], final val capacity: Int, final val pushTimeOut: Duration)
|
||||||
|
extends MailboxType with ProducesMessageQueue[BoundedPriorityMailbox.MessageQueue] {
|
||||||
|
|
||||||
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
if (capacity < 0) throw new IllegalArgumentException("The capacity for BoundedMailbox can not be negative")
|
||||||
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedMailbox can not be null")
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new BoundedBlockingQueue[Envelope](capacity, new PriorityQueue[Envelope](11, cmp)) with QueueBasedMessageQueue with BoundedMessageQueueSemantics {
|
new BoundedPriorityMailbox.MessageQueue(capacity, cmp, pushTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
object BoundedPriorityMailbox {
|
||||||
|
class MessageQueue(capacity: Int, cmp: Comparator[Envelope], val pushTimeOut: Duration)
|
||||||
|
extends BoundedBlockingQueue[Envelope](capacity, new PriorityQueue[Envelope](11, cmp))
|
||||||
|
with BoundedQueueBasedMessageQueue {
|
||||||
final def queue: BlockingQueue[Envelope] = this
|
final def queue: BlockingQueue[Envelope] = this
|
||||||
final val pushTimeOut = BoundedPriorityMailbox.this.pushTimeOut
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UnboundedDequeBasedMailbox is an unbounded MailboxType, backed by a Deque.
|
* UnboundedDequeBasedMailbox is an unbounded MailboxType, backed by a Deque.
|
||||||
*/
|
*/
|
||||||
case class UnboundedDequeBasedMailbox() extends MailboxType {
|
case class UnboundedDequeBasedMailbox() extends MailboxType with ProducesMessageQueue[UnboundedDequeBasedMailbox.MessageQueue] {
|
||||||
|
|
||||||
def this(settings: ActorSystem.Settings, config: Config) = this()
|
def this(settings: ActorSystem.Settings, config: Config) = this()
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new LinkedBlockingDeque[Envelope]() with DequeBasedMessageQueue with UnboundedDequeBasedMessageQueueSemantics {
|
new UnboundedDequeBasedMailbox.MessageQueue
|
||||||
|
}
|
||||||
|
|
||||||
|
object UnboundedDequeBasedMailbox {
|
||||||
|
class MessageQueue extends LinkedBlockingDeque[Envelope] with UnboundedDequeBasedMessageQueue {
|
||||||
final val queue = this
|
final val queue = this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -610,7 +655,8 @@ case class UnboundedDequeBasedMailbox() extends MailboxType {
|
||||||
/**
|
/**
|
||||||
* BoundedDequeBasedMailbox is an bounded MailboxType, backed by a Deque.
|
* BoundedDequeBasedMailbox is an bounded MailboxType, backed by a Deque.
|
||||||
*/
|
*/
|
||||||
case class BoundedDequeBasedMailbox( final val capacity: Int, final val pushTimeOut: FiniteDuration) extends MailboxType {
|
case class BoundedDequeBasedMailbox( final val capacity: Int, final val pushTimeOut: FiniteDuration)
|
||||||
|
extends MailboxType with ProducesMessageQueue[BoundedDequeBasedMailbox.MessageQueue] {
|
||||||
|
|
||||||
def this(settings: ActorSystem.Settings, config: Config) = this(config.getInt("mailbox-capacity"),
|
def this(settings: ActorSystem.Settings, config: Config) = this(config.getInt("mailbox-capacity"),
|
||||||
Duration(config.getNanoseconds("mailbox-push-timeout-time"), TimeUnit.NANOSECONDS))
|
Duration(config.getNanoseconds("mailbox-push-timeout-time"), TimeUnit.NANOSECONDS))
|
||||||
|
|
@ -619,9 +665,13 @@ case class BoundedDequeBasedMailbox( final val capacity: Int, final val pushTime
|
||||||
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedDequeBasedMailbox can not be null")
|
if (pushTimeOut eq null) throw new IllegalArgumentException("The push time-out for BoundedDequeBasedMailbox can not be null")
|
||||||
|
|
||||||
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
final override def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue =
|
||||||
new LinkedBlockingDeque[Envelope](capacity) with DequeBasedMessageQueue with BoundedDequeBasedMessageQueueSemantics {
|
new BoundedDequeBasedMailbox.MessageQueue(capacity, pushTimeOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
object BoundedDequeBasedMailbox {
|
||||||
|
class MessageQueue(capacity: Int, val pushTimeOut: FiniteDuration)
|
||||||
|
extends LinkedBlockingDeque[Envelope](capacity) with BoundedDequeBasedMessageQueue {
|
||||||
final val queue = this
|
final val queue = this
|
||||||
final val pushTimeOut = BoundedDequeBasedMailbox.this.pushTimeOut
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,24 @@ import akka.event.Logging.Warning
|
||||||
import akka.ConfigurationException
|
import akka.ConfigurationException
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
|
import akka.util.Reflect
|
||||||
|
import akka.actor.Props
|
||||||
|
import akka.actor.Deploy
|
||||||
|
import scala.util.Try
|
||||||
|
import scala.util.Failure
|
||||||
|
import scala.util.control.NonFatal
|
||||||
|
import akka.dispatch.DispatcherPrerequisites
|
||||||
|
|
||||||
|
object Mailboxes {
|
||||||
|
final val DefaultMailboxId = "akka.actor.default-mailbox"
|
||||||
|
}
|
||||||
|
|
||||||
private[akka] class Mailboxes(
|
private[akka] class Mailboxes(
|
||||||
val settings: ActorSystem.Settings,
|
val settings: ActorSystem.Settings,
|
||||||
val eventStream: EventStream,
|
val eventStream: EventStream,
|
||||||
dynamicAccess: DynamicAccess) {
|
dynamicAccess: DynamicAccess) {
|
||||||
|
|
||||||
private val mailboxTypeConfigurators = new ConcurrentHashMap[String, Option[MailboxTypeConfigurator]]
|
private val mailboxTypeConfigurators = new ConcurrentHashMap[String, MailboxType]
|
||||||
|
|
||||||
private val mailboxBindings: Map[Class[_ <: Any], String] = {
|
private val mailboxBindings: Map[Class[_ <: Any], String] = {
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
|
|
@ -38,108 +49,148 @@ private[akka] class Mailboxes(
|
||||||
/**
|
/**
|
||||||
* Returns a mailbox type as specified in configuration, based on the id, or if not defined None.
|
* Returns a mailbox type as specified in configuration, based on the id, or if not defined None.
|
||||||
*/
|
*/
|
||||||
def lookup(id: String): Option[MailboxType] = lookupConfigurator(id).map { _.mailboxType }
|
def lookup(id: String): MailboxType = lookupConfigurator(id)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a mailbox type as specified in configuration, based on the type, or if not defined None.
|
* Returns a mailbox type as specified in configuration, based on the type, or if not defined None.
|
||||||
*/
|
*/
|
||||||
def lookupByQueueType(queueType: Class[_ <: Any]): Option[MailboxType] =
|
def lookupByQueueType(queueType: Class[_ <: Any]): MailboxType = lookup(lookupId(queueType))
|
||||||
lookupId(queueType).flatMap(x ⇒ lookup(x))
|
|
||||||
|
|
||||||
private final val rmqClass = classOf[RequiresMessageQueue[_]]
|
private final val rmqClass = classOf[RequiresMessageQueue[_]]
|
||||||
/**
|
/**
|
||||||
* Return the required message queue type for this class if any.
|
* Return the required message queue type for this class if any.
|
||||||
*/
|
*/
|
||||||
def getRequiredType(actorClass: Class[_ <: Actor]): Option[Class[_]] = {
|
def getRequiredType(actorClass: Class[_ <: Actor]): Class[_] =
|
||||||
@tailrec
|
Reflect.findMarker(actorClass, rmqClass) match {
|
||||||
def innerRequiredType(classes: Iterator[Class[_]]): Option[Class[_]] = {
|
case t: ParameterizedType ⇒ t.getActualTypeArguments.head match {
|
||||||
if (classes.isEmpty) None
|
|
||||||
else {
|
|
||||||
val c = classes.next()
|
|
||||||
if (rmqClass.isAssignableFrom(c)) {
|
|
||||||
val ifaces = c.getGenericInterfaces
|
|
||||||
val tpe = ifaces.collectFirst {
|
|
||||||
case t: ParameterizedType if rmqClass.isAssignableFrom(t.getRawType.asInstanceOf[Class[_]]) ⇒
|
|
||||||
t.getActualTypeArguments.head.asInstanceOf[Class[_]]
|
|
||||||
}
|
|
||||||
if (tpe.isDefined) tpe
|
|
||||||
else innerRequiredType(classes ++ ifaces.map {
|
|
||||||
case c: Class[_] ⇒ c
|
case c: Class[_] ⇒ c
|
||||||
case c: ParameterizedType ⇒ c.getRawType.asInstanceOf[Class[_]]
|
case x ⇒ throw new IllegalArgumentException(s"no wildcard type allowed in RequireMessageQueue argument (was [$x])")
|
||||||
} ++ Iterator(c.getSuperclass))
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// don’t care if this happens twice
|
||||||
|
private var mailboxSizeWarningIssued = false
|
||||||
|
|
||||||
|
def getMailboxRequirement(config: Config) = config.getString("mailbox-requirement") match {
|
||||||
|
case "" ⇒ classOf[MessageQueue]
|
||||||
|
case x ⇒ dynamicAccess.getClassFor[AnyRef](x).get
|
||||||
|
}
|
||||||
|
|
||||||
|
def getProducedMessageQueueType(mailboxType: MailboxType): Class[_] = {
|
||||||
|
val pmqClass = classOf[ProducesMessageQueue[_]]
|
||||||
|
if (!pmqClass.isAssignableFrom(mailboxType.getClass)) classOf[MessageQueue]
|
||||||
|
else Reflect.findMarker(mailboxType.getClass, pmqClass) match {
|
||||||
|
case t: ParameterizedType ⇒
|
||||||
|
t.getActualTypeArguments.head match {
|
||||||
|
case c: Class[_] ⇒ c
|
||||||
|
case x ⇒ throw new IllegalArgumentException(
|
||||||
|
s"no wildcard type allowed in ProducesMessageQueue argument (was [$x])")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds out the mailbox type for an actor based on configuration, props and requirements.
|
||||||
|
*/
|
||||||
|
protected[akka] def getMailboxType(props: Props, dispatcherConfig: Config): MailboxType = {
|
||||||
|
val id = dispatcherConfig.getString("id")
|
||||||
|
val deploy = props.deploy
|
||||||
|
val actorClass = props.actorClass
|
||||||
|
lazy val actorRequirement = getRequiredType(actorClass)
|
||||||
|
|
||||||
|
val mailboxRequirement: Class[_] = getMailboxRequirement(dispatcherConfig)
|
||||||
|
|
||||||
|
val hasMailboxRequirement: Boolean = mailboxRequirement != classOf[MessageQueue]
|
||||||
|
|
||||||
|
val hasMailboxType =
|
||||||
|
dispatcherConfig.hasPath("mailbox-type") &&
|
||||||
|
dispatcherConfig.getString("mailbox-type") != Deploy.NoMailboxGiven
|
||||||
|
|
||||||
|
// TODO remove in 2.3
|
||||||
|
if (!hasMailboxType && !mailboxSizeWarningIssued && dispatcherConfig.hasPath("mailbox-size")) {
|
||||||
|
eventStream.publish(Warning("mailboxes", getClass,
|
||||||
|
"ignoring setting 'mailbox-size' for dispatcher [$id], you need to specify 'mailbox-type=bounded'"))
|
||||||
|
mailboxSizeWarningIssued = true
|
||||||
|
}
|
||||||
|
|
||||||
|
def verifyRequirements(mailboxType: MailboxType): MailboxType = {
|
||||||
|
lazy val mqType: Class[_] = getProducedMessageQueueType(mailboxType)
|
||||||
|
if (hasMailboxRequirement && !mailboxRequirement.isAssignableFrom(mqType))
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"produced message queue type [$mqType] does not fulfill requirement for dispatcher [${id}]")
|
||||||
|
if (hasRequiredType(actorClass) && !actorRequirement.isAssignableFrom(mqType))
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"produced message queue type [$mqType] does not fulfill requirement for actor class [$actorClass]")
|
||||||
|
mailboxType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deploy.mailbox != Deploy.NoMailboxGiven) {
|
||||||
|
verifyRequirements(lookup(deploy.mailbox))
|
||||||
|
} else if (deploy.dispatcher != Deploy.NoDispatcherGiven && hasMailboxType) {
|
||||||
|
verifyRequirements(lookup(dispatcherConfig.getString("id")))
|
||||||
|
} else if (hasRequiredType(actorClass)) {
|
||||||
|
try verifyRequirements(lookupByQueueType(getRequiredType(actorClass)))
|
||||||
|
catch {
|
||||||
|
case NonFatal(thr) if (hasMailboxRequirement) ⇒ verifyRequirements(lookupByQueueType(mailboxRequirement))
|
||||||
|
}
|
||||||
|
} else if (hasMailboxRequirement) {
|
||||||
|
verifyRequirements(lookupByQueueType(mailboxRequirement))
|
||||||
} else {
|
} else {
|
||||||
innerRequiredType(classes)
|
verifyRequirements(lookup(Mailboxes.DefaultMailboxId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (rmqClass.isAssignableFrom(actorClass)) innerRequiredType(Iterator(actorClass))
|
|
||||||
else None
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this class can have a required message queue type.
|
* Check if this class can have a required message queue type.
|
||||||
*/
|
*/
|
||||||
def hasRequiredType(actorClass: Class[_ <: Actor]): Boolean = rmqClass.isAssignableFrom(actorClass)
|
def hasRequiredType(actorClass: Class[_ <: Actor]): Boolean = rmqClass.isAssignableFrom(actorClass)
|
||||||
|
|
||||||
private def lookupId(queueType: Class[_]): Option[String] = {
|
private def lookupId(queueType: Class[_]): String =
|
||||||
mailboxBindings.get(queueType) match {
|
mailboxBindings.get(queueType) match {
|
||||||
case None ⇒
|
case None ⇒ throw new IllegalArgumentException(s"Mailbox Mapping for [${queueType}] not configured")
|
||||||
eventStream.publish(Warning("Mailboxes", this.getClass, s"Mailbox Mapping for [${queueType}] not configured"))
|
case Some(s) ⇒ s
|
||||||
None
|
|
||||||
case s ⇒ s
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def lookupConfigurator(id: String): Option[MailboxTypeConfigurator] = {
|
private def lookupConfigurator(id: String): MailboxType = {
|
||||||
mailboxTypeConfigurators.get(id) match {
|
mailboxTypeConfigurators.get(id) match {
|
||||||
case null ⇒
|
case null ⇒
|
||||||
// It doesn't matter if we create a mailbox type configurator that isn't used due to concurrent lookup.
|
// It doesn't matter if we create a mailbox type configurator that isn't used due to concurrent lookup.
|
||||||
val newConfigurator =
|
val newConfigurator = id match {
|
||||||
if (settings.config.hasPath(id)) {
|
case "unbounded" ⇒ UnboundedMailbox()
|
||||||
Some(new MailboxTypeConfigurator(settings, config(id), dynamicAccess))
|
case "bounded" ⇒ new BoundedMailbox(settings, config(id))
|
||||||
} else {
|
case _ ⇒
|
||||||
eventStream.publish(Warning("Mailboxes", this.getClass, s"Mailbox Type [${id}] not configured"))
|
if (!settings.config.hasPath(id)) throw new IllegalArgumentException(s"Mailbox Type [${id}] not configured")
|
||||||
None
|
val conf = config(id)
|
||||||
}
|
conf.getString("mailbox-type") match {
|
||||||
|
case "" ⇒ throw new IllegalArgumentException(s"The setting mailbox-type, defined in [$id] is empty")
|
||||||
if (newConfigurator.isDefined) {
|
|
||||||
mailboxTypeConfigurators.putIfAbsent(id, newConfigurator) match {
|
|
||||||
case null ⇒ newConfigurator
|
|
||||||
case existing ⇒ existing
|
|
||||||
}
|
|
||||||
} else None
|
|
||||||
|
|
||||||
case existing ⇒ existing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//INTERNAL API
|
|
||||||
private def config(id: String): Config = {
|
|
||||||
import scala.collection.JavaConverters._
|
|
||||||
ConfigFactory.parseMap(Map("id" -> id).asJava)
|
|
||||||
.withFallback(settings.config.getConfig(id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private[akka] class MailboxTypeConfigurator(
|
|
||||||
val settings: ActorSystem.Settings,
|
|
||||||
val config: Config,
|
|
||||||
dynamicAccess: DynamicAccess) {
|
|
||||||
private val instance: MailboxType = {
|
|
||||||
val id = config.getString("id")
|
|
||||||
config.getString("mailbox-type") match {
|
|
||||||
case "" ⇒ throw new IllegalArgumentException(s"The setting mailbox-type, defined in [${id}}] is empty")
|
|
||||||
case fqcn ⇒
|
case fqcn ⇒
|
||||||
val args = List(classOf[ActorSystem.Settings] -> settings, classOf[Config] -> config)
|
val args = List(classOf[ActorSystem.Settings] -> settings, classOf[Config] -> conf)
|
||||||
dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
|
dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
|
||||||
case exception ⇒
|
case exception ⇒
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
(s"Cannot instantiate MailboxType [${fqcn}], defined in [${id}], make sure it has a public" +
|
(s"Cannot instantiate MailboxType [$fqcn], defined in [$id], make sure it has a public" +
|
||||||
" constructor with [akka.actor.ActorSystem.Settings, com.typesafe.config.Config] parameters"),
|
" constructor with [akka.actor.ActorSystem.Settings, com.typesafe.config.Config] parameters"),
|
||||||
exception)
|
exception)
|
||||||
}).get
|
}).get
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def mailboxType: MailboxType = instance
|
mailboxTypeConfigurators.putIfAbsent(id, newConfigurator) match {
|
||||||
|
case null ⇒ newConfigurator
|
||||||
|
case existing ⇒ existing
|
||||||
|
}
|
||||||
|
|
||||||
|
case existing ⇒ existing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val defaultMailboxConfig = settings.config.getConfig(Mailboxes.DefaultMailboxId)
|
||||||
|
|
||||||
|
//INTERNAL API
|
||||||
|
private def config(id: String): Config = {
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
ConfigFactory.parseMap(Map("id" -> id).asJava)
|
||||||
|
.withFallback(settings.config.getConfig(id))
|
||||||
|
.withFallback(defaultMailboxConfig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,15 @@ import scala.concurrent.duration.FiniteDuration
|
||||||
* the `lookup` method in [[akka.dispatch.Dispatchers]].
|
* the `lookup` method in [[akka.dispatch.Dispatchers]].
|
||||||
*/
|
*/
|
||||||
class PinnedDispatcher(
|
class PinnedDispatcher(
|
||||||
_prerequisites: DispatcherPrerequisites,
|
_configurator: MessageDispatcherConfigurator,
|
||||||
_actor: ActorCell,
|
_actor: ActorCell,
|
||||||
_id: String,
|
_id: String,
|
||||||
_mailboxType: MailboxType,
|
|
||||||
_mailboxTypeConfigured: Boolean,
|
|
||||||
_shutdownTimeout: FiniteDuration,
|
_shutdownTimeout: FiniteDuration,
|
||||||
_threadPoolConfig: ThreadPoolConfig)
|
_threadPoolConfig: ThreadPoolConfig)
|
||||||
extends Dispatcher(_prerequisites,
|
extends Dispatcher(_configurator,
|
||||||
_id,
|
_id,
|
||||||
Int.MaxValue,
|
Int.MaxValue,
|
||||||
Duration.Zero,
|
Duration.Zero,
|
||||||
_mailboxType,
|
|
||||||
_mailboxTypeConfigured,
|
|
||||||
_threadPoolConfig.copy(corePoolSize = 1, maxPoolSize = 1),
|
_threadPoolConfig.copy(corePoolSize = 1, maxPoolSize = 1),
|
||||||
_shutdownTimeout) {
|
_shutdownTimeout) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ package akka.routing
|
||||||
|
|
||||||
import language.implicitConversions
|
import language.implicitConversions
|
||||||
import language.postfixOps
|
import language.postfixOps
|
||||||
|
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
import scala.concurrent.duration._
|
import scala.concurrent.duration._
|
||||||
import akka.actor._
|
import akka.actor._
|
||||||
|
|
@ -20,22 +19,33 @@ import akka.event.Logging.Warning
|
||||||
import scala.concurrent.forkjoin.ThreadLocalRandom
|
import scala.concurrent.forkjoin.ThreadLocalRandom
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
import akka.event.Logging.Warning
|
import akka.event.Logging.Warning
|
||||||
|
import akka.dispatch.{ MailboxType, MessageDispatcher }
|
||||||
|
import akka.dispatch.BalancingDispatcher
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A RoutedActorRef is an ActorRef that has a set of connected ActorRef and it uses a Router to
|
* A RoutedActorRef is an ActorRef that has a set of connected ActorRef and it uses a Router to
|
||||||
* send a message to one (or more) of these actors.
|
* send a message to one (or more) of these actors.
|
||||||
*/
|
*/
|
||||||
private[akka] class RoutedActorRef(_system: ActorSystemImpl, _props: Props, _supervisor: InternalActorRef, _path: ActorPath)
|
private[akka] class RoutedActorRef(
|
||||||
extends RepointableActorRef(_system, _props, _supervisor, _path) {
|
_system: ActorSystemImpl,
|
||||||
|
_routerProps: Props,
|
||||||
|
_routerDispatcher: MessageDispatcher,
|
||||||
|
_routerMailbox: MailboxType,
|
||||||
|
_routeeProps: Props,
|
||||||
|
_supervisor: InternalActorRef,
|
||||||
|
_path: ActorPath)
|
||||||
|
extends RepointableActorRef(_system, _routerProps, _routerDispatcher, _routerMailbox, _supervisor, _path) {
|
||||||
|
|
||||||
// verify that a BalancingDispatcher is not used with a Router
|
// verify that a BalancingDispatcher is not used with a Router
|
||||||
if (_props.routerConfig != NoRouter && _system.dispatchers.isBalancingDispatcher(_props.routerConfig.routerDispatcher)) {
|
if (_routerProps.routerConfig != NoRouter && _routerDispatcher.isInstanceOf[BalancingDispatcher]) {
|
||||||
throw new ConfigurationException(
|
throw new ConfigurationException(
|
||||||
"Configuration for " + this +
|
"Configuration for " + this +
|
||||||
" is invalid - you can not use a 'BalancingDispatcher' as a Router's dispatcher, you can however use it for the routees.")
|
" is invalid - you can not use a 'BalancingDispatcher' as a Router's dispatcher, you can however use it for the routees.")
|
||||||
} else _props.routerConfig.verifyConfig(_path)
|
} else _routerProps.routerConfig.verifyConfig(_path)
|
||||||
|
|
||||||
override def newCell(old: UnstartedCell): Cell = new RoutedActorCell(system, this, props, supervisor).init(sendSupervise = false)
|
override def newCell(old: UnstartedCell): Cell =
|
||||||
|
new RoutedActorCell(system, this, props, dispatcher, _routeeProps, supervisor)
|
||||||
|
.init(sendSupervise = false, mailboxType)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,16 +56,16 @@ private[akka] object RoutedActorCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private[akka] final class RoutedActorCell(_system: ActorSystemImpl, _ref: InternalActorRef, _props: Props, _supervisor: InternalActorRef)
|
private[akka] final class RoutedActorCell(
|
||||||
extends ActorCell(
|
_system: ActorSystemImpl,
|
||||||
_system,
|
_ref: InternalActorRef,
|
||||||
_ref,
|
_routerProps: Props,
|
||||||
_props.copy(
|
_routerDispatcher: MessageDispatcher,
|
||||||
deploy = _props.deploy.copy(dispatcher = _props.routerConfig.routerDispatcher),
|
val routeeProps: Props,
|
||||||
classOf[RoutedActorCell.RouterCreator], Vector(_props.routerConfig)),
|
_supervisor: InternalActorRef)
|
||||||
_supervisor) {
|
extends ActorCell(_system, _ref, _routerProps, _routerDispatcher, _supervisor) {
|
||||||
|
|
||||||
private[akka] val routerConfig = _props.routerConfig
|
private[akka] val routerConfig = _routerProps.routerConfig
|
||||||
private[akka] val resizeInProgress = new AtomicBoolean
|
private[akka] val resizeInProgress = new AtomicBoolean
|
||||||
private val resizeCounter = new AtomicLong
|
private val resizeCounter = new AtomicLong
|
||||||
|
|
||||||
|
|
@ -72,7 +82,6 @@ private[akka] final class RoutedActorCell(_system: ActorSystemImpl, _ref: Intern
|
||||||
def route = _route
|
def route = _route
|
||||||
|
|
||||||
private def startRoute() {
|
private def startRoute() {
|
||||||
val routeeProps = _props.withRouter(NoRouter)
|
|
||||||
_routeeProvider = routerConfig.createRouteeProvider(this, routeeProps)
|
_routeeProvider = routerConfig.createRouteeProvider(this, routeeProps)
|
||||||
val r = routerConfig.createRoute(routeeProvider)
|
val r = routerConfig.createRoute(routeeProvider)
|
||||||
// initial resize, before message send
|
// initial resize, before message send
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ package akka.util
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import java.lang.reflect.Constructor
|
import java.lang.reflect.Constructor
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import scala.annotation.tailrec
|
||||||
|
import java.lang.reflect.ParameterizedType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of internal reflection utilities which may or may not be
|
* Collection of internal reflection utilities which may or may not be
|
||||||
|
|
@ -95,4 +98,20 @@ private[akka] object Reflect {
|
||||||
* @return a function which when applied will create a new instance from the default constructor of the given class
|
* @return a function which when applied will create a new instance from the default constructor of the given class
|
||||||
*/
|
*/
|
||||||
private[akka] def instantiator[T](clazz: Class[T]): () ⇒ T = () ⇒ instantiate(clazz)
|
private[akka] def instantiator[T](clazz: Class[T]): () ⇒ T = () ⇒ instantiate(clazz)
|
||||||
|
|
||||||
|
def findMarker(root: Class[_], marker: Class[_]): Type = {
|
||||||
|
@tailrec def rec(curr: Class[_]): Type = {
|
||||||
|
if (curr.getSuperclass != null && marker.isAssignableFrom(curr.getSuperclass)) rec(curr.getSuperclass)
|
||||||
|
else curr.getGenericInterfaces collectFirst {
|
||||||
|
case c: Class[_] if marker isAssignableFrom c ⇒ c
|
||||||
|
case t: ParameterizedType if marker isAssignableFrom t.getRawType.asInstanceOf[Class[_]] ⇒ t
|
||||||
|
} match {
|
||||||
|
case None ⇒ throw new IllegalArgumentException("cannot find [$marker] in ancestors of [$root]")
|
||||||
|
case Some(c: Class[_]) ⇒ if (c == marker) c else rec(c)
|
||||||
|
case Some(t: ParameterizedType) ⇒ if (t.getRawType == marker) t else rec(t.getRawType.asInstanceOf[Class[_]])
|
||||||
|
case _ ⇒ ??? // cannot happen due to collectFirst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rec(root)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import java.util.concurrent.{ ConcurrentHashMap, ConcurrentLinkedQueue }
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
|
||||||
import akka.actor.{ ActorContext, ActorRef, ActorSystem, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider }
|
import akka.actor.{ ActorContext, ActorRef, ActorSystem, ExtendedActorSystem, Extension, ExtensionId, ExtensionIdProvider }
|
||||||
import akka.dispatch.{ Envelope, MailboxType, MessageQueue, QueueBasedMessageQueue, UnboundedMessageQueueSemantics }
|
import akka.dispatch.{ Envelope, MailboxType, MessageQueue, UnboundedQueueBasedMessageQueue }
|
||||||
|
|
||||||
object PeekMailboxExtension extends ExtensionId[PeekMailboxExtension] with ExtensionIdProvider {
|
object PeekMailboxExtension extends ExtensionId[PeekMailboxExtension] with ExtensionIdProvider {
|
||||||
def lookup = this
|
def lookup = this
|
||||||
|
|
@ -53,7 +53,7 @@ class PeekMailboxType(settings: ActorSystem.Settings, config: Config) extends Ma
|
||||||
}
|
}
|
||||||
|
|
||||||
class PeekMailbox(owner: ActorRef, system: ActorSystem, maxRetries: Int)
|
class PeekMailbox(owner: ActorRef, system: ActorSystem, maxRetries: Int)
|
||||||
extends QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
extends UnboundedQueueBasedMessageQueue {
|
||||||
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,96 @@
|
||||||
.. _mailboxes-java:
|
.. _mailboxes-java:
|
||||||
|
|
||||||
Mailboxes
|
Mailboxes
|
||||||
=========
|
#########
|
||||||
|
|
||||||
An Akka ``Mailbox`` holds the messages that are destined for an ``Actor``.
|
An Akka ``Mailbox`` holds the messages that are destined for an ``Actor``.
|
||||||
Normally each ``Actor`` has its own mailbox, but with for example a ``BalancingDispatcher``
|
Normally each ``Actor`` has its own mailbox, but with for example a ``BalancingDispatcher``
|
||||||
all actors with the same ``BalancingDispatcher`` will share a single instance.
|
all actors with the same ``BalancingDispatcher`` will share a single instance.
|
||||||
|
|
||||||
|
Mailbox Selection
|
||||||
|
=================
|
||||||
|
|
||||||
|
Requiring a Message Queue Type for an Actor
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
It is possible to require a certain type of message queue for a certain type of actor
|
||||||
|
by having that actor implement the parameterized interface :class:`RequiresMessageQueue`. Here is
|
||||||
|
an example:
|
||||||
|
|
||||||
|
.. includecode:: code/docs/actor/MyBoundedUntypedActor.java#my-bounded-untyped-actor
|
||||||
|
|
||||||
|
The type parameter to the :class:`RequiresMessageQueue` interface needs to be mapped to a mailbox in
|
||||||
|
configuration like this:
|
||||||
|
|
||||||
|
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala
|
||||||
|
:include: bounded-mailbox-config,required-mailbox-config
|
||||||
|
|
||||||
|
Now every time you create an actor of type :class:`MyBoundedUntypedActor` it will try to get a bounded
|
||||||
|
mailbox. If the actor has a different mailbox configured in deployment, either directly or via
|
||||||
|
a dispatcher with a specified mailbox type, then that will override this mapping.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The type of the queue in the mailbox created for an actor will be checked against the required type in the
|
||||||
|
interface and if the queue doesn't implement the required type then actor creation will fail.
|
||||||
|
|
||||||
|
Requiring a Message Queue Type for a Dispatcher
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
A dispatcher may also have a requirement for the mailbox type used by the
|
||||||
|
actors running on it. An example is the BalancingDispatcher which requires a
|
||||||
|
message queue that is thread-safe for multiple concurrent consumers. Such a
|
||||||
|
requirement is formulated within the dispatcher configuration section like
|
||||||
|
this::
|
||||||
|
|
||||||
|
my-dispatcher {
|
||||||
|
mailbox-requirement = org.example.MyInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
The given requirement names a class or interface which will then be ensured to
|
||||||
|
be a supertype of the message queue’s implementation. In case of a
|
||||||
|
conflict—e.g. if the actor requires a mailbox type which does not satisfy this
|
||||||
|
requirement—then actor creation will fail.
|
||||||
|
|
||||||
|
How the Mailbox Type is Selected
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
When an actor is created, the :class:`ActorRefProvider` first determines the
|
||||||
|
dispatcher which will execute it. Then the mailbox is determined as follows:
|
||||||
|
|
||||||
|
1. If the actor’s deployment configuration section contains a ``mailbox`` key
|
||||||
|
then that names a configuration section describing the mailbox type to be
|
||||||
|
used.
|
||||||
|
|
||||||
|
2. If the actor’s ``Props`` contains a mailbox selection—i.e. ``withMailbox``
|
||||||
|
was called on it—then that names a configuration section describing the
|
||||||
|
mailbox type to be used.
|
||||||
|
|
||||||
|
3. If the dispatcher’s configuration section contains a ``mailbox-type`` key
|
||||||
|
the same section will be used to configure the mailbox type.
|
||||||
|
|
||||||
|
4. If the actor requires a mailbox type as described above then the mapping for
|
||||||
|
that requirement will be used to determine the mailbox type to be used; if
|
||||||
|
that fails then the dispatcher’s requirement—if any—will be tried instead.
|
||||||
|
|
||||||
|
5. If the dispatcher requires a mailbox type as described above then the
|
||||||
|
mapping for that requirement will be used to determine the mailbox type to
|
||||||
|
be used.
|
||||||
|
|
||||||
|
6. The default mailbox ``akka.actor.default-mailbox`` will be used.
|
||||||
|
|
||||||
|
Which Configuration is pass to the Mailbox Type
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Each mailbox type is implemented by a class which extends :class:`MailboxType`
|
||||||
|
and takes two constructor arguments: a :class:`ActorSystem.Settings` object and
|
||||||
|
a :class:`Config` section. The latter is computed by obtaining the named
|
||||||
|
configuration section from the actor system’s configuration, overriding its
|
||||||
|
``id`` key with the configuration path of the mailbox type and adding a
|
||||||
|
fall-back to the default mailbox configuration section.
|
||||||
|
|
||||||
Builtin Mailbox Implementations
|
Builtin Mailbox Implementations
|
||||||
-------------------------------
|
===============================
|
||||||
|
|
||||||
Akka comes shipped with a number of default mailbox implementations:
|
Akka comes shipped with a number of default mailbox implementations:
|
||||||
|
|
||||||
|
|
@ -47,7 +129,7 @@ Akka comes shipped with a number of default mailbox implementations:
|
||||||
* Durable mailboxes, see :ref:`durable-mailboxes-java`.
|
* Durable mailboxes, see :ref:`durable-mailboxes-java`.
|
||||||
|
|
||||||
Mailbox configuration examples
|
Mailbox configuration examples
|
||||||
------------------------------
|
==============================
|
||||||
|
|
||||||
How to create a PriorityMailbox:
|
How to create a PriorityMailbox:
|
||||||
|
|
||||||
|
|
@ -75,44 +157,8 @@ Or code like this:
|
||||||
.. includecode:: code/docs/dispatcher/DispatcherDocTest.java#defining-mailbox-in-code
|
.. includecode:: code/docs/dispatcher/DispatcherDocTest.java#defining-mailbox-in-code
|
||||||
|
|
||||||
|
|
||||||
Requiring a message queue type for an Actor
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
It is possible to require a certain type of message queue for a certain type of actor
|
|
||||||
by having that actor implement the parameterized interface :class:`RequiresMessageQueue`. Here is
|
|
||||||
an example:
|
|
||||||
|
|
||||||
.. includecode:: code/docs/actor/MyBoundedUntypedActor.java#my-bounded-untyped-actor
|
|
||||||
|
|
||||||
The type parameter to the :class:`RequiresMessageQueue` interface needs to be mapped to a mailbox in
|
|
||||||
configuration like this:
|
|
||||||
|
|
||||||
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala
|
|
||||||
:include: bounded-mailbox-config,required-mailbox-config
|
|
||||||
|
|
||||||
Now every time you create an actor of type :class:`MyBoundedUntypedActor` it will try to get a bounded
|
|
||||||
mailbox. If the actor has a different mailbox configured in deployment, either directly or via
|
|
||||||
a dispatcher with a specified mailbox type, then that will override this mapping.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The type of the queue in the mailbox created for an actor will be checked against the required type in the
|
|
||||||
interface and if the queue doesn't implement the required type then actor creation will fail.
|
|
||||||
|
|
||||||
|
|
||||||
Mailbox configuration precedence
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
The order of precedence for the mailbox type of an actor, where lower numbers override higher, is:
|
|
||||||
|
|
||||||
1. Mailbox type configured in the deployment of the actor
|
|
||||||
2. Mailbox type configured on the dispatcher of the actor
|
|
||||||
3. Mailbox type configured on the Props of the actor
|
|
||||||
4. Mailbox type configured via message queue requirement
|
|
||||||
|
|
||||||
|
|
||||||
Creating your own Mailbox type
|
Creating your own Mailbox type
|
||||||
------------------------------
|
==============================
|
||||||
|
|
||||||
An example is worth a thousand quacks:
|
An example is worth a thousand quacks:
|
||||||
|
|
||||||
|
|
@ -135,7 +181,7 @@ configuration, or the mailbox configuration.
|
||||||
|
|
||||||
|
|
||||||
Special Semantics of ``system.actorOf``
|
Special Semantics of ``system.actorOf``
|
||||||
---------------------------------------
|
=======================================
|
||||||
|
|
||||||
In order to make ``system.actorOf`` both synchronous and non-blocking while
|
In order to make ``system.actorOf`` both synchronous and non-blocking while
|
||||||
keeping the return type :class:`ActorRef` (and the semantics that the returned
|
keeping the return type :class:`ActorRef` (and the semantics that the returned
|
||||||
|
|
|
||||||
|
|
@ -348,3 +348,20 @@ message. Therefore the following is now safe::
|
||||||
context.stop(target)
|
context.stop(target)
|
||||||
context.unwatch(target)
|
context.unwatch(target)
|
||||||
|
|
||||||
|
Dispatcher and Mailbox Implementation Changes
|
||||||
|
=============================================
|
||||||
|
|
||||||
|
This point is only relevant if you have implemented a custom mailbox or
|
||||||
|
dispatcher and want to migrate that to Akka 2.2. The constructor signature of
|
||||||
|
:class:`MessageDispatcher` has changed, it now takes a
|
||||||
|
:class:`MessageDispatcherConfigurator` instead of
|
||||||
|
:class:`DispatcherPrerequisites`. Its :class:`createMailbox` method now
|
||||||
|
receives one more argument of type :class:`MailboxType`, which is the mailbox
|
||||||
|
type determined by the :class:`ActorRefProvider` for the actor based on its
|
||||||
|
deployment. The :class:`DispatcherPrerequisites` now include a
|
||||||
|
:class:`Mailboxes` instance which can be used for resolving mailbox references.
|
||||||
|
The constructor signatures of the built-in dispatcher implementation have been
|
||||||
|
adapted accordingly. The traits describing mailbox semantics have been
|
||||||
|
separated from the implementation traits.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -203,8 +203,7 @@ object DispatcherDocSpec {
|
||||||
import akka.dispatch.{
|
import akka.dispatch.{
|
||||||
Envelope,
|
Envelope,
|
||||||
MessageQueue,
|
MessageQueue,
|
||||||
QueueBasedMessageQueue,
|
UnboundedQueueBasedMessageQueue
|
||||||
UnboundedMessageQueueSemantics
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This constructor signature must exist, it will be called by Akka
|
// This constructor signature must exist, it will be called by Akka
|
||||||
|
|
@ -213,7 +212,7 @@ object DispatcherDocSpec {
|
||||||
// The create method is called to create the MessageQueue
|
// The create method is called to create the MessageQueue
|
||||||
final override def create(owner: Option[ActorRef],
|
final override def create(owner: Option[ActorRef],
|
||||||
system: Option[ActorSystem]): MessageQueue =
|
system: Option[ActorSystem]): MessageQueue =
|
||||||
new QueueBasedMessageQueue with UnboundedMessageQueueSemantics {
|
new UnboundedQueueBasedMessageQueue {
|
||||||
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
final val queue = new ConcurrentLinkedQueue[Envelope]()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ class RouterViaProgramDocSpec extends AkkaSpec with ImplicitSender {
|
||||||
val actor3 = system.actorOf(Props[ExampleActor1], "actor3")
|
val actor3 = system.actorOf(Props[ExampleActor1], "actor3")
|
||||||
val routees = Vector[String]("/user/actor1", "/user/actor2", "/user/actor3")
|
val routees = Vector[String]("/user/actor1", "/user/actor2", "/user/actor3")
|
||||||
val router = system.actorOf(
|
val router = system.actorOf(
|
||||||
Props().withRouter(RoundRobinRouter(routees = routees)))
|
Props.empty.withRouter(RoundRobinRouter(routees = routees)))
|
||||||
//#programmaticRoutingRouteePaths
|
//#programmaticRoutingRouteePaths
|
||||||
1 to 6 foreach { i ⇒ router ! Message1(i) }
|
1 to 6 foreach { i ⇒ router ! Message1(i) }
|
||||||
val received = receiveN(6, 5.seconds.dilated)
|
val received = receiveN(6, 5.seconds.dilated)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,96 @@
|
||||||
.. _mailboxes-scala:
|
.. _mailboxes-scala:
|
||||||
|
|
||||||
Mailboxes
|
Mailboxes
|
||||||
=========
|
#########
|
||||||
|
|
||||||
An Akka ``Mailbox`` holds the messages that are destined for an ``Actor``.
|
An Akka ``Mailbox`` holds the messages that are destined for an ``Actor``.
|
||||||
Normally each ``Actor`` has its own mailbox, but with for example a ``BalancingDispatcher``
|
Normally each ``Actor`` has its own mailbox, but with for example a ``BalancingDispatcher``
|
||||||
all actors with the same ``BalancingDispatcher`` will share a single instance.
|
all actors with the same ``BalancingDispatcher`` will share a single instance.
|
||||||
|
|
||||||
|
Mailbox Selection
|
||||||
|
=================
|
||||||
|
|
||||||
|
Requiring a Message Queue Type for an Actor
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
It is possible to require a certain type of message queue for a certain type of actor
|
||||||
|
by having that actor extend the parameterized trait :class:`RequiresMessageQueue`. Here is
|
||||||
|
an example:
|
||||||
|
|
||||||
|
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala#required-mailbox-class
|
||||||
|
|
||||||
|
The type parameter to the :class:`RequiresMessageQueue` trait needs to be mapped to a mailbox in
|
||||||
|
configuration like this:
|
||||||
|
|
||||||
|
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala
|
||||||
|
:include: bounded-mailbox-config,required-mailbox-config
|
||||||
|
|
||||||
|
Now every time you create an actor of type :class:`MyBoundedActor` it will try to get a bounded
|
||||||
|
mailbox. If the actor has a different mailbox configured in deployment, either directly or via
|
||||||
|
a dispatcher with a specified mailbox type, then that will override this mapping.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
The type of the queue in the mailbox created for an actor will be checked against the required type in the
|
||||||
|
trait and if the queue doesn't implement the required type then actor creation will fail.
|
||||||
|
|
||||||
|
Requiring a Message Queue Type for a Dispatcher
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
A dispatcher may also have a requirement for the mailbox type used by the
|
||||||
|
actors running on it. An example is the BalancingDispatcher which requires a
|
||||||
|
message queue that is thread-safe for multiple concurrent consumers. Such a
|
||||||
|
requirement is formulated within the dispatcher configuration section like
|
||||||
|
this::
|
||||||
|
|
||||||
|
my-dispatcher {
|
||||||
|
mailbox-requirement = org.example.MyInterface
|
||||||
|
}
|
||||||
|
|
||||||
|
The given requirement names a class or interface which will then be ensured to
|
||||||
|
be a supertype of the message queue’s implementation. In case of a
|
||||||
|
conflict—e.g. if the actor requires a mailbox type which does not satisfy this
|
||||||
|
requirement—then actor creation will fail.
|
||||||
|
|
||||||
|
How the Mailbox Type is Selected
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
When an actor is created, the :class:`ActorRefProvider` first determines the
|
||||||
|
dispatcher which will execute it. Then the mailbox is determined as follows:
|
||||||
|
|
||||||
|
1. If the actor’s deployment configuration section contains a ``mailbox`` key
|
||||||
|
then that names a configuration section describing the mailbox type to be
|
||||||
|
used.
|
||||||
|
|
||||||
|
2. If the actor’s ``Props`` contains a mailbox selection—i.e. ``withMailbox``
|
||||||
|
was called on it—then that names a configuration section describing the
|
||||||
|
mailbox type to be used.
|
||||||
|
|
||||||
|
3. If the dispatcher’s configuration section contains a ``mailbox-type`` key
|
||||||
|
the same section will be used to configure the mailbox type.
|
||||||
|
|
||||||
|
4. If the actor requires a mailbox type as described above then the mapping for
|
||||||
|
that requirement will be used to determine the mailbox type to be used; if
|
||||||
|
that fails then the dispatcher’s requirement—if any—will be tried instead.
|
||||||
|
|
||||||
|
5. If the dispatcher requires a mailbox type as described above then the
|
||||||
|
mapping for that requirement will be used to determine the mailbox type to
|
||||||
|
be used.
|
||||||
|
|
||||||
|
6. The default mailbox ``akka.actor.default-mailbox`` will be used.
|
||||||
|
|
||||||
|
Which Configuration is pass to the Mailbox Type
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
Each mailbox type is implemented by a class which extends :class:`MailboxType`
|
||||||
|
and takes two constructor arguments: a :class:`ActorSystem.Settings` object and
|
||||||
|
a :class:`Config` section. The latter is computed by obtaining the named
|
||||||
|
configuration section from the actor system’s configuration, overriding its
|
||||||
|
``id`` key with the configuration path of the mailbox type and adding a
|
||||||
|
fall-back to the default mailbox configuration section.
|
||||||
|
|
||||||
Builtin implementations
|
Builtin implementations
|
||||||
-----------------------
|
=======================
|
||||||
|
|
||||||
Akka comes shipped with a number of default mailbox implementations:
|
Akka comes shipped with a number of default mailbox implementations:
|
||||||
|
|
||||||
|
|
@ -47,7 +129,7 @@ Akka comes shipped with a number of default mailbox implementations:
|
||||||
* Durable mailboxes, see :ref:`durable-mailboxes-scala`.
|
* Durable mailboxes, see :ref:`durable-mailboxes-scala`.
|
||||||
|
|
||||||
Mailbox configuration examples
|
Mailbox configuration examples
|
||||||
------------------------------
|
==============================
|
||||||
|
|
||||||
How to create a PriorityMailbox:
|
How to create a PriorityMailbox:
|
||||||
|
|
||||||
|
|
@ -75,44 +157,8 @@ Or code like this:
|
||||||
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala#defining-mailbox-in-code
|
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala#defining-mailbox-in-code
|
||||||
|
|
||||||
|
|
||||||
Requiring a message queue type for an Actor
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
It is possible to require a certain type of message queue for a certain type of actor
|
|
||||||
by having that actor extend the parameterized trait :class:`RequiresMessageQueue`. Here is
|
|
||||||
an example:
|
|
||||||
|
|
||||||
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala#required-mailbox-class
|
|
||||||
|
|
||||||
The type parameter to the :class:`RequiresMessageQueue` trait needs to be mapped to a mailbox in
|
|
||||||
configuration like this:
|
|
||||||
|
|
||||||
.. includecode:: ../scala/code/docs/dispatcher/DispatcherDocSpec.scala
|
|
||||||
:include: bounded-mailbox-config,required-mailbox-config
|
|
||||||
|
|
||||||
Now every time you create an actor of type :class:`MyBoundedActor` it will try to get a bounded
|
|
||||||
mailbox. If the actor has a different mailbox configured in deployment, either directly or via
|
|
||||||
a dispatcher with a specified mailbox type, then that will override this mapping.
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The type of the queue in the mailbox created for an actor will be checked against the required type in the
|
|
||||||
trait and if the queue doesn't implement the required type then actor creation will fail.
|
|
||||||
|
|
||||||
|
|
||||||
Mailbox configuration precedence
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
The order of precedence for the mailbox type of an actor, where lower numbers override higher, is:
|
|
||||||
|
|
||||||
1. Mailbox type configured in the deployment of the actor
|
|
||||||
2. Mailbox type configured on the dispatcher of the actor
|
|
||||||
3. Mailbox type configured on the Props of the actor
|
|
||||||
4. Mailbox type configured via message queue requirement
|
|
||||||
|
|
||||||
|
|
||||||
Creating your own Mailbox type
|
Creating your own Mailbox type
|
||||||
------------------------------
|
==============================
|
||||||
|
|
||||||
An example is worth a thousand quacks:
|
An example is worth a thousand quacks:
|
||||||
|
|
||||||
|
|
@ -133,7 +179,7 @@ configuration, or the mailbox configuration.
|
||||||
|
|
||||||
|
|
||||||
Special Semantics of ``system.actorOf``
|
Special Semantics of ``system.actorOf``
|
||||||
---------------------------------------
|
=======================================
|
||||||
|
|
||||||
In order to make ``system.actorOf`` both synchronous and non-blocking while
|
In order to make ``system.actorOf`` both synchronous and non-blocking while
|
||||||
keeping the return type :class:`ActorRef` (and the semantics that the returned
|
keeping the return type :class:`ActorRef` (and the semantics that the returned
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,14 @@ private[akka] class RemoteActorRefProvider(
|
||||||
} else if (props.deploy.scope == LocalScope) {
|
} else if (props.deploy.scope == LocalScope) {
|
||||||
throw new IllegalArgumentException(s"configuration requested remote deployment for local-only Props at [$path]")
|
throw new IllegalArgumentException(s"configuration requested remote deployment for local-only Props at [$path]")
|
||||||
} else try {
|
} else try {
|
||||||
|
try {
|
||||||
|
// for consistency we check configuration of dispatcher and mailbox locally
|
||||||
|
val dispatcher = system.dispatchers.lookup(props.dispatcher)
|
||||||
|
system.mailboxes.getMailboxType(props, dispatcher.configurator.config)
|
||||||
|
} catch {
|
||||||
|
case NonFatal(e) ⇒ throw new IllegalArgumentException(
|
||||||
|
s"configuration problem while creating [$path] with dispatcher [${props.dispatcher}] and mailbox [${props.mailbox}]", e)
|
||||||
|
}
|
||||||
val localAddress = transport.localAddressForRemote(addr)
|
val localAddress = transport.localAddressForRemote(addr)
|
||||||
val rpath = (RootActorPath(addr) / "remote" / localAddress.protocol / localAddress.hostPort / path.elements).
|
val rpath = (RootActorPath(addr) / "remote" / localAddress.protocol / localAddress.hostPort / path.elements).
|
||||||
withUid(path.uid)
|
withUid(path.uid)
|
||||||
|
|
|
||||||
|
|
@ -126,18 +126,16 @@ object CallingThreadDispatcher {
|
||||||
*
|
*
|
||||||
* @since 1.1
|
* @since 1.1
|
||||||
*/
|
*/
|
||||||
class CallingThreadDispatcher(
|
class CallingThreadDispatcher(_configurator: MessageDispatcherConfigurator) extends MessageDispatcher(_configurator) {
|
||||||
_prerequisites: DispatcherPrerequisites,
|
|
||||||
val mailboxType: MailboxType,
|
|
||||||
val mailboxTypeConfigured: Boolean) extends MessageDispatcher(_prerequisites) {
|
|
||||||
import CallingThreadDispatcher._
|
import CallingThreadDispatcher._
|
||||||
|
import configurator.prerequisites._
|
||||||
|
|
||||||
val log = akka.event.Logging(prerequisites.eventStream, "CallingThreadDispatcher")
|
val log = akka.event.Logging(eventStream, "CallingThreadDispatcher")
|
||||||
|
|
||||||
override def id: String = Id
|
override def id: String = Id
|
||||||
|
|
||||||
protected[akka] override def createMailbox(actor: akka.actor.Cell) =
|
protected[akka] override def createMailbox(actor: akka.actor.Cell, mailboxType: MailboxType) =
|
||||||
new CallingThreadMailbox(actor, getMailboxType(actor, mailboxType, mailboxTypeConfigured))
|
new CallingThreadMailbox(actor, mailboxType)
|
||||||
|
|
||||||
protected[akka] override def shutdown() {}
|
protected[akka] override def shutdown() {}
|
||||||
|
|
||||||
|
|
@ -303,7 +301,7 @@ class CallingThreadDispatcher(
|
||||||
class CallingThreadDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
|
class CallingThreadDispatcherConfigurator(config: Config, prerequisites: DispatcherPrerequisites)
|
||||||
extends MessageDispatcherConfigurator(config, prerequisites) {
|
extends MessageDispatcherConfigurator(config, prerequisites) {
|
||||||
|
|
||||||
private val instance = new CallingThreadDispatcher(prerequisites, mailboxType(), mailBoxTypeConfigured)
|
private val instance = new CallingThreadDispatcher(this)
|
||||||
|
|
||||||
override def dispatcher(): MessageDispatcher = instance
|
override def dispatcher(): MessageDispatcher = instance
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,15 @@ import akka.pattern.ask
|
||||||
*/
|
*/
|
||||||
class TestActorRef[T <: Actor](
|
class TestActorRef[T <: Actor](
|
||||||
_system: ActorSystem,
|
_system: ActorSystem,
|
||||||
_prerequisites: DispatcherPrerequisites,
|
|
||||||
_props: Props,
|
_props: Props,
|
||||||
_supervisor: ActorRef,
|
_supervisor: ActorRef,
|
||||||
name: String)
|
name: String)
|
||||||
extends {
|
extends {
|
||||||
|
val props =
|
||||||
|
_props.withDispatcher(
|
||||||
|
if (_props.deploy.dispatcher == Deploy.NoDispatcherGiven) CallingThreadDispatcher.Id
|
||||||
|
else _props.dispatcher)
|
||||||
|
val dispatcher = _system.dispatchers.lookup(props.dispatcher)
|
||||||
private val disregard = _supervisor match {
|
private val disregard = _supervisor match {
|
||||||
case l: LocalActorRef ⇒ l.underlying.reserveChild(name)
|
case l: LocalActorRef ⇒ l.underlying.reserveChild(name)
|
||||||
case r: RepointableActorRef ⇒ r.underlying match {
|
case r: RepointableActorRef ⇒ r.underlying match {
|
||||||
|
|
@ -36,9 +40,9 @@ class TestActorRef[T <: Actor](
|
||||||
}
|
}
|
||||||
} with LocalActorRef(
|
} with LocalActorRef(
|
||||||
_system.asInstanceOf[ActorSystemImpl],
|
_system.asInstanceOf[ActorSystemImpl],
|
||||||
_props.withDispatcher(
|
props,
|
||||||
if (_props.dispatcher == Dispatchers.DefaultDispatcherId) CallingThreadDispatcher.Id
|
dispatcher,
|
||||||
else _props.dispatcher),
|
_system.mailboxes.getMailboxType(props, dispatcher.configurator.config),
|
||||||
_supervisor.asInstanceOf[InternalActorRef],
|
_supervisor.asInstanceOf[InternalActorRef],
|
||||||
_supervisor.path / name) {
|
_supervisor.path / name) {
|
||||||
|
|
||||||
|
|
@ -47,8 +51,9 @@ class TestActorRef[T <: Actor](
|
||||||
|
|
||||||
import TestActorRef.InternalGetActor
|
import TestActorRef.InternalGetActor
|
||||||
|
|
||||||
protected override def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props, supervisor: InternalActorRef): ActorCell =
|
protected override def newActorCell(system: ActorSystemImpl, ref: InternalActorRef, props: Props,
|
||||||
new ActorCell(system, ref, props, supervisor) {
|
dispatcher: MessageDispatcher, supervisor: InternalActorRef): ActorCell =
|
||||||
|
new ActorCell(system, ref, props, dispatcher, supervisor) {
|
||||||
override def autoReceiveMessage(msg: Envelope) {
|
override def autoReceiveMessage(msg: Envelope) {
|
||||||
msg.message match {
|
msg.message match {
|
||||||
case InternalGetActor ⇒ sender ! actor
|
case InternalGetActor ⇒ sender ! actor
|
||||||
|
|
@ -132,7 +137,8 @@ object TestActorRef {
|
||||||
apply[T](props, system.asInstanceOf[ActorSystemImpl].guardian, name)
|
apply[T](props, system.asInstanceOf[ActorSystemImpl].guardian, name)
|
||||||
|
|
||||||
def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T] = {
|
def apply[T <: Actor](props: Props, supervisor: ActorRef, name: String)(implicit system: ActorSystem): TestActorRef[T] = {
|
||||||
new TestActorRef(system.asInstanceOf[ActorSystemImpl], system.dispatchers.prerequisites, props, supervisor.asInstanceOf[InternalActorRef], name)
|
val sysImpl = system.asInstanceOf[ActorSystemImpl]
|
||||||
|
new TestActorRef(sysImpl, props, supervisor.asInstanceOf[InternalActorRef], name)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply[T <: Actor](implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T] = apply[T](randomName)
|
def apply[T <: Actor](implicit t: ClassTag[T], system: ActorSystem): TestActorRef[T] = apply[T](randomName)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import akka.actor._
|
||||||
import scala.concurrent.duration.Duration
|
import scala.concurrent.duration.Duration
|
||||||
import akka.dispatch.DispatcherPrerequisites
|
import akka.dispatch.DispatcherPrerequisites
|
||||||
import scala.concurrent.duration.FiniteDuration
|
import scala.concurrent.duration.FiniteDuration
|
||||||
|
import akka.dispatch.MessageDispatcher
|
||||||
|
import akka.dispatch.MailboxType
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a specialised form of the TestActorRef with support for querying and
|
* This is a specialised form of the TestActorRef with support for querying and
|
||||||
|
|
@ -35,11 +37,10 @@ import scala.concurrent.duration.FiniteDuration
|
||||||
*/
|
*/
|
||||||
class TestFSMRef[S, D, T <: Actor](
|
class TestFSMRef[S, D, T <: Actor](
|
||||||
system: ActorSystem,
|
system: ActorSystem,
|
||||||
_prerequisites: DispatcherPrerequisites,
|
|
||||||
props: Props,
|
props: Props,
|
||||||
supervisor: ActorRef,
|
supervisor: ActorRef,
|
||||||
name: String)(implicit ev: T <:< FSM[S, D])
|
name: String)(implicit ev: T <:< FSM[S, D])
|
||||||
extends TestActorRef(system, _prerequisites, props, supervisor, name) {
|
extends TestActorRef(system, props, supervisor, name) {
|
||||||
|
|
||||||
private def fsm: T = underlyingActor
|
private def fsm: T = underlyingActor
|
||||||
|
|
||||||
|
|
@ -93,11 +94,11 @@ object TestFSMRef {
|
||||||
|
|
||||||
def apply[S, D, T <: Actor](factory: ⇒ T)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
def apply[S, D, T <: Actor](factory: ⇒ T)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
||||||
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
||||||
new TestFSMRef(impl, system.dispatchers.prerequisites, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], TestActorRef.randomName)
|
new TestFSMRef(impl, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], TestActorRef.randomName)
|
||||||
}
|
}
|
||||||
|
|
||||||
def apply[S, D, T <: Actor](factory: ⇒ T, name: String)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
def apply[S, D, T <: Actor](factory: ⇒ T, name: String)(implicit ev: T <:< FSM[S, D], system: ActorSystem): TestFSMRef[S, D, T] = {
|
||||||
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
val impl = system.asInstanceOf[ActorSystemImpl] //TODO ticket #1559
|
||||||
new TestFSMRef(impl, system.dispatchers.prerequisites, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], name)
|
new TestFSMRef(impl, Props(creator = () ⇒ factory), impl.guardian.asInstanceOf[InternalActorRef], name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue