ApplicationMaster 部分,这里是整个分布式程序的指挥中心, 为了追寻大神们的脚步,我们在源码里面添加了一些日志提示 ,跟着运行的轨迹,我们来翻开Master部分的迷雾。
且说上回我们说到 Client在申请了一个 Container 之后便把我们的jar提交到容器中执行了,接下来事情交给我们的 ApplicationMaster 了,我们在 main方法里面添加我们的日志信息:
图一:在Master主入口添加日志信息
我们打包重新执行,找到这段日志的输出:
图二:在Master主入口处的日志信息
是的,这里我们看到了我们的 Master 程序以一种近乎平淡方式出场:执行main函数,然后带几个参数,不一样的是程序被分配到了01节点执行,和客户端申请的Container对应,然后获取了容器相关的参数。
接下来,我们找到main函数中的执行过程init->run->finished,这种一点就通透的逻辑果然印证了是最最简单的Yarn程序...好吧我们假装若有所思地去看看run里面具体做的内容:
图三:超级简单的三个流程,目测不用解释太多
根据我们之前的经验也可以了解到,主要流程会在run中执行,按照顺序下去,我们在关键的地方添加日志:
图四:获取客户端的过程
再次打包我们的程序:
图五:申请资源后的输出日志
好,到了这一步,我们已经能够知道 Master程序会和RM和NM通信,继续往下读,我们找到了又一个关键逻辑:
图六:发现程序申请了容器,天啊
通过和RM通信,我们在脚本中给出的参数 num_containers 3,在这一步中被使用,找RM申请了三份容器,我们在程序执行之后看到:
图七:申请容器的证据
我们继续跟踪接下来的逻辑,陆续做了这些工作:
Master 程序在获取到 Container 之后,会把我们脚本执行时候的参数交给 Container,同时把 Container 环境变量和参数设置好,最后,执行,我们在关键的地方添加信息:
图八:申请容器之后往容器里面塞东西
我们在添加信息之后再yarn上面执行一下,看下具体的内容:
图九:容器里面塞的内容,我们输出
我们按照同样的方式输入我们的 Contain信息:
图十:命令不断有变化去尝试,我们看到了变化
看到了么,我们在脚本中要执行的命令,被申请到的容器作为参数执行了 。接下来的内容,我们在前面也知道,我们传递给 Container的命令会被在对应的机器中执行:
图十一:在各个容器中执行的内容
程序最终分配到我们节点中执行了,后续工作是获取监控信息和完成作业的工作,看日志也很明确。
我们把逻辑整理出来: Client提交->Master申请Container->NM中执行。 DistributedShell 程序是Hadoop的开发人员给我们提供的最简化的 Yarn 程序。在这个简化的程序中,我们本应该写计算逻辑的那部分程序被简化成了一个简单的linux 命令,即便如此,在对应的各个 Container 中执行命令的这个过程仍然是完整的一个进程,应该做计算逻辑流程控制的主控程序简化成了直接获取容器调用,对应我们的 ApplicationMaster。好,到了这一步,我们回想一下我们原本的分布式程序的样子,我们是把整个计算工作拆分到了很多台机器上面,再把汇总的结果进行输出 ,我们需要不断去操作各个机器,把计算分发过去,还要控制我们不同节点做不同的工作。但是,有了Yarn程序,我们的故事发生了一点点改变。
我们回想我们之前分析程序的运行过程,Client 从 RM端获得了 Container 执行了 Master,同时我们的 Master 程序在计算过程中不断重RM中获取 Container 执行程序。是的,在这一步,在我们有了 Yarn之后, 我们已经没有去处理具体节点机器之类的信息了,我们的计算单元被 Yarn划分成了一个个 Container,我们只需要不断从RM中获取到 Container,接下来的事情呢,我们把真正要计算的作业,替换掉我们写shell命令的那部分工作,作为我们的Container的启动命令。发现了吗,当我们这样去做到时候,我们的程序开始有了质的飞越,原本我们单机下面玩的程序,通过我们的Yarn,庞大的计算量被拆分成一个个Container的计算单元去执行,这些运行的程序统一被我们的 Yarn服务分配、监控。我们更加像是开发一个简单顺序执行的小程序一样去开发一个海量数据运算的程序,我们只需要简单的把作业统一启动运行就完成了。
经过了那么多,我们在看看官网给出的结构图:
图十二:官网中的Yarn工作流程
现在再看图就比较清楚了,我们再考虑我们的Yarn上面的工作流程:
NodeManager是我们容器的载体,类比我们之前的商场中的每一个楼层 。每一个NodeManager中有一定数量的资源对应我们的vcores、memory,类比我们商场中里面的店铺,每一个店铺都是有一定面积和空间的 。这种资源会被统一登记在ResourceManager中,类比我们的商店也是会有个大房东,他管着这些店铺。我们从Yarn的面板中看到的资源情况就是:
图十三:Yarn上面的资源
那么Container怎么解释呢,我们从之前的程序运行过程了解到,Container实际就是我们的一个程序运行的单元,这部分单元拥有一定的cpu和内存,我们在里面需要给定具体的执行命令和环境变量我们就可以在上面执行程序,类比的话自然是我们商铺里面一间一间的屋子啦,我们对他装修改造一下,再挂个招牌,里面就会变成一家店铺啦至于具体卖什么,还是取决我们给里面放了啥。这里很多小伙伴肯定联想到了Linux 的Cgroup和Docker之类的东西。实际情况是:在Yarn的具体实现中,Container只是里面对于的一个序列化类而已,这个类可以用于执行我们的脚本,回想一下我们在java中的Runtime.exec()。一开始yarn上面的程序只是用来运行mapreduce所以只是逻辑上面的隔离的概念。不过正因为container在概念上是和资源合理概念是一致的,yarn也已经实现了Yarn on Docker的运行方式^^,嗯,容器就这样子达成一致了。
NodeManager中可以有若干的资源也就是我们的Container。点线的箭头,就是我们的申请资源的动作,资源会被我们的Master和Client申请,需要运行jar之类程序的,都可以申请,值得强调的是,资源都是从RM进行申请,申请完成之后呢,作业的提交也是提交给RM的,由RM调度NodeManager进行执行,RM显然是Yarn 体系的老大,而NM呢就是计算单元,可以不断横向扩展。
似乎我们没有太主动去写Yarn程序什么的。是的,实际的情况,对于我们常见的mapreduce、spark、storm、hbase常见的这些要运行在yarn上面社区都有相应的实现的,这部分工作不用我们去做的,但是如果要实现自己的web程序或者某些服务化的程序在yarn上面,就自己可以去实现,yarn提供的api非常开放,是完全可以这样去做的 。
从下篇文章开始我们去使用我们的yarn运行各种程序。敬请期待~