akka的每个路径上可能会存在多个值,而每个值都需要能被索引,akka用uid:Int
保证对单个节点上的值进行再区分,路径和uid之间使用#
来进行区分,如果每个节点只会存在一个值的话,关于uid
部分的代码就可以删掉了,下文中,我会将相应的代码注释掉。
还有一个值得注意的是,akka将路径序列化和toString
进行了分离
sealed trait ActorPath extends Comparable[ActorPath] with Serializable { /** * The Address under which this path can be reached; walks up the tree to * the RootActorPath. */ def address: Address//akka remote actorSelection时需要知道远程服务器地址 /** * The name of the actor that this path refers to. */ def name: String//tree节点上的名称 /** * The path for the parent actor. */ def parent: ActorPath//父节点路径 /** * Create a new child actor path. */ def /(child: String): ActorPath//路径dsl /** * Java API: Create a new child actor path. */ //def child(child: String): ActorPath = /(child) /** * Recursively create a descendant’s path by appending all child names. */ def /(child: Iterable[String]): ActorPath = (this /: child)((path, elem) ⇒ if (elem.isEmpty) path else path / elem)//路径dsl /** * Sequence of names for this path from root to this. Performance implication: has to allocate a list. */ def elements: immutable.Iterable[String]//当前路径 /** * Walk up the tree to obtain and return the RootActorPath. */ def root: RootActorPath//根节点的抽象// tree唯一标示+节点 /** * String representation of the path elements, excluding the address * information. The elements are separated with "/" and starts with "/", * e.g. "/user/a/b". */ def toStringWithoutAddress: String = elements.mkString("/", "/", "") /** * Generate String representation, replacing the Address in the RootActor * Path with the given one unless this path’s address includes host and port * information. */ //def toStringWithAddress(address: Address): String /** * Generate full String representation including the * uid for the actor cell instance as URI fragment. * This representation should be used as serialized * representation instead of `toString`. */ def toSerializationFormat: String /** * Generate full String representation including the uid for the actor cell * instance as URI fragment, replacing the Address in the RootActor Path * with the given one unless this path’s address includes host and port * information. This representation should be used as serialized * representation instead of `toStringWithAddress`. */ //def toSerializationFormatWithAddress(address: Address): String /** * INTERNAL API * Unique identifier of the actor. Used for distinguishing * different incarnations of actors with same path (name elements). */ //private[akka] def uid: Int /** * INTERNAL API * Creates a new ActorPath with same elements but with the specified `uid`. */ //private[akka] def withUid(uid: Int): ActorPath//当uid相同是,what will happen depends on you! } /** * Root of the hierarchy of ActorPaths. There is exactly root per ActorSystem * and node (for remote-enabled or clustered systems). */ @SerialVersionUID(1L) final case class RootActorPath(address: Address, name: String = "/") extends ActorPath { override def parent: ActorPath = this//根节点的父节点为自己,(这个地方为null或者将parent的返回值变为Option[ActorPath]是否更好些) override def root: RootActorPath = this /*override def /(child: String): ActorPath = { val (childName, uid) = ActorCell.splitNameAndUid(child) new ChildActorPath(this, childName, uid) }*///针对uid做了特殊处理 override def elements: immutable.Iterable[String] = ActorPath.emptyActorPath override val toString: String = address + name override val toSerializationFormat: String = toString override def toStringWithAddress(addr: Address): String = if (address.host.isDefined) address + name else addr + name override def toSerializationFormatWithAddress(addr: Address): String = toStringWithAddress(addr) override def compareTo(other: ActorPath): Int = other match { case r: RootActorPath ⇒ toString compareTo r.toString // FIXME make this cheaper by comparing address and name in isolation case c: ChildActorPath ⇒ 1 } /** * INTERNAL API */ //private[akka] def uid: Int = ActorCell.undefinedUid /** * INTERNAL API */ /*override private[akka] def withUid(uid: Int): ActorPath = if (uid == ActorCell.undefinedUid) this else throw new IllegalStateException(s"RootActorPath must have undefinedUid, [$uid != ${ActorCell.undefinedUid}")*/ } @SerialVersionUID(1L) final class ChildActorPath private[akka] (val parent: ActorPath, val name: String, override private[akka] val uid: Int) extends ActorPath {//uid可以被干掉 if (name.indexOf('/') != -1) throw new IllegalArgumentException("/ is a path separator and is not legal in ActorPath names: [%s]" format name) //if (name.indexOf('#') != -1) throw new IllegalArgumentException("# is a fragment separator and is not legal in ActorPath names: [%s]" format name) def this(parent: ActorPath, name: String) = this(parent, name, ActorCell.undefinedUid) override def address: Address = root.address override def /(child: String): ActorPath = { val (childName, uid) = ActorCell.splitNameAndUid(child)//干掉 new ChildActorPath(this, childName, uid) } override def elements: immutable.Iterable[String] = { @tailrec def rec(p: ActorPath, acc: List[String]): immutable.Iterable[String] = p match { case r: RootActorPath ⇒ acc case _ ⇒ rec(p.parent, p.name :: acc) } rec(this, Nil) } override def root: RootActorPath = { @tailrec def rec(p: ActorPath): RootActorPath = p match { case r: RootActorPath ⇒ r case _ ⇒ rec(p.parent) } rec(this) } /** * INTERNAL API */ /*override private[akka] def withUid(uid: Int): ActorPath = if (uid == this.uid) this else new ChildActorPath(parent, name, uid)*/ override def toString: String = { val length = toStringLength buildToString(new JStringBuilder(length), length, 0, _.toString).toString } override def toSerializationFormat: String = { val length = toStringLength val sb = buildToString(new JStringBuilder(length + 12), length, 0, _.toString) appendUidFragment(sb).toString } private def toStringLength: Int = toStringOffset + name.length private val toStringOffset: Int = parent match { case r: RootActorPath ⇒ r.address.toString.length + r.name.length case c: ChildActorPath ⇒ c.toStringLength + 1 }//获取路径字符串长度 override def toStringWithAddress(addr: Address): String = { val diff = addressStringLengthDiff(addr) val length = toStringLength + diff buildToString(new JStringBuilder(length), length, diff, _.toStringWithAddress(addr)).toString } override def toSerializationFormatWithAddress(addr: Address): String = { val diff = addressStringLengthDiff(addr) val length = toStringLength + diff val sb = buildToString(new JStringBuilder(length + 12), length, diff, _.toStringWithAddress(addr)) appendUidFragment(sb).toString } private def addressStringLengthDiff(addr: Address): Int = { val r = root if (r.address.host.isDefined) 0 else (addr.toString.length - r.address.toString.length) } /** * Optimized toString construction. Used by `toString`, `toSerializationFormat`, * and friends `WithAddress` * @param sb builder that will be modified (and same instance is returned) * @param length pre-calculated length of the to be constructed String, not * necessarily same as sb.capacity because more things may be appended to the * sb afterwards * @param diff difference in offset for each child element, due to different address * @param rootString function to construct the root element string */ private def buildToString(sb: JStringBuilder, length: Int, diff: Int, rootString: RootActorPath ⇒ String): JStringBuilder = { @tailrec def rec(p: ActorPath): JStringBuilder = p match { case r: RootActorPath ⇒ val rootStr = rootString(r) sb.replace(0, rootStr.length, rootStr) case c: ChildActorPath ⇒ val start = c.toStringOffset + diff val end = start + c.name.length sb.replace(start, end, c.name) if (c ne this) sb.replace(end, end + 1, "/") rec(c.parent) } sb.setLength(length) rec(this) } private def appendUidFragment(sb: JStringBuilder): JStringBuilder = { if (uid == ActorCell.undefinedUid) sb else sb.append("#").append(uid) } override def equals(other: Any): Boolean = { @tailrec def rec(left: ActorPath, right: ActorPath): Boolean = if (left eq right) true else if (left.isInstanceOf[RootActorPath]) left equals right else if (right.isInstanceOf[RootActorPath]) right equals left else left.name == right.name && rec(left.parent, right.parent) other match { case p: ActorPath ⇒ rec(this, p) case _ ⇒ false } } // TODO RK investigate Phil’s hash from scala.collection.mutable.HashTable.improve override def hashCode: Int = { import akka.routing.MurmurHash._ @tailrec def rec(p: ActorPath, h: Int, c: Int, k: Int): Int = p match { case r: RootActorPath ⇒ extendHash(h, r.##, c, k) case _ ⇒ rec(p.parent, extendHash(h, stringHash(name), c, k), nextMagicA(c), nextMagicB(k)) } finalizeHash(rec(this, startHash(42), startMagicA, startMagicB)) } override def compareTo(other: ActorPath): Int = { @tailrec def rec(left: ActorPath, right: ActorPath): Int = if (left eq right) 0 else if (left.isInstanceOf[RootActorPath]) left compareTo right else if (right.isInstanceOf[RootActorPath]) -(right compareTo left) else { val x = left.name compareTo right.name if (x == 0) rec(left.parent, right.parent) else x } rec(this, other) } } ``` 路径抽象完了,接下了便是抽象`索引字符串`了,akka对此抽象为`ActorSelection` ```scala /** * INTERNAL API */ @SerialVersionUID(1L) private[akka] sealed trait SelectionPathElement /** * INTERNAL API */ @SerialVersionUID(2L) private[akka] case class SelectChildName(name: String) extends SelectionPathElement { override def toString: String = name } /** * INTERNAL API */ @SerialVersionUID(2L) private[akka] case class SelectChildPattern(patternStr: String) extends SelectionPathElement { val pattern: Pattern = Helpers.makePattern(patternStr) override def toString: String = patternStr }//正则索引子节点 /** * INTERNAL API */ @SerialVersionUID(2L) private[akka] case object SelectParent extends SelectionPathElement { override def toString: String = ".." }//akka没有正则索引祖先节点,那种jquery api中的parents函数在akka中的使用场景现在还未遇到过。 /** * When [[ActorSelection#resolveOne]] can't identify the actor the * `Future` is completed with this failure. */ @SerialVersionUID(1L) case class ActorNotFound(selection: ActorSelection) extends RuntimeException("Actor not found for: " + selection)