Akka源码分析-Remote-ActorSystem
前面的文章都是基于local模式分析的,现在我们简要分析一下在remote模式下,ActorSystem的创建过程。
final val ProviderClass: String =
setup.get[BootstrapSetup]
.flatMap(_.actorRefProvider).map(_.identifier)
.getOrElse(getString("akka.actor.provider")) match {
case "local" ⇒ classOf[LocalActorRefProvider].getName
// these two cannot be referenced by class as they may not be on the classpath
case "remote" ⇒ "akka.remote.RemoteActorRefProvider"
case "cluster" ⇒ "akka.cluster.ClusterActorRefProvider"
case fqcn ⇒ fqcn
}
之前我们分析过,在创建provider过程中,是通过ProviderClass来判断具体是哪种模式的。从ProviderClass源码来看,当我们配置akka.actor.provider为remote时,会创建akka.remote.RemoteActorRefProvider的实例。我们知道ActorSystem在start时候会去调用provider.init方法进行初始化。
def init(system: ActorSystemImpl): Unit = {
local.init(system)
actorRefResolveThreadLocalCache = ActorRefResolveThreadLocalCache(system)
remotingTerminator = system.systemActorOf(
remoteSettings.configureDispatcher(Props(classOf[RemotingTerminator], local.systemGuardian)),
"remoting-terminator")
val internals = Internals(
remoteDaemon = {
val d = new RemoteSystemDaemon(
system,
local.rootPath / "remote",
rootGuardian,
remotingTerminator,
_log,
untrustedMode = remoteSettings.UntrustedMode)
local.registerExtraNames(Map(("remote", d)))
d
},
transport =
if (remoteSettings.Artery.Enabled) remoteSettings.Artery.Transport match {
case ArterySettings.AeronUpd ⇒ new ArteryAeronUdpTransport(system, this)
case ArterySettings.Tcp ⇒ new ArteryTcpTransport(system, this, tlsEnabled = false)
case ArterySettings.TlsTcp ⇒ new ArteryTcpTransport(system, this, tlsEnabled = true)
}
else new Remoting(system, this))
_internals = internals
remotingTerminator ! internals
_log = Logging.withMarker(eventStream, getClass.getName)
// this enables reception of remote requests
transport.start()
_remoteWatcher = createRemoteWatcher(system)
remoteDeploymentWatcher = createRemoteDeploymentWatcher(system)
}
我们来结合RemoteActorRefProvider的构造函数和init函数来初步理解RemoteActorRefProvider的行为。首先在init方法的第一步就是调用local的init,通过local的类型我们发现这是一个LocalActorRefProvider,local的作用暂时不做分析,继续往下看。
下面创建了ActorRefResolveThreadLocalCache对象,从ActorRefResolveThreadLocalCache的定义来看(这里就不再贴出相关代码),它是一个ThreadLocal变量,且是一个实现了Lru的缓存器,缓存的内容是ActorRef,具体作用也忽略。remotingTerminator的具体作用也不做深入分析。
private final case class Internals(transport: RemoteTransport, remoteDaemon: InternalActorRef)
extends NoSerializationVerificationNeeded
Internals的定义还是值得一看的,它有两个变量,其中transport的值应该是new Remoting(system, this),remoteDaemon的值是RemoteSystemDaemon。然后调用了transport.start(),也就是Remoting的start。那么Remoting具体又是什呢?
// Start assumes that it cannot be followed by another start() without having a shutdown() first
override def start(): Unit = {
endpointManager match {
case None ⇒
log.info("Starting remoting")
val manager: ActorRef = system.systemActorOf(
configureDispatcher(Props(classOf[EndpointManager], provider.remoteSettings.config, log)).withDeploy(Deploy.local),
Remoting.EndpointManagerName)
endpointManager = Some(manager) try {
val addressesPromise: Promise[Seq[(AkkaProtocolTransport, Address)]] = Promise()
manager ! Listen(addressesPromise) val transports: Seq[(AkkaProtocolTransport, Address)] = Await.result(
addressesPromise.future,
StartupTimeout.duration)
if (transports.isEmpty) throw new RemoteTransportException("No transport drivers were loaded.", null) transportMapping = transports.groupBy {
case (transport, _) ⇒ transport.schemeIdentifier
} map { case (k, v) ⇒ k → v.toSet } defaultAddress = transports.head._2
addresses = transports.map { _._2 }.toSet log.info("Remoting started; listening on addresses :" + addresses.mkString("[", ", ", "]")) manager ! StartupFinished
eventPublisher.notifyListeners(RemotingListenEvent(addresses)) } catch {
case e: TimeoutException ⇒
notifyError("Startup timed out. This is usually related to actor system host setting or host name resolution misconfiguration.", e)
throw e
case NonFatal(e) ⇒
notifyError("Startup failed", e)
throw e
} case Some(_) ⇒
log.warning("Remoting was already started. Ignoring start attempt.")
}
}
在Remoting.start过程中,首先创建了EndpointManager,然后发送了一条Listen消息,并使用Await.result等待它的返回,然后又给EndpointManager发送了StartUpFinished。上面代码中的log.info("Remoting started; listening on addresses :" + addresses.mkString("[", ", ", "]"))还是值得我们关注的,毕竟我们启动remote模式的ActorSystem会经常看到这个日志信息。我们来看看EndpointManager收到Listen消息后做了哪些操作。

那么listens又是什么呢?
private def listens: Future[Seq[(AkkaProtocolTransport, Address, Promise[AssociationEventListener])]] = {
/*
* Constructs chains of adapters on top of each driver as given in configuration. The resulting structure looks
* like the following:
* AkkaProtocolTransport <- Adapter <- ... <- Adapter <- Driver
*
* The transports variable contains only the heads of each chains (the AkkaProtocolTransport instances).
*/
val transports: Seq[AkkaProtocolTransport] = for ((fqn, adapters, config) ← settings.Transports) yield {
val args = Seq(classOf[ExtendedActorSystem] → context.system, classOf[Config] → config)
// Loads the driver -- the bottom element of the chain.
// The chain at this point:
// Driver
val driver = extendedSystem.dynamicAccess
.createInstanceFor[Transport](fqn, args).recover({
case exception ⇒ throw new IllegalArgumentException(
s"Cannot instantiate transport [$fqn]. " +
"Make sure it extends [akka.remote.transport.Transport] and has constructor with " +
"[akka.actor.ExtendedActorSystem] and [com.typesafe.config.Config] parameters", exception)
}).get
// Iteratively decorates the bottom level driver with a list of adapters.
// The chain at this point:
// Adapter <- ... <- Adapter <- Driver
val wrappedTransport =
adapters.map { TransportAdaptersExtension.get(context.system).getAdapterProvider }.foldLeft(driver) {
(t: Transport, provider: TransportAdapterProvider) ⇒
// The TransportAdapterProvider will wrap the given Transport and returns with a wrapped one
provider.create(t, context.system.asInstanceOf[ExtendedActorSystem])
}
// Apply AkkaProtocolTransport wrapper to the end of the chain
// The chain at this point:
// AkkaProtocolTransport <- Adapter <- ... <- Adapter <- Driver
new AkkaProtocolTransport(wrappedTransport, context.system, new AkkaProtocolSettings(conf), AkkaPduProtobufCodec)
}
// Collect all transports, listen addresses and listener promises in one future
Future.sequence(transports.map { transport ⇒
transport.listen map { case (address, listenerPromise) ⇒ (transport, address, listenerPromise) }
})
}
很明显这是一个transports集合,每个transports应该是一个AkkaProtocolTransport对象,AkkaProtocolTransport创建完成之后,调用了listen方法,最终返回AkkaProtocolTransport的列表。其实分析到这里我们可以不必再继续深入AkkaProtocolTransport的具体功能,从上面的官方注释以及我们的猜测来看,这大概是在初始化网络相关的对象。比如它可以是一个socket或者netty封装后的socket,是用来listen某个端口号,接收和发送数据的。
当然RemoteActorRefProvider.init的最后两行分别创建了RemoteWatcher、RemoteDeploymentWatcher,这两个Actor的作用后面再具体分析。
至此,remote模式下的初始化基本就算结束了,其实就是用RemoteActorRefProvider替换了LocalActorRefProvider,并完成了provider相关的初始化。remote模式与local模式下,ActorSystem初始化过程区别并不大,这还得多谢Akka框架封装的好。下一篇博客我们会分析actor的创建过程,毕竟在remote模式下,actor的创建过程还是有点不同的。
Akka源码分析-Remote-ActorSystem的更多相关文章
- Akka源码分析-Cluster-ActorSystem
前面几篇博客,我们依次介绍了local和remote的一些内容,其实再分析cluster就会简单很多,后面关于cluster的源码分析,能够省略的地方,就不再贴源码而是一句话带过了,如果有不理解的地方 ...
- Akka源码分析-Akka-Streams-概念入门
今天我们来讲解akka-streams,这应该算akka框架下实现的一个很高级的工具.之前在学习akka streams的时候,我是觉得云里雾里的,感觉非常复杂,而且又难学,不过随着对akka源码的深 ...
- Akka源码分析-Cluster-Metrics
一个应用软件维护的后期一定是要做监控,akka也不例外,它提供了集群模式下的度量扩展插件. 其实如果读者读过前面的系列文章的话,应该是能够自己写一个这样的监控工具的.简单来说就是创建一个actor,它 ...
- Akka源码分析-Cluster-Distributed Publish Subscribe in Cluster
在ClusterClient源码分析中,我们知道,他是依托于“Distributed Publish Subscribe in Cluster”来实现消息的转发的,那本文就来分析一下Pub/Sub是如 ...
- Akka源码分析-Cluster-Singleton
akka Cluster基本实现原理已经分析过,其实它就是在remote基础上添加了gossip协议,同步各个节点信息,使集群内各节点能够识别.在Cluster中可能会有一个特殊的节点,叫做单例节点. ...
- Akka源码分析-Persistence
在学习akka过程中,我们了解了它的监督机制,会发现actor非常可靠,可以自动的恢复.但akka框架只会简单的创建新的actor,然后调用对应的生命周期函数,如果actor有状态需要回复,我们需要h ...
- Akka源码分析-local-DeathWatch
生命周期监控,也就是死亡监控,是akka编程中常用的机制.比如我们有了某个actor的ActorRef之后,希望在该actor死亡之后收到响应的消息,此时我们就可以使用watch函数达到这一目的. c ...
- Akka源码分析-Akka Typed
对不起,akka typed 我是不准备进行源码分析的,首先这个库的API还没有release,所以会may change,也就意味着其概念和设计包括API都会修改,基本就没有再深入分析源码的意义了. ...
- Akka源码分析-ActorSystem
由于本人对Akka比较感兴趣,也用Akka开发了一些系统,但对Akka的源码还没有具体分析过,希望研究源码的同时写一点博客跟大家分享.有不当之处还请指正.我准备采取Debug的方式来研究Akka的运行 ...
- Akka源码分析-Serialization
今天我们来谈一下akka的序列化框架,其实序列化.反序列化是一个老生常谈的问题,那么我们为什么还要研究一下akka的序列化框架呢?不就是使用哪种序列化.反序列化方法的区别么?其实刚开始的时候我也是这么 ...
随机推荐
- Python学习-while循环练习
1.计算1-100的和 i = 1; total = 0; while i <= 100: total = total + i; i = i + 1; print(total); 2.打印出1- ...
- 爬虫实战(一) 用Python爬取百度百科
最近博主遇到这样一个需求:当用户输入一个词语时,返回这个词语的解释 我的第一个想法是做一个数据库,把常用的词语和词语的解释放到数据库里面,当用户查询时直接读取数据库结果 但是自己又没有心思做这样一个数 ...
- 搭建一台功能简单的FTP服务器
#vi /etc/sysconfig/network-scripts/ifcfg-eno33554952 #revice network restart #ping www.baidu.com #ip ...
- [bzoj1966][Ahoi2005][VIRUS 病毒检测] (字符串dp)
Description 科学家们在Samuel星球上的探险仍在继续.非常幸运的,在Samuel星球的南极附近,探险机器人发现了一个巨大的冰湖!机器人在这个冰湖中搜集到了许多RNA片段运回了实验基地.科 ...
- redis—持久化
Redis持久化 概述 Redis的强大性能很大程度上都是因为所有数据都是存储在内存中的,然而当Redis重启后,所有存储在内存中的数据将会丢失,在很多情况下是无法容忍这样的事情的.所以,我们需要将内 ...
- 调试pcb板子的步骤
在从外边焊回来的板子中查找问题的时候,如果只是简单的 一通乱调,很有可能一下子就调好了,但是大多数的时候是调了半天,不知道接下来该如何进行,因此,严格的按照步骤走,是个不错的想法: 1.拿到板子的第一 ...
- HDU1507 Uncle Tom's Inherited Land*
题目是跟 zoj1516是一样的,但多了匹配后的输出 详解zoj1516可见http://www.cnblogs.com/CSU3901130321/p/4228057.html #include & ...
- C语言编程规范试题
C语言编程规范试题 [说明]: 1.本试题中不考虑头文件引用问题(假定已经包含正确的头文件),C语言的标准函数都可用: 2.如果不特别说明,假定程序运行环境为:操作系统Windows 2000, VC ...
- SpringBoot 拦截器--只允许进入登录注册页面,没登录不允许查看其它页面
SpringBoot注册登录(一):User表的设计点击打开链接 SpringBoot注册登录(二):注册---验证码kaptcha的实现点击打开链接 SpringBoot注册登录(三):注册--验证 ...
- 最小生成树+BFS J - Borg Maze
The Borg is an immensely powerful race of enhanced humanoids from the delta quadrant of the galaxy. ...