上一篇博客中,我们研究了remote模式下如何发消息给远程actor,其实无论如何,最终都是通过RemoteActorRef来发送消息的。另外官网也明确说明了,ActorRef是可以忽略网络位置的,这其实有两点含义:1.ActorRef可以序列化后跨网络传输;2.ActorRef反序列化后在本地可以正常识别是本地还是远程。那么实现位置透明就有两个关键点:1.ActorRef的序列化过程;2.ActorRef的识别。下面我们来逐一研究这两个关键点。

  在local模式下,是通过InternalActorRef发送消息的;remote是通过RemoteActorRef发送消息的,那这两者有什么区别呢?

/**
* INTERNAL API
* Remote ActorRef that is used when referencing the Actor on a different node than its "home" node.
* This reference is network-aware (remembers its origin) and immutable.
*/
private[akka] class RemoteActorRef private[akka] (
remote: RemoteTransport,
val localAddressToUse: Address,
val path: ActorPath,
val getParent: InternalActorRef,
props: Option[Props],
deploy: Option[Deploy])
extends InternalActorRef with RemoteRef

  从源码可以看出RemoteActorRef继承了InternalActorRef,还扩展了RemoteRef特质。

private[akka] trait RemoteRef extends ActorRefScope {
final def isLocal = false
}

  RemoteRef比较简单,就是把isLocal定义成了false。这样看来RemoteRef和InternalActorRef差别并不是特别大。ActorRef在本地传输时,默认是不需要序列化的,那该如何切入序列化过程呢?我们首先来看序列化的过程。

  还记得之前的文章吗?在remote模式下,是通过EndpointWriter.writeSend发送消息的。

 def writeSend(s: Send): Boolean = try {
handle match {
case Some(h) ⇒
if (provider.remoteSettings.LogSend && log.isDebugEnabled) {
def msgLog = s"RemoteMessage: [${s.message}] to [${s.recipient}]<+[${s.recipient.path}] from [${s.senderOption.getOrElse(extendedSystem.deadLetters)}]"
log.debug("sending message {}", msgLog)
} val pdu = codec.constructMessage(
s.recipient.localAddressToUse,
s.recipient,
serializeMessage(s.message),
s.senderOption,
seqOption = s.seqOpt,
ackOption = lastAck) val pduSize = pdu.size
remoteMetrics.logPayloadBytes(s.message, pduSize) if (pduSize > transport.maximumPayloadBytes) {
val reason = new OversizedPayloadException(s"Discarding oversized payload sent to ${s.recipient}: max allowed size ${transport.maximumPayloadBytes} bytes, actual size of encoded ${s.message.getClass} was ${pdu.size} bytes.")
log.error(reason, "Transient association error (association remains live)")
true
} else {
val ok = h.write(pdu)
if (ok) {
ackDeadline = newAckDeadline
lastAck = None
}
ok
} case None ⇒
throw new EndpointException("Internal error: Endpoint is in state Writing, but no association handle is present.")
}
} catch {
case e: NotSerializableException ⇒
log.error(e, "Serializer not defined for message type [{}]. Transient association error (association remains live)", s.message.getClass)
true
case e: IllegalArgumentException ⇒
log.error(e, "Serializer not defined for message type [{}]. Transient association error (association remains live)", s.message.getClass)
true
case e: MessageSerializer.SerializationException ⇒
log.error(e, "{} Transient association error (association remains live)", e.getMessage)
true
case e: EndpointException ⇒
publishAndThrow(e, Logging.ErrorLevel)
case NonFatal(e) ⇒
publishAndThrow(new EndpointException("Failed to write message to the transport", e), Logging.ErrorLevel)
}

  可以看到codec.constructMessage函数中调用了serializeMessage函数对待发送的消息进行了序列化,那如果用户发送的消息中包含ActorRef,就一定会在这个函数处理。哪些消息会包含ActorRef呢?还记得ActorIdentity吗,里面就包含ActorRef。当然了,如果用户自定义的消息包含ActorRef,也一定会被序列化。

private def serializeMessage(msg: Any): SerializedMessage = handle match {
case Some(h) ⇒
Serialization.currentTransportInformation.withValue(Serialization.Information(h.localAddress, extendedSystem)) {
MessageSerializer.serialize(extendedSystem, msg.asInstanceOf[AnyRef])
}
case None ⇒
throw new EndpointException("Internal error: No handle was present during serialization of outbound message.")
}

  很明显EndpointWriter.serializeMessage有调用了MessageSerializer.serialize进行序列化。

  /**
* Serialization information needed for serializing local actor refs,
* or if serializer library e.g. custom serializer/deserializer in Jackson need
* access to the current `ActorSystem`.
*/
final case class Information(address: Address, system: ActorSystem)

  Serialization.Information这个case class比较简单,官网说的也很清楚,这里不再详细分析。简单点说,它就是给序列化过程提供了必需的基础变量,例如地址和当前的ActorSystem。

 /**
* INTERNAL API: This holds a reference to the current transport serialization information used for
* serializing local actor refs, or if serializer library e.g. custom serializer/deserializer in
* Jackson need access to the current `ActorSystem`.
*/
@InternalApi private[akka] val currentTransportInformation = new DynamicVariable[Information](null)

  Serialization.currentTransportInformation又是什么呢?

