【这是一猿小讲的第 48 篇原创分享】服务器
各位坐稳扶好,咱们要开车了。不过在开车以前,咱们仍是例行回顾一下上期分享的要点。socket
上期咱们抛了一个砖:“如何实现 Java 应用进程的状态监控,若是被监控的进程 down 掉,是否有机制能启动起来?”并结合 Resin 应用服务器背后启动的进程,更详细的阐述了一下问题。函数
咱们依然不考虑本身怎么去实现,而是先看看 Resin 这款技术轮子,是否是按照咱们的猜测设计的呢?是否是能够模仿一二?ui
好了,熟读唐诗三百首,不会做诗也会吟,抱着疑问,咱们再深刻一次 Resin 的源码,若是不感兴趣,或者脑仁疼,那建议跳到下篇直接撸码实战。命令行
咱们从 ResinBoot 的 main 函数开始,一步一步来看看怎么实现,目标是提取里面的关键核心思想,细枝末节莫纠结。线程
ResinBoot 的 main 函数是 Resin 应用服务器入口,大概流程以下:设计
建立 ResinBoot 实例 boot;3d
而后根据传入的 args 参数获取对应的 XxCommand 对象;日志
接着调用 ResinBoot 的 start 函数,完成服务的启动;orm
最后退出 ResinBoot 的进程。
那接下来不妨再深刻看看建立 ResinBoot 对象时,构造方法中都干了啥?
发现 ResinBoot 建立对象时,构造方法中建立了 WatchdogArgs 对象,WatchdogArgs 里面到底又作了什么呢?
发现 WatchdogArgs 静态代码块中提早预制了一堆命令执行的对象实例,其中就包括 StartCommand,再回头看看它的构造方法,会发现会进行解析我们 main 函数传入的 args 入参,而后根据入参获取对应的命令执行对象。
其中格式化命令行入参的方法 parseCommandLine 会匹配一堆预制的参数,实在匹配不到就从静态的 _commandMap 中去匹配对应的命令执行对象,固然我们传入的参数是 start,因此会匹配成功 StartCommand。
那么咱们再回头看看 ResinBoot 的 main 函数,发现又调用了 ResinBoot 的 start 方法。
那咱们也一块儿看看 ResinBoot 的 start 方法作了些什么操做?发现方法中获取对应的 command,而后调用 command.doCommand() 方法。
咱们知道此时的 command 为 StartCommand,那 StartCommand 中的 doCommand() 到底作了什么事情呢?会发现 doCommand 方法中调用了 WatchdogClient 的 startWatchdog 的方法。
那咱们看看 startWatchdog 方法中又作了哪些事情?重点要来了,重点要来了,重点要来了。发现调用了 launchManager 方法,那 launchManager 又是干什么的呢?
仔细深刻会发现,launchManager 方法主要用 ProcessBuilder 来实现启动 WatchdogManager 的进程。
而且第 539 行有一句日志输出,和我们在控制台输入 ./resin.sh start 命令启动 Resin 服务时的日志不谋而合。
至此,ResinBoot 经过 ProcessBuilder.start() 成功触发启动 WatchdogManager 进程,也就是我们猜想的大总管进程,哪丫环进程是否也存在呢?丫环进程又是怎么启动的呢?
此时咱们跟随源码设计的脚步,继续向下刨一刨 WatchdogManager,发现 WatchdogManager 程序入口 main 函数中,根据传入参数构建了 WatchdogManager 对象,并经过参数获取对应的 XxCommand(似曾相识的感受),而后经过 XxCommand 调用 doWatchdogStart 函数完成服务的启动。
再深刻去跟踪,发现 doWatchdogStart 方法中会调用 WatchdogManager 的 startServer 方法。
那 startServer 方法中又干了什么事情呢?很显然是经过配置找到要启动的子服务,而后调用重载的 startServer 方法。
接着就开始调用 WatchdogChild 的 start 方法,方法干了什么事情呢?
WatchdogChild 的 start 方法中建立了 WatchdogChildTask 任务对象,并调用 start 方法完成任务启动。
其中 WatchdogChildTask 的 start 方法很简单,就是起了一个线程开始跑任务。
重点再一次来临,会发现线程体中建立了一个 WatchdogChildProcess 对象实例,接着调用对象的 run 方法。此处就是保证了 Resin 应用为何一直杀不死的缘由,有个循环调度,一旦子进程有问题,当即再次进行建立子进程。
咱们看看 run 方法的实现,会发现建立了一个 socket 用于通信;而后把端口传入 createProcess 方法构建 Resin 进程对象。
接着会发现 WatchdogProcess 的建立进程的方法 createProcess 中定义要启动的类为 com.caucho.server.resin.Resin;而后封装一系列的参数;紧接着用 ProcessBuilder 完成 Resin 进程的启动。
而后 connectToChild 方法主要用于等待子进程的链接。这不就是大总管开辟的实时通信的端口么!
至此,咱们知道 WatchdogManager 会建立 WatchdogTask 任务,用于建立WatchdogChildProcess 对象,此对象会触发启动 Resin 进程,并提供 socket 通信服务。
那 Resin 进程又干了啥?咱们依然从 main 函数开始看起。一目了然根据传入参数建立 Resin 实例,而后重点关注一下 waitForExit,这个是否是和我们猜想的丫环进程与大总管进程通信不上就退出,是否是这么回事呢?
而后深刻 Resin 的 waitForExit 方法发现经过 pingSocket 参数构建 ResinWaitForExitService,用于启动 ResinActor(这个 Actor 模型前期的分享我们提过一嘴),而后调用 waitForExit 方法。
到这大概也就到了最后,根据蓝色框中的日志输出约莫也就是那么回事儿。
...... 此处省略一万图(不刨了,点到为止)......
至此,丫环进程 Resin 也启动完毕了,而且与父进程创建了实时通信。源码看了七七八八,细枝末节没细谈,主要是能明白梗概,梳理个大体脉略,能大体清晰 Resin 服务启动时启动了 ResinBoot 进程、WatchdogManager 进程、Resin 进程,当进程启动完成后 ResinBoot 进程就正常退出了,因此当咱们用 jps 命令看时,就发现只有 WatchdogManager、Resin 两个进程啦,其中用到的核心技术为 ProcessBuilder、Socket 通信。
好了,能坚持看到这儿的,那绝对都是铁粉,但愿我不是一人在饮酒醉,独醉不如众醉,独乐乐不如众乐乐,但愿这期的分享能帮你打通任督二脉,之后若是真用到时,不妨以本文做为参考,说不定会有点价值。
下期咱们将站在 Resin 的肩膀上进行实战,请各位期待。
推荐阅读: