接着上一篇的 tree路径匹配抽象(1),我们开始看如何对tree进行索引,akka的路径匹配包含了远程节点的匹配,这样就得引入多个通信机制(akka采用消息),为了简化,我们先假设只在一个本地tree中进行索引:
object ActorSelection {//...省略 /** * Construct an ActorSelection from the given string representing a path * relative to the given target. This operation has to create all the * matching magic, so it is preferable to cache its result if the * intention is to send messages frequently. */ def apply(anchorRef: ActorRef, path: String): ActorSelection = apply(anchorRef, path.split("/+"))//ActorRef可以被当作当前路径的引用 /** * Construct an ActorSelection from the given string representing a path * relative to the given target. This operation has to create all the * matching magic, so it is preferable to cache its result if the * intention is to send messages frequently. */ def apply(anchorRef: ActorRef, elements: Iterable[String]): ActorSelection = { val compiled: immutable.IndexedSeq[SelectionPathElement] = elements.collect({ case x if !x.isEmpty ? if ((x.indexOf(‘?‘) != -1) || (x.indexOf(‘*‘) != -1)) SelectChildPattern(x) else if (x == "..") SelectParent else SelectChildName(x) })(scala.collection.breakOut)//直接转换为immutable.IndexedSeq[SelectionPathElement] new ActorSelection with ScalaActorSelection { override val anchor = anchorRef override val path = compiled } }//------上面是将字符串转化为SelectionPathElement,下面则是根据SelectionPathElement进行索引tree值 /** * INTERNAL API * The receive logic for ActorSelectionMessage. The idea is to recursively descend as far as possible * with local refs and hand over to that “foreign” child when we encounter it. */ private[akka] def deliverSelection(anchor: InternalActorRef, sender: ActorRef, sel: ActorSelectionMessage): Unit = if (sel.elements.isEmpty) anchor.tell(sel.msg, sender)//自己 else { val iter = sel.elements.iterator @tailrec def rec(ref: InternalActorRef): Unit = { ref match { case refWithCell: ActorRefWithCell ? def emptyRef = new EmptyLocalActorRef(refWithCell.provider, anchor.path / sel.elements.map(_.toString), refWithCell.underlying.system.eventStream) iter.next() match { case SelectParent ? val parent = ref.getParent if (iter.isEmpty) parent.tell(sel.msg, sender) else rec(parent) case SelectChildName(name) ? val child = refWithCell.getSingleChild(name) if (child == Nobody) { // don‘t send to emptyRef after wildcard fan-out if (!sel.wildcardFanOut) emptyRef.tell(sel, sender) } else if (iter.isEmpty) child.tell(sel.msg, sender) else rec(child) case p: SelectChildPattern ? // fan-out when there is a wildcard val chldr = refWithCell.children if (iter.isEmpty) { // leaf val matchingChildren = chldr.filter(c ? p.pattern.matcher(c.path.name).matches) if (matchingChildren.isEmpty && !sel.wildcardFanOut) emptyRef.tell(sel, sender) else matchingChildren.foreach(_.tell(sel.msg, sender)) } else { val matchingChildren = chldr.filter(c ? p.pattern.matcher(c.path.name).matches) // don‘t send to emptyRef after wildcard fan-out if (matchingChildren.isEmpty && !sel.wildcardFanOut) emptyRef.tell(sel, sender) else { val m = sel.copy(elements = iter.toVector, wildcardFanOut = sel.wildcardFanOut || matchingChildren.size > 1) matchingChildren.foreach(c ? deliverSelection(c.asInstanceOf[InternalActorRef], sender, m)) } } } //case _ ? // foreign ref, continue by sending ActorSelectionMessage to it with remaining elements //ref.tell(sel.copy(elements = iter.toVector), sender) } } rec(anchor) } }
既然path允许正则索引,那么path最好有个命名规则,akka的命名规则为:
/** * This Regular Expression is used to validate a path element (Actor Name). * Since Actors form a tree, it is addressable using an URL, therefore an Actor Name has to conform to: * http://www.ietf.org/rfc/rfc2396.txt */ val ElementRegex = """(?:[-\w:@&=+,.!~*‘_;]|%\p{XDigit}{2})(?:[-\w:@&=+,.!~*‘$_;]|%\p{XDigit}{2})*""".r
时间: 2024-10-16 13:49:11