/** `DynamicVariables` provide a binding mechanism where the current
* value is found through dynamic scope, but where access to the
* variable itself is resolved through static scope.
*
* The current value can be retrieved with the value method. New values
* should be pushed using the `withValue` method. Values pushed via
* `withValue` only stay valid while the `withValue`'s second argument, a
* parameterless closure, executes. When the second argument finishes,
* the variable reverts to the previous value.
*
* {{{
* someDynamicVariable.withValue(newValue) {
* // ... code called in here that calls value ...
* // ... will be given back the newValue ...
* }
* }}}
*
* Each thread gets its own stack of bindings. When a
* new thread is created, the `DynamicVariable` gets a copy
* of the stack of bindings from the parent thread, and
* from then on the bindings for the new thread
* are independent of those for the original thread.
*
* @author Lex Spoon
* @version 1.1, 2007-5-21
*/
class DynamicVariable[T](init: T)

  currentTransportInformation是一个动态变量,其具体的功能和用法,scala官网说的也很清楚,你可以把它理解成一个能够继承父线程数据的ThreadLocal变量。

/** Set the value of the variable while executing the specified
* thunk.
*
* @param newval The value to which to set the variable
* @param thunk The code to evaluate under the new setting
*/
def withValue[S](newval: T)(thunk: => S): S = {
val oldval = value
tl set newval try thunk
finally tl set oldval
}

  withValue函数,其实就是给thunk提供一个线程安全的执行变量环境。

  综上所述,MessageSerializer.serialize(extendedSystem, msg.asInstanceOf[AnyRef])在执行时通过currentTransportInformation获取到的值就是Serialization.Information(h.localAddress, extendedSystem),那就来看看serialize在做什么。

/**
* Uses Akka Serialization for the specified ActorSystem to transform the given message to a MessageProtocol
* Throws `NotSerializableException` if serializer was not configured for the message type.
* Throws `MessageSerializer.SerializationException` if exception was thrown from `toBinary` of the
* serializer.
*/
def serialize(system: ExtendedActorSystem, message: AnyRef): SerializedMessage = {
val s = SerializationExtension(system)
val serializer = s.findSerializerFor(message)
val builder = SerializedMessage.newBuilder val oldInfo = Serialization.currentTransportInformation.value
try {
if (oldInfo eq null)
Serialization.currentTransportInformation.value = system.provider.serializationInformation builder.setMessage(ByteString.copyFrom(serializer.toBinary(message)))
builder.setSerializerId(serializer.identifier) val ms = Serializers.manifestFor(serializer, message)
if (ms.nonEmpty) builder.setMessageManifest(ByteString.copyFromUtf8(ms)) builder.build
} catch {
case NonFatal(e) ⇒
throw new SerializationException(s"Failed to serialize remote message [${message.getClass}] " +
s"using serializer [${serializer.getClass}].", e)
} finally Serialization.currentTransportInformation.value = oldInfo
}

  serialize函数简单来说,就是通过SerializationExtension给message找到一个serializer,用serializer把message转化成二进制,也就是序列化message。在通过SerializedMessage.Builder设置一些其他信息,最终返回SerializedMessage消息。那么如何通过SerializationExtension找到一个合适的serializer就很重要了。

/**
* Returns the configured Serializer for the given Class. The configured Serializer
* is used if the configured class `isAssignableFrom` from the `clazz`, i.e.
* the configured class is a super class or implemented interface. In case of
* ambiguity it is primarily using the most specific configured class,
* and secondly the entry configured first.
*
* Throws java.io.NotSerializableException if no `serialization-bindings` is configured for the class.
*/
@throws(classOf[NotSerializableException])
def serializerFor(clazz: Class[_]): Serializer =
serializerMap.get(clazz) match {
case null ⇒ // bindings are ordered from most specific to least specific
def unique(possibilities: immutable.Seq[(Class[_], Serializer)]): Boolean =
possibilities.size == 1 ||
(possibilities forall (_._1 isAssignableFrom possibilities(0)._1)) ||
(possibilities forall (_._2 == possibilities(0)._2)) val ser = {
bindings.filter {
case (c, _) ⇒ c isAssignableFrom clazz
} match {
case immutable.Seq() ⇒
throw new NotSerializableException(s"No configured serialization-bindings for class [${clazz.getName}]")
case possibilities ⇒
if (unique(possibilities))
possibilities.head._2
else {
// give JavaSerializer lower priority if multiple serializers found
val possibilitiesWithoutJavaSerializer = possibilities.filter {
case (_, _: JavaSerializer) ⇒ false
case (_, _: DisabledJavaSerializer) ⇒ false
case _ ⇒ true
}
if (possibilitiesWithoutJavaSerializer.isEmpty) {
// shouldn't happen
throw new NotSerializableException(s"More than one JavaSerializer configured for class [${clazz.getName}]")
} if (!unique(possibilitiesWithoutJavaSerializer)) {
_log.warning(LogMarker.Security, "Multiple serializers found for [{}], choosing first of: [{}]",
clazz.getName,
possibilitiesWithoutJavaSerializer.map { case (_, s) ⇒ s.getClass.getName }.mkString(", "))
}
possibilitiesWithoutJavaSerializer.head._2 } }
} serializerMap.putIfAbsent(clazz, ser) match {
case null ⇒
if (shouldWarnAboutJavaSerializer(clazz, ser)) {
_log.warning(LogMarker.Security, "Using the default Java serializer for class [{}] which is not recommended because of " +
"performance implications. Use another serializer or disable this warning using the setting " +
"'akka.actor.warn-about-java-serializer-usage'", clazz.getName)
}
log.debug("Using serializer [{}] for message [{}]", ser.getClass.getName, clazz.getName)
ser
case some ⇒ some
}
case ser ⇒ ser
}

  findSerializerFor最终调用了serializerFor,serializerFor简单点来说就是首先查找配置的序列化函数,如果没有找到则通过bindings中查找是否符合isAssignableFrom条件的序列化类,如果只找到了相同的序列化类,则使用该序列化类,如果找到多个则优先使用除JavaSerializer以外的序列化类。当然了,默认情况下是一定可以找到JavaSerializer的。serializer具体加载的过程这里就不再具体分析,只需要知道它是从配置文件加载的就可以了。那默认配置是怎么样的呢?下面是akka remote包里面的reference.conf摘录出来的部分配置。

serializers {
akka-containers = "akka.remote.serialization.MessageContainerSerializer"
akka-misc = "akka.remote.serialization.MiscMessageSerializer"
artery = "akka.remote.serialization.ArteryMessageSerializer"
proto = "akka.remote.serialization.ProtobufSerializer"
daemon-create = "akka.remote.serialization.DaemonMsgCreateSerializer"
primitive-long = "akka.remote.serialization.LongSerializer"
primitive-int = "akka.remote.serialization.IntSerializer"
primitive-string = "akka.remote.serialization.StringSerializer"
primitive-bytestring = "akka.remote.serialization.ByteStringSerializer"
akka-system-msg = "akka.remote.serialization.SystemMessageSerializer"
} serialization-bindings {
"akka.actor.ActorSelectionMessage" = akka-containers "akka.remote.DaemonMsgCreate" = daemon-create "akka.remote.artery.ArteryMessage" = artery # Since akka.protobuf.Message does not extend Serializable but
# GeneratedMessage does, need to use the more specific one here in order
# to avoid ambiguity.
"akka.protobuf.GeneratedMessage" = proto # Since com.google.protobuf.Message does not extend Serializable but
# GeneratedMessage does, need to use the more specific one here in order
# to avoid ambiguity.
# This com.google.protobuf serialization binding is only used if the class can be loaded,
# i.e. com.google.protobuf dependency has been added in the application project.
"com.google.protobuf.GeneratedMessage" = proto "java.util.Optional" = akka-misc # The following are handled by the MiscMessageSerializer, but they are not enabled for
# compatibility reasons (it was added in Akka 2.5.[8,9,12]). Enable them by adding:
# akka.actor.serialization-bindings {
# "akka.Done" = akka-misc
# "akka.NotUsed" = akka-misc
# "akka.actor.Address" = akka-misc
# "akka.remote.UniqueAddress" = akka-misc
# }
} # Additional serialization-bindings that are replacing Java serialization are
# defined in this section for backwards compatibility reasons. They are included
# by default but can be excluded for backwards compatibility with Akka 2.4.x.
# They can be disabled with enable-additional-serialization-bindings=off.
additional-serialization-bindings {
"akka.actor.Identify" = akka-misc
"akka.actor.ActorIdentity" = akka-misc
"scala.Some" = akka-misc
"scala.None$" = akka-misc
"akka.actor.Status$Success" = akka-misc
"akka.actor.Status$Failure" = akka-misc
"akka.actor.ActorRef" = akka-misc
"akka.actor.PoisonPill$" = akka-misc
"akka.actor.Kill$" = akka-misc
"akka.remote.RemoteWatcher$Heartbeat$" = akka-misc
"akka.remote.RemoteWatcher$HeartbeatRsp" = akka-misc
"akka.actor.ActorInitializationException" = akka-misc "akka.dispatch.sysmsg.SystemMessage" = akka-system-msg "java.lang.String" = primitive-string
"akka.util.ByteString$ByteString1C" = primitive-bytestring
"akka.util.ByteString$ByteString1" = primitive-bytestring
"akka.util.ByteString$ByteStrings" = primitive-bytestring
"java.lang.Long" = primitive-long
"scala.Long" = primitive-long
"java.lang.Integer" = primitive-int
"scala.Int" = primitive-int # Java Serializer is by default used for exceptions.
# It's recommended that you implement custom serializer for exceptions that are
# sent remotely, e.g. in akka.actor.Status.Failure for ask replies. You can add
# binding to akka-misc (MiscMessageSerializerSpec) for the exceptions that have
# a constructor with single message String or constructor with message String as
# first parameter and cause Throwable as second parameter. Note that it's not
# safe to add this binding for general exceptions such as IllegalArgumentException
# because it may have a subclass without required constructor.
"java.lang.Throwable" = java
"akka.actor.IllegalActorStateException" = akka-misc
"akka.actor.ActorKilledException" = akka-misc
"akka.actor.InvalidActorNameException" = akka-misc
"akka.actor.InvalidMessageException" = akka-misc "akka.actor.LocalScope$" = akka-misc
"akka.remote.RemoteScope" = akka-misc "com.typesafe.config.impl.SimpleConfig" = akka-misc
"com.typesafe.config.Config" = akka-misc "akka.routing.FromConfig" = akka-misc
"akka.routing.DefaultResizer" = akka-misc
"akka.routing.BalancingPool" = akka-misc
"akka.routing.BroadcastGroup" = akka-misc
"akka.routing.BroadcastPool" = akka-misc
"akka.routing.RandomGroup" = akka-misc
"akka.routing.RandomPool" = akka-misc
"akka.routing.RoundRobinGroup" = akka-misc
"akka.routing.RoundRobinPool" = akka-misc
"akka.routing.ScatterGatherFirstCompletedGroup" = akka-misc
"akka.routing.ScatterGatherFirstCompletedPool" = akka-misc
"akka.routing.SmallestMailboxPool" = akka-misc
"akka.routing.TailChoppingGroup" = akka-misc
"akka.routing.TailChoppingPool" = akka-misc
"akka.remote.routing.RemoteRouterConfig" = akka-misc
}

  通过上面的配置,我们知道ActorRef是通过akka-misc,也就是akka.remote.serialization.MiscMessageSerializer来序列化的。

  MiscMessageSerializer.toBinary是调用了serializeActorRef对ActorRef序列化的。

private def serializeActorRef(ref: ActorRef): Array[Byte] =
actorRefBuilder(ref).build().toByteArray
  private def actorRefBuilder(actorRef: ActorRef): ContainerFormats.ActorRef.Builder =
ContainerFormats.ActorRef.newBuilder()
.setPath(Serialization.serializedActorPath(actorRef))
/**
* The serialized path of an actorRef, based on the current transport serialization information.
* If there is no external address available in the given `ActorRef` then the systems default
* address will be used and that is retrieved from the ThreadLocal `Serialization.Information`
* that was set with [[Serialization#withTransportInformation]].
*/
def serializedActorPath(actorRef: ActorRef): String = {
val path = actorRef.path
val originalSystem: ExtendedActorSystem = actorRef match {
case a: ActorRefWithCell ⇒ a.underlying.system.asInstanceOf[ExtendedActorSystem]
case _ ⇒ null
}
Serialization.currentTransportInformation.value match {
case null ⇒ originalSystem match {
case null ⇒ path.toSerializationFormat
case system ⇒
try path.toSerializationFormatWithAddress(system.provider.getDefaultAddress)
catch { case NonFatal(_) ⇒ path.toSerializationFormat }
}
case Information(address, system) ⇒
if (originalSystem == null || originalSystem == system)
path.toSerializationFormatWithAddress(address)
else {
val provider = originalSystem.provider
path.toSerializationFormatWithAddress(provider.getExternalAddressFor(address).getOrElse(provider.getDefaultAddress))
}
}
}

  首先Serialization.currentTransportInformation.value一定不为空,这个之前已经赋值过了,所以一定会走到Information(address, system),而且无论执行if的哪个分支,最后都是通过调用toSerializationFormatWithAddress对ActorRef进行序列化的。

  这样来看ActorRef在序列化时,对当前的path转化成了序列化的格式,其实就是ActorPath的String值。只不过在remote模式下,是包含host:port、协议(比如akka.tcp)等信息的。那么分析到这里,聪明的读者一定知道反序列化的过程了:对ActorPath的String值进行解析,转化成对应的RemoteActorRef。关于如果通过ActorPath在之前的文章其实我们有分析过,但这里还是再带领大家过一遍。

override def receive: Receive = {
case Disassociated(info) ⇒ handleDisassociated(info) case InboundPayload(p) if p.size <= transport.maximumPayloadBytes ⇒
val (ackOption, msgOption) = tryDecodeMessageAndAck(p) for (ack ← ackOption; reliableDelivery ← reliableDeliverySupervisor) reliableDelivery ! ack msgOption match {
case Some(msg) ⇒
if (msg.reliableDeliveryEnabled) {
ackedReceiveBuffer = ackedReceiveBuffer.receive(msg)
deliverAndAck()
} else try
msgDispatch.dispatch(msg.recipient, msg.recipientAddress, msg.serializedMessage, msg.senderOption)
catch {
case e: NotSerializableException ⇒ logTransientSerializationError(msg, e)
case e: IllegalArgumentException ⇒ logTransientSerializationError(msg, e)
} case None ⇒
} case InboundPayload(oversized) ⇒
log.error(
new OversizedPayloadException(s"Discarding oversized payload received: " +
s"max allowed size [${transport.maximumPayloadBytes}] bytes, actual size [${oversized.size}] bytes."),
"Transient error while reading from association (association remains live)") case StopReading(writer, replyTo) ⇒
saveState()
context.become(notReading)
replyTo ! StoppedReading(writer) }

  EndpointReader.receive在收到InboundPayload消息后,先把它decode成Message,然后把消息通过msgDispatch.dispatch发送出去,而msgDispatch是一个DefaultMessageDispatcher实例。

override def dispatch(
recipient: InternalActorRef,
recipientAddress: Address,
serializedMessage: SerializedMessage,
senderOption: OptionVal[ActorRef]): Unit = { import provider.remoteSettings._ lazy val payload: AnyRef = MessageSerializer.deserialize(system, serializedMessage)
def payloadClass: Class[_] = if (payload eq null) null else payload.getClass
val sender: ActorRef = senderOption.getOrElse(system.deadLetters)
val originalReceiver = recipient.path def logMessageReceived(messageType: String): Unit = {
if (LogReceive && log.isDebugEnabled)
log.debug(s"received $messageType RemoteMessage: [{}] to [{}]<+[{}] from [{}]", payload, recipient, originalReceiver, sender)
} recipient match { case `remoteDaemon` ⇒
if (UntrustedMode) log.debug(LogMarker.Security, "dropping daemon message in untrusted mode")
else {
logMessageReceived("daemon message")
remoteDaemon ! payload
} case l @ (_: LocalRef | _: RepointableRef) if l.isLocal ⇒
logMessageReceived("local message")
payload match {
case sel: ActorSelectionMessage ⇒
if (UntrustedMode && (!TrustedSelectionPaths.contains(sel.elements.mkString("/", "/", "")) ||
sel.msg.isInstanceOf[PossiblyHarmful] || l != provider.rootGuardian))
log.debug(
LogMarker.Security,
"operating in UntrustedMode, dropping inbound actor selection to [{}], " +
"allow it by adding the path to 'akka.remote.trusted-selection-paths' configuration",
sel.elements.mkString("/", "/", ""))
else
// run the receive logic for ActorSelectionMessage here to make sure it is not stuck on busy user actor
ActorSelection.deliverSelection(l, sender, sel)
case msg: PossiblyHarmful if UntrustedMode ⇒
log.debug(LogMarker.Security, "operating in UntrustedMode, dropping inbound PossiblyHarmful message of type [{}]", msg.getClass.getName)
case msg: SystemMessage ⇒ l.sendSystemMessage(msg)
case msg ⇒ l.!(msg)(sender)
} case r @ (_: RemoteRef | _: RepointableRef) if !r.isLocal && !UntrustedMode ⇒
logMessageReceived("remote-destined message")
if (provider.transport.addresses(recipientAddress))
// if it was originally addressed to us but is in fact remote from our point of view (i.e. remote-deployed)
r.!(payload)(sender)
else
log.error(
"dropping message [{}] for non-local recipient [{}] arriving at [{}] inbound addresses are [{}]",
payloadClass, r, recipientAddress, provider.transport.addresses.mkString(", ")) case r ⇒ log.error(
"dropping message [{}] for unknown recipient [{}] arriving at [{}] inbound addresses are [{}]",
payloadClass, r, recipientAddress, provider.transport.addresses.mkString(", ")) }
}

  dispatch首先调用MessageSerializer.deserialize(system, serializedMessage)对消息进行反序列化。

  /**
* Uses Akka Serialization for the specified ActorSystem to transform the given MessageProtocol to a message
*/
def deserialize(system: ExtendedActorSystem, messageProtocol: SerializedMessage): AnyRef = {
SerializationExtension(system).deserialize(
messageProtocol.getMessage.toByteArray,
messageProtocol.getSerializerId,
if (messageProtocol.hasMessageManifest) messageProtocol.getMessageManifest.toStringUtf8 else "").get
}
 /**
* Deserializes the given array of bytes using the specified serializer id,
* using the optional type hint to the Serializer.
* Returns either the resulting object or an Exception if one was thrown.
*/
def deserialize(bytes: Array[Byte], serializerId: Int, manifest: String): Try[AnyRef] =
Try {
val serializer = try getSerializerById(serializerId) catch {
case _: NoSuchElementException ⇒ throw new NotSerializableException(
s"Cannot find serializer with id [$serializerId]. The most probable reason is that the configuration entry " +
"akka.actor.serializers is not in synch between the two systems.")
}
deserializeByteArray(bytes, serializer, manifest)
}

  分析上面代码得知,反序列化时就是简单的通过serializerId找到对应具体的序列化类,然后调用deserializeByteArray函数进行反序列化。还记得如果当前消息是ActorRef的话,serializerId是什么吗?没错,就是akka-misc。也就是说最终通过akka.remote.serialization.MiscMessageSerializer进行反序列化。但有一个字段也是比较关键manifest,这个manifest是什么呢?可以从ActorRef的序列化过程找到蛛丝马迹。

  在MessageSerializer.serialize函数中,有一段代码对这个manifest进行了赋值:val ms = Serializers.manifestFor(serializer, message)。

def manifestFor(s: Serializer, message: AnyRef): String = s match {
case s2: SerializerWithStringManifest ⇒ s2.manifest(message)
case _ ⇒ if (s.includeManifest) message.getClass.getName else ""
}

  其实就是判断当前的Serializer是不是SerializerWithStringManifest的子类,如果是就调用manifest,如果不是,就判断includeManifest是不是为true,如果是就返回当前类的类名,否则返回空字符串。我们来看下MiscMessageSerializer的定义。

class MiscMessageSerializer(val system: ExtendedActorSystem) extends SerializerWithStringManifest with BaseSerializer

private val ActorRefManifest = "G"

  很明显它继承了SerializerWithStringManifest,而且对于ActorRef,manifest的值就是字符串G。

private def deserializeByteArray(bytes: Array[Byte], serializer: Serializer, manifest: String): AnyRef = {

    @tailrec def updateCache(cache: Map[String, Option[Class[_]]], key: String, value: Option[Class[_]]): Boolean = {
manifestCache.compareAndSet(cache, cache.updated(key, value)) ||
updateCache(manifestCache.get, key, value) // recursive, try again
} withTransportInformation { () ⇒
serializer match {
case s2: SerializerWithStringManifest ⇒ s2.fromBinary(bytes, manifest)
case s1 ⇒
if (manifest == "")
s1.fromBinary(bytes, None)
else {
val cache = manifestCache.get
cache.get(manifest) match {
case Some(cachedClassManifest) ⇒ s1.fromBinary(bytes, cachedClassManifest)
case None ⇒
system.dynamicAccess.getClassFor[AnyRef](manifest) match {
case Success(classManifest) ⇒
val classManifestOption: Option[Class[_]] = Some(classManifest)
updateCache(cache, manifest, classManifestOption)
s1.fromBinary(bytes, classManifestOption)
case Failure(e) ⇒
throw new NotSerializableException(
s"Cannot find manifest class [$manifest] for serializer with id [${serializer.identifier}].")
}
}
}
}
}
}

  那我们接下来看看deserializeByteArray的源码,它首先会判断当前serializer是不是SerializerWithStringManifest,很显然对于ActorRef,serializer是SerializerWithStringManifest的子类,那我们来看看是fromBinary是如何实现的。

  override def fromBinary(bytes: Array[Byte], manifest: String): AnyRef =
fromBinaryMap.get(manifest) match {
case Some(deserializer) ⇒ deserializer(bytes)
case None ⇒ throw new NotSerializableException(
s"Unimplemented deserialization of message with manifest [$manifest] in [${getClass.getName}]")
}
 private val fromBinaryMap = Map[String, Array[Byte] ⇒ AnyRef](
IdentifyManifest → deserializeIdentify,
ActorIdentityManifest → deserializeActorIdentity,
StatusSuccessManifest → deserializeStatusSuccess,
StatusFailureManifest → deserializeStatusFailure,
ThrowableManifest → throwableSupport.deserializeThrowable,
ActorRefManifest → deserializeActorRefBytes,
OptionManifest → deserializeOption,
OptionalManifest → deserializeOptional,
PoisonPillManifest → ((_) ⇒ PoisonPill),
KillManifest → ((_) ⇒ Kill),
RemoteWatcherHBManifest → ((_) ⇒ RemoteWatcher.Heartbeat),
DoneManifest → ((_) ⇒ Done),
NotUsedManifest → ((_) ⇒ NotUsed),
AddressManifest → deserializeAddressData,
UniqueAddressManifest → deserializeUniqueAddress,
RemoteWatcherHBRespManifest → deserializeHeartbeatRsp,
ActorInitializationExceptionManifest → deserializeActorInitializationException,
LocalScopeManifest → ((_) ⇒ LocalScope),
RemoteScopeManifest → deserializeRemoteScope,
ConfigManifest → deserializeConfig,
FromConfigManifest → deserializeFromConfig,
DefaultResizerManifest → deserializeDefaultResizer,
BalancingPoolManifest → deserializeBalancingPool,
BroadcastPoolManifest → deserializeBroadcastPool,
RandomPoolManifest → deserializeRandomPool,
RoundRobinPoolManifest → deserializeRoundRobinPool,
ScatterGatherPoolManifest → deserializeScatterGatherPool,
TailChoppingPoolManifest → deserializeTailChoppingPool,
RemoteRouterConfigManifest → deserializeRemoteRouterConfig
)
  private def deserializeActorRefBytes(bytes: Array[Byte]): ActorRef =
