AKKA-源码-mailbox构建的主要逻辑

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中执行。

你可能感兴趣的:(BigData)