ActorSystem
中的一个变量mailboxes
,定义如下:
val mailboxes: Mailboxes = new Mailboxes(settings, eventStream, dynamicAccess, deadLetters)
MailBoxes
的具体定义如下:
private[akka] class Mailboxes(
val settings: ActorSystem.Settings,
val eventStream: EventStream,
dynamicAccess: DynamicAccess,
deadLetters: ActorRef)
其中各个参数的说明如下:
settings
——> 就是ActorSystem
中的settings
,也就是akka
世界的所有外部配置参数
eventStream
——> 可以简单理解,就是一个集中管理日志打印的地方
dynamicAccess
——> 通过反射机制来生成一个指定类的实例的地方
deadLetters
——> 死信发送的地方
Mailboxes
有一个变量mailboxBindings
用来存储配置文件中配置的mailbox
的类名与其对应的配置参数名的对应关系,在默认配置文件中的配置如下:
requirements {
"akka.dispatch.UnboundedMessageQueueSemantics" =
akka.actor.mailbox.unbounded-queue-based
"akka.dispatch.BoundedMessageQueueSemantics" =
akka.actor.mailbox.bounded-queue-based
"akka.dispatch.DequeBasedMessageQueueSemantics" =
akka.actor.mailbox.unbounded-deque-based
"akka.dispatch.UnboundedDequeBasedMessageQueueSemantics" =
akka.actor.mailbox.unbounded-deque-based
"akka.dispatch.BoundedDequeBasedMessageQueueSemantics" =
akka.actor.mailbox.bounded-deque-based
"akka.dispatch.MultipleConsumerSemantics" =
akka.actor.mailbox.unbounded-queue-based
}
经过Mailboxes中的这段解析代码解析:
private val mailboxBindings: Map[Class[_ <: Any], String] = {
import scala.collection.JavaConverters._
settings.config.getConfig("akka.actor.mailbox.requirements").root.unwrapped.asScala
.toMap.foldLeft(Map.empty[Class[_ <: Any], String]) {
case (m, (k, v)) ⇒
dynamicAccess.getClassFor[Any](k).map {
case x ⇒ m.updated(x, v.toString)
}.recover {
case e ⇒
throw new ConfigurationException(s"Type [${k}] specified as akka.actor.mailbox.requirement " +
s"[${v}] in config can't be loaded due to [${e.getMessage}]", e)
}.get
}
}
得到的mailboxBindings
如下的map
UnboundedMessageQueueSemantics.class => “akka.actor.mailbox.unbounded-queue-based”
BoundedMessageQueueSemantics.class => "akka.actor.mailbox.bounded-queue-based”
DequeBasedMessageQueueSemantics.class => "akka.actor.mailbox.unbounded-deque-based”
UnboundedDequeBasedMessageQueueSemantics.class => "akka.actor.mailbox.unbounded-deque-based”
BoundedDequeBasedMessageQueueSemantics.class => "akka.actor.mailbox.bounded-deque-based”
MultipleConsumerSemantics.class => "akka.actor.mailbox.unbounded-queue-based"
根据给定的class
来找对应的mailbox
的配置的路径如下:
//总入口是调用lookupByQueueType方法
def lookupByQueueType(queueType: Class[_ <: Any]): MailboxType = lookup(lookupId(queueType))
//具体实现是先调用lookupId方法
private def lookupId(queueType: Class[_]): String =
mailboxBindings.get(queueType) match {
case None ⇒ throw new ConfigurationException(s"Mailbox Mapping for [${queueType}] not configured")
case Some(s) ⇒ s
}
从mailboxBindings
中获取对应的配置参数的路径,如
akka.actor.mailbox.unbounded-queue-based
再调用lookup
方法,进一步查找
def lookup(id: String): MailboxType = lookupConfigurator(id)
lookup
方法内,又是调用lookupConfigurator
方法,根据传入的id
,如akka.actor.mailbox.unbounded-queue-based
来进一步查找。akka.actor.mailbox.unbounded-queue-based
的配置一般如下
unbounded-queue-based {
# FQCN of the MailboxType, The Class of the FQCN must have a public
mailbox-type = "akka.dispatch.UnboundedMailbox"
}
主要就是一个mailbox-type
参数,用来指定mailbox
的全限定类名
lookupConfigurator
方法的实现逻辑也比较容易理解
private def lookupConfigurator(id: String): MailboxType = {
//先从mailboxTypeConfigurators中根据FQCN来查找,有就直接返回,没有则进一步判断
mailboxTypeConfigurators.get(id) match {
case null ⇒
// It doesn't matter if we create a mailbox type configurator that isn't used due to concurrent lookup. val newConfigurator = id match {
// TODO RK remove these two for Akka 2.3 case "unbounded" ⇒ UnboundedMailbox()
case "bounded" ⇒ new BoundedMailbox(settings, config(id))
case _ ⇒
if (!settings.config.hasPath(id)) throw new ConfigurationException(s"Mailbox Type [${id}] not configured")
val conf = config(id)
//从conf中获取相应的mailbox-type配置的值,正常配置就是一个类名
conf.getString("mailbox-type") match {
case "" ⇒ throw new ConfigurationException(s"The setting mailbox-type, defined in [$id] is empty")
case fqcn ⇒
val args = List(classOf[ActorSystem.Settings] -> settings, classOf[Config] -> conf)
//进行实例化,然后返回
dynamicAccess.createInstanceFor[MailboxType](fqcn, args).recover({
case exception ⇒
throw new IllegalArgumentException(
(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"),
exception)
}).get
}
}
mailboxTypeConfigurators.putIfAbsent(id, newConfigurator) match {
case null ⇒ newConfigurator
case existing ⇒ existing
}
case existing ⇒ existing
}
}
在创建一个actor
时,要确定其对应的MailboxType
,具体的实现逻辑如下:
/**
* 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)
//在default-dispatcher中mailbox-requirement的默认值是空字符串,也就是默认没有mailbox的要求,所以返回的就是classOf[MessageQueue]
val mailboxRequirement: Class[_] = getMailboxRequirement(dispatcherConfig)
//默认值下,该值为false
val hasMailboxRequirement: Boolean = mailboxRequirement != classOf[MessageQueue]
//默认该值也为false
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 }
//校验创建的MailboxType与actor所设置的MailboxType是否一致
def verifyRequirements(mailboxType: MailboxType): MailboxType = {
// 获取Mailbox是否有设置指定的MessageQueue,如果没有设置,则返回classOf[MessageQueue],否则就返回设定的类
lazy val mqType: Class[_] = getProducedMessageQueueType(mailboxType)
//如果设置了mailbox-requirement,则需要判断与实际用的mailbox是否一致
if (hasMailboxRequirement && !mailboxRequirement.isAssignableFrom(mqType))
throw new IllegalArgumentException(s"produced message queue type [$mqType] does not fulfill requirement for dispatcher [${id}]")
//判断acotr要求设置的mailbox,与实际使用的mqType是否一致
if (hasRequiredType(actorClass) && !actorRequirement.isAssignableFrom(mqType))
throw new IllegalArgumentException(s"produced message queue type [$mqType] does not fulfill requirement for actor class [$actorClass]")
//上述都校验通过,则说明mailboxType设置正确,返回mailboxType
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)) {
// 如果actor设置了MailboxType,则进如该逻辑
try
verifyRequirements(lookupByQueueType(getRequiredType(actorClass)))
catch {
case NonFatal(thr) if (hasMailboxRequirement) ⇒ verifyRequirements(lookupByQueueType(mailboxRequirement))
}
} else if (hasMailboxRequirement) {
// 如果在default-dispatcher中设置了mailbox-requirement,则进入该逻辑
verifyRequirements(lookupByQueueType(mailboxRequirement))
} else {
//默认进入该逻辑,也就是采用UnboundedMailbox
verifyRequirements(lookup(DefaultMailboxId))
}
}
上面主要是说在Mailboxes
中,如果根据配置获取配置的MailboxType
,那MailboxType的定义如何:
trait MailboxType {
def create(owner: Option[ActorRef], system: Option[ActorSystem]): MessageQueue
}
可以看出MailboxType
只有一个方法,那就是create
,也就是说MailboxType
就是用来创建一个MessageQueue
。
先看下Mailbox
的定义
private[akka] abstract class Mailbox(val messageQueue: MessageQueue)
extends ForkJoinTask[Unit] with SystemMessageQueue with Runnable {}
从这个定义可以看出,Mailbox
只需要一个入参,就是一个MessageQueue
,既然是邮箱,那么这个messageQueue
的属性,应该就是用来存储相应的消息的队列,另外我们发现Mailbox
继承了ForkJoinTask
,同时实现了Runnable
接口,说明Mailbox
是可以放到一个线程中执行的,即可以是传统的线程池中执行,也可以放到ForkJoinPool
中执行。