deserializeActorRef(ContainerFormats.ActorRef.parseFrom(bytes))
private def deserializeActorRef(actorRef: ContainerFormats.ActorRef): ActorRef =
serialization.system.provider.resolveActorRef(actorRef.getPath)

  由此可见,首先调用了ContainerFormats.ActorRef.parseFrom把Array[Byte] 转化成了ContainerFormats.ActorRef,这个过程就不再具体分析;其次调用serialization.system.provider.resolveActorRef把当前的ActorPathString转化成了ActorRef。根据上下文,serialization.system.provider应该就是RemoteActorRefProvider。

def resolveActorRef(path: String): ActorRef = {
// using thread local LRU cache, which will call internalRresolveActorRef
// if the value is not cached
actorRefResolveThreadLocalCache match {
case null ⇒ internalResolveActorRef(path) // not initalized yet
case c ⇒ c.threadLocalCache(this).getOrCompute(path)
}
}

  resolveActorRef这段代码会先判断当前actorRefResolveThreadLocalCache缓存是否已经初始化,很显然在RemoteActorRefProvider.init过程中,actorRefResolveThreadLocalCache已经被创建,之前也分析过。

/**
* INTERNAL API
*/
private[akka] final class ActorRefResolveCache(provider: RemoteActorRefProvider)
extends LruBoundedCache[String, ActorRef](capacity = 1024, evictAgeThreshold = 600) { override protected def compute(k: String): ActorRef =
provider.internalResolveActorRef(k) override protected def hash(k: String): Int = Unsafe.fastHash(k) override protected def isCacheable(v: ActorRef): Boolean = !v.isInstanceOf[EmptyLocalActorRef]
}

  actorRefResolveThreadLocalCache是一个ActorSystem的Extension,这个Extension最终是ActorRefResolveCache的实例,这个类是不是很熟悉?它是一个LruBoundedCache,容量是1024,过期时间是600秒。很显然第一次getOrCompute时,会调用compute函数,而compute又调用了provider.internalResolveActorRef,解析之后对解析的结果进行缓存,具体如何缓存也不再分析。下面来分析internalResolveActorRef。

 /**
* INTERNAL API: This is used by the `ActorRefResolveCache` via the
* public `resolveActorRef(path: String)`.
*/
private[akka] def internalResolveActorRef(path: String): ActorRef = path match {
case ActorPathExtractor(address, elems) ⇒
if (hasAddress(address)) local.resolveActorRef(rootGuardian, elems)
else {
val rootPath = RootActorPath(address) / elems
try {
new RemoteActorRef(transport, transport.localAddressForRemote(address),
rootPath, Nobody, props = None, deploy = None)
} catch {
case NonFatal(e) ⇒
log.warning("Error while resolving ActorRef [{}] due to [{}]", path, e.getMessage)
new EmptyLocalActorRef(this, rootPath, eventStream)
}
}
case _ ⇒
log.debug("Resolve (deserialization) of unknown (invalid) path [{}], using deadLetters.", path)
deadLetters
}

  internalResolveActorRef还有印象吗?这个函数在分析actorSelection过程中出现过,它首先会判断当前机器是否包含path中的address,如果包含就调用LocalActorRefProvider进行查找,否则就创建RemoteActorRef。因为消息来自远程actor,所以会创建RemoteActorRef作为远程actor的代理。至此远程ActorRef反序列化结束。

  至此我们对ActorRef的序列化、反序列化过程做了完整的分析,其实Akka的位置透明就是通过ActorPath来实现的,ActorRef跨网路传输都是通过ActorPath的String值(包含host/port等信息)来完成的,指定host收到ActorRef的序列化消息,会根据反序列化后的ActorPath的String值,在本地创建远程actor的ActorRef代理:RemoteActorRef。之后的通信都是通过RemoteActorRef。这样看来,位置透明也是比较简单的。

Akka源码分析-Remote-位置透明的更多相关文章

  1. Akka源码分析-Cluster-Singleton

    akka Cluster基本实现原理已经分析过,其实它就是在remote基础上添加了gossip协议,同步各个节点信息,使集群内各节点能够识别.在Cluster中可能会有一个特殊的节点,叫做单例节点. ...

  2. Akka源码分析-Cluster-Distributed Publish Subscribe in Cluster

    在ClusterClient源码分析中,我们知道,他是依托于“Distributed Publish Subscribe in Cluster”来实现消息的转发的,那本文就来分析一下Pub/Sub是如 ...

  3. Akka源码分析-Persistence

    在学习akka过程中,我们了解了它的监督机制,会发现actor非常可靠,可以自动的恢复.但akka框架只会简单的创建新的actor,然后调用对应的生命周期函数,如果actor有状态需要回复,我们需要h ...

  4. Akka源码分析-local-DeathWatch

    生命周期监控,也就是死亡监控,是akka编程中常用的机制.比如我们有了某个actor的ActorRef之后,希望在该actor死亡之后收到响应的消息,此时我们就可以使用watch函数达到这一目的. c ...

  5. Akka源码分析-Cluster-ActorSystem

    前面几篇博客,我们依次介绍了local和remote的一些内容,其实再分析cluster就会简单很多,后面关于cluster的源码分析,能够省略的地方,就不再贴源码而是一句话带过了,如果有不理解的地方 ...

  6. Akka源码分析-Akka Typed

    对不起,akka typed 我是不准备进行源码分析的,首先这个库的API还没有release,所以会may change,也就意味着其概念和设计包括API都会修改,基本就没有再深入分析源码的意义了. ...

  7. Akka源码分析-Akka-Streams-概念入门

    今天我们来讲解akka-streams,这应该算akka框架下实现的一个很高级的工具.之前在学习akka streams的时候,我是觉得云里雾里的,感觉非常复杂,而且又难学,不过随着对akka源码的深 ...

  8. Akka源码分析-Cluster-Metrics

    一个应用软件维护的后期一定是要做监控,akka也不例外,它提供了集群模式下的度量扩展插件. 其实如果读者读过前面的系列文章的话,应该是能够自己写一个这样的监控工具的.简单来说就是创建一个actor,它 ...

  9. Akka源码分析-Remote-Creating Actors Remotely

    在akka官网中关于远程actor交互,介绍了两种方法,一种是通过actorSelection查询,另一种是通过actorOf在远程节点创建一个actor.actorSelection我们之前的博客中 ...

随机推荐

  1. Java对象序列化为什么要使用SerialversionUID

    1.首先谈谈为什么要序列化对象 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通 ...

  2. python_ 学习笔记(运算符)

    python的运算符基本和C语言一致,以下说一些不一样的! 算术运算符 **:代表乘方,对应也有**=: //:代表商向下取整,对应也有//=: 逻辑运算符 and or not 位运算符 :& ...

  3. response对象设置输出缓冲大小

    response对象设置输出缓冲大小 制作人:全心全意 通常情况下,服务器要输出到客户端的内容不会直接写到客户端,而是先写到一个输出缓冲区,在计算机术语中,缓冲区被定义为暂时放置输入或输出资料的内存. ...

  4. docke容器使用

    Docker 容器使用 Docker 客户端 docker 客户端非常简单 ,我们可以直接输入 docker 命令来查看到 Docker 客户端的所有命令选项. runoob@runoob:~# do ...

  5. JRebel 7.1.5 插件下载 安装 激活 结合 IntelliJ IDEA--自动编译进行热部署---

    Intellij IDEA 安装和配置jrebel进行项目的热部署 https://www.cnblogs.com/a8457013/p/7866625.html Intellij IDEA 使用jr ...

  6. 九度oj 题目1059:abc

    题目1059:abc 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:4510 解决:3546 题目描述: 设a.b.c均是0到9之间的数字,abc.bcc是两个三位数,且有:abc+bcc ...

  7. [K/3Cloud] KSQL 关联表更新字段Update语法

    关联表更新字段 UPDATE tmp369faa3f7d224b0595670425008 as t1 SET FStatus=-1 where exists(select 1 from t_BD_S ...

  8. Happy 2006 欧几里得定理

    Happy 2006 Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 11956   Accepted: 4224 Descr ...

  9. tomcat服务器配置把Http协议强制转化为Https

    1)在命令提示符窗口,进入Tomcat目录,执行以下命令: keytool -genkey -alias tomcat -keyalg RSA -keypass changeit -storepass ...

  10. Hive之内置函数

    函数分类 UDF(User Defined Function):数据一对一 UDAF(User Defined Aggreation Function):数据多对一 UDTF(User Defined ...