转自:http://blog.csdn.net/androidlushangderen/article/details/48128955

YARN学习系列:http://blog.csdn.net/Androidlushangderen/article/category/5780183

前言

在之前两周主要学了HDFS中的一些模块知识,其中的许多都或多或少有我们借鉴学习的地方,现在将目光转向另外一个块,被誉为MRv2,就是yarn,在Yarn中,解决了MR中JobTracker单点的问题,将此拆分成了ResourceManager和NodeManager这样的结构,在每个节点上,还会有ApplicationMaster来管理应用程序的整个生命周期,的确在Yarn中,多了许多优秀的设计,而今天,我主要分享的就是这个ApplicationMaster相关的一整套服务,他是隶属于ResoureManager的内部服务中的.了解了AM的启动机制,你将会更进一步了解Yarn的任务启动过程.

ApplicationMaster管理涉及类

ApplicationMaster管理涉及到了4大类,ApplicationMasterLauncher,AMLivelinessMonitor,ApplicationMasterService,以及ApplicationMaster自身类.下面介绍一下这些类的用途,在Yarn中,每个类都会有自己明确的功能模块的区分.

1.ApplicationMasterLauncher--姑且叫做AM启动关闭事件处理器,他既是一个服务也是一个处理器,在这个类中,只处理2类事件,launch和cleanup事件.分别对应启动应用和关闭应用的情形.

2.AMLivelinessMonitor--这个类从名字上可以看出他是监控类,监控的对象是AM存活状态的监控类,检测的方法与之前的HDFS一样,都是采用heartbeat的方式,如果有节点过期了,将会触发一次过期事件.

3.ApplicationMasterService--AM请求服务处理类.AMS存在于ResourceManager,中,服务的对象是各个节点上的ApplicationMaster,负责接收各个AM的注册请求,更新心跳包信息等.

4.ApplicationMaster--节点应用管理类,简单的说,ApplicationMaster负责管理整个应用的生命周期.

简答的描述完AM管理的相关类,下面从源码级别分析一下几个流程.

AM启动

要想让AM启动,启动的背景当然是有用户提交了新的Application的时候,之后ApplicationMasterLauncher会生成Launch事件,与对应的nodemanager通信,让其准备启动的新的AM的Container.在这里,就用到了ApplicationMasterLauncher这个类,之前在上文中已经提到,此类就处理2类事件,Launch启动和Cleanup清洗事件,先来看看这个类的基本变量设置

  1. //Application应用事件处理器
  2. public class ApplicationMasterLauncher extends AbstractService implements
  3. EventHandler<AMLauncherEvent> {
  4. private static final Log LOG = LogFactory.getLog(
  5. ApplicationMasterLauncher.class);
  6. private final ThreadPoolExecutor launcherPool;
  7. private LauncherThread launcherHandlingThread;
  8. //事件队列
  9. private final BlockingQueue<Runnable> masterEvents
  10. = new LinkedBlockingQueue<Runnable>();
  11. //资源管理器上下文
  12. protected final RMContext context;
  13. public ApplicationMasterLauncher(RMContext context) {
  14. super(ApplicationMasterLauncher.class.getName());
  15. this.context = context;
  16. //初始化线程池
  17. this.launcherPool = new ThreadPoolExecutor(10, 10, 1,
  18. TimeUnit.HOURS, new LinkedBlockingQueue<Runnable>());
  19. //新建处理线程
  20. this.launcherHandlingThread = new LauncherThread();
  21. }

还算比较简单,有一个masterEvents事件队列,还有执行线程以及所需的线程池执行环境。在RM相关的服务中,基本都是继承自AbstractService这个抽象服务类的。ApplicationMasterLauncher中主要处理2类事件,就是下面的展示的

  1. @Override
  2. public synchronized void  handle(AMLauncherEvent appEvent) {
  3. AMLauncherEventType event = appEvent.getType();
  4. RMAppAttempt application = appEvent.getAppAttempt();
  5. //处理来自ApplicationMaster获取到的请求,分为启动事件和清洗事件2种
  6. switch (event) {
  7. case LAUNCH:
  8. launch(application);
  9. break;
  10. case CLEANUP:
  11. cleanup(application);
  12. default:
  13. break;
  14. }
  15. }

然后调用具体的实现方法,以启动事件launch事件为例

  1. //添加应用启动事件
  2. private void launch(RMAppAttempt application) {
  3. Runnable launcher = createRunnableLauncher(application,
  4. AMLauncherEventType.LAUNCH);
  5. //将启动事件加入事件队列中
  6. masterEvents.add(launcher);
  7. }

这些事件被加入到事件队列之后,是如何被处理的呢,通过消息队列的形式,在一个独立的线程中逐一被执行

  1. //执行线程实现
  2. private class LauncherThread extends Thread {
  3. public LauncherThread() {
  4. super("ApplicationMaster Launcher");
  5. }
  6. @Override
  7. public void run() {
  8. while (!this.isInterrupted()) {
  9. Runnable toLaunch;
  10. try {
  11. //执行方法为从事件队列中逐一取出事件
  12. toLaunch = masterEvents.take();
  13. //放入线程池池中进行执行
  14. launcherPool.execute(toLaunch);
  15. } catch (InterruptedException e) {
  16. LOG.warn(this.getClass().getName() + " interrupted. Returning.");
  17. return;
  18. }
  19. }
  20. }
  21. }

如果论到事件的具体执行方式,就要看具体AMLauch是如何执行的,AMLauch本身就是一个runnable实例。

  1. /**
  2. * The launch of the AM itself.
  3. * Application事件执行器
  4. */
  5. public class AMLauncher implements Runnable {
  6. private static final Log LOG = LogFactory.getLog(AMLauncher.class);
  7. private ContainerManagementProtocol containerMgrProxy;
  8. private final RMAppAttempt application;
  9. private final Configuration conf;
  10. private final AMLauncherEventType eventType;
  11. private final RMContext rmContext;
  12. private final Container masterContainer;

在里面主要的run方法如下,就是按照事件类型进行区分操作

  1. @SuppressWarnings("unchecked")
  2. public void run() {
  3. //AMLauncher分2中事件分别处理
  4. switch (eventType) {
  5. case LAUNCH:
  6. try {
  7. LOG.info("Launching master" + application.getAppAttemptId());
  8. //调用启动方法
  9. launch();
  10. handler.handle(new RMAppAttemptEvent(application.getAppAttemptId(),
  11. RMAppAttemptEventType.LAUNCHED));
  12. ...
  13. break;
  14. case CLEANUP:
  15. try {
  16. LOG.info("Cleaning master " + application.getAppAttemptId());
  17. //调用作业清洗方法
  18. cleanup();
  19. ...
  20. break;
  21. default:
  22. LOG.warn("Received unknown event-type " + eventType + ". Ignoring.");
  23. break;
  24. }
  25. }

后面的launch操作会调用RPC函数与远程的NodeManager通信来启动Container。然后到了ApplicationMaster的run()启动方法,在启动方法中,会进行应用注册的方法,

  1. @SuppressWarnings({ "unchecked" })
  2. public boolean run() throws YarnException, IOException {
  3. LOG.info("Starting ApplicationMaster");
  4. Credentials credentials =
  5. UserGroupInformation.getCurrentUser().getCredentials();
  6. DataOutputBuffer dob = new DataOutputBuffer();
  7. credentials.writeTokenStorageToStream(dob);
  8. // Now remove the AM->RM token so that containers cannot access it.
  9. Iterator<Token<?>> iter = credentials.getAllTokens().iterator();
  10. while (iter.hasNext()) {
  11. Token<?> token = iter.next();
  12. if (token.getKind().equals(AMRMTokenIdentifier.KIND_NAME)) {
  13. iter.remove();
  14. }
  15. }
  16. allTokens = ByteBuffer.wrap(dob.getData(), 0, dob.getLength());
  17. //与ResourceManager通信,周期性发送心跳信息,包含了应用的最新信息
  18. AMRMClientAsync.CallbackHandler allocListener = new RMCallbackHandler();
  19. amRMClient = AMRMClientAsync.createAMRMClientAsync(1000, allocListener);
  20. amRMClient.init(conf);
  21. amRMClient.start();
  22. .....
  23. // Register self with ResourceManager
  24. // This will start heartbeating to the RM
  25. //启动之后进行AM的注册
  26. appMasterHostname = NetUtils.getHostname();
  27. RegisterApplicationMasterResponse response = amRMClient
  28. .registerApplicationMaster(appMasterHostname, appMasterRpcPort,
  29. appMasterTrackingUrl);
  30. // Dump out information about cluster capability as seen by the
  31. // resource manager
  32. int maxMem = response.getMaximumResourceCapability().getMemory();
  33. LOG.info("Max mem capabililty of resources in this cluster " + maxMem);
  34. // A resource ask cannot exceed the max.
  35. if (containerMemory > maxMem) {
  36. LOG.info("Container memory specified above max threshold of cluster."
  37. + " Using max value." + ", specified=" + containerMemory + ", max="
  38. + maxMem);
  39. containerMemory = maxMem;
  40. }


在这个操作中,会将自己注册到AMLivelinessMonitor中,此刻开始启动心跳监控。

AMLiveLinessMonitor监控

在这里把重心从ApplicationMaster转移到AMLivelinessMonitor上,首先这是一个激活状态的监控线程,此类线程都有一个共同的父类

  1. //应用存活状态监控线程
  2. public class AMLivelinessMonitor extends AbstractLivelinessMonitor<ApplicationAttemptId> {


在AbstractlinessMonitor中定义监控类线程的一类特征和方法

  1. //进程存活状态监控类
  2. public abstract class AbstractLivelinessMonitor<O> extends AbstractService {
  3. private static final Log LOG = LogFactory.getLog(AbstractLivelinessMonitor.class);
  4. //thread which runs periodically to see the last time since a heartbeat is
  5. //received.
  6. //检查线程
  7. private Thread checkerThread;
  8. private volatile boolean stopped;
  9. //默认超时时间5分钟
  10. public static final int DEFAULT_EXPIRE = 5*60*1000;//5 mins
  11. //超时时间
  12. private int expireInterval = DEFAULT_EXPIRE;
  13. //监控间隔检测时间,为超时时间的1/3
  14. private int monitorInterval = expireInterval/3;
  15. private final Clock clock;
  16. //保存了心跳检验的结果记录
  17. private Map<O, Long> running = new HashMap<O, Long>();


心跳检测本身非常的简单,做一次通信记录检查,然后更新一下,记录时间,当一个新的节点加入监控或解除监控操作

  1. //新的节点注册心跳监控
  2. public synchronized void register(O ob) {
  3. running.put(ob, clock.getTime());
  4. }
  5. //节点移除心跳监控
  6. public synchronized void unregister(O ob) {
  7. running.remove(ob);
  8. }


每次做心跳周期检测的时候,调用下述方法

  1. //更新心跳监控检测最新时间
  2. public synchronized void receivedPing(O ob) {
  3. //only put for the registered objects
  4. if (running.containsKey(ob)) {
  5. running.put(ob, clock.getTime());
  6. }
  7. }


非常简单的更新方法,O ob对象在这里因场景而异,在AM监控中,为ApplicationID应用ID。在后面的AMS和AM的交互中会看到。新的应用加入AMLivelinessMonitor监控中后,后面的主要操作就是AMS与AM之间的交互操作了。

AM与AMS

在ApplicationMaster运行之后,会周期性的向ApplicationMasterService发送心跳信息,心跳信息包含有许多资源描述信息。

  1. //ApplicationMaster心跳信息更新
  2. @Override
  3. public AllocateResponse allocate(AllocateRequest request)
  4. throws YarnException, IOException {
  5. ApplicationAttemptId appAttemptId = authorizeRequest();
  6. //进行心跳信息时间的更新
  7. this.amLivelinessMonitor.receivedPing(appAttemptId);
  8. ....


每次心跳信息一来,就会更新最新监控时间。在AMS也有对应的注册应用的方法

  1. //ApplicationMaster在ApplicationMasterService上服务上进行应用注册
  2. @Override
  3. public RegisterApplicationMasterResponse registerApplicationMaster(
  4. RegisterApplicationMasterRequest request) throws YarnException,
  5. IOException {
  6. ApplicationAttemptId applicationAttemptId = authorizeRequest();
  7. ApplicationId appID = applicationAttemptId.getApplicationId();
  8. .....
  9. //在存活监控线程上进行心跳记录,更新检测时间,key为应用ID
  10. this.amLivelinessMonitor.receivedPing(applicationAttemptId);
  11. RMApp app = this.rmContext.getRMApps().get(appID);
  12. // Setting the response id to 0 to identify if the
  13. // application master is register for the respective attemptid
  14. lastResponse.setResponseId(0);
  15. responseMap.put(applicationAttemptId, lastResponse);
  16. LOG.info("AM registration " + applicationAttemptId);
  17. this.rmContext


如果在心跳监控中出现过期的现象,就会触发一个expire事件,在AMLiveLinessMonitor中,这部分的工作是交给CheckThread执行的

  1. //进程存活状态监控类
  2. public abstract class AbstractLivelinessMonitor<O> extends AbstractService {
  3. ...
  4. //thread which runs periodically to see the last time since a heartbeat is
  5. //received.
  6. //检查线程
  7. private Thread checkerThread;
  8. ....
  9. //默认超时时间5分钟
  10. public static final int DEFAULT_EXPIRE = 5*60*1000;//5 mins
  11. //超时时间
  12. private int expireInterval = DEFAULT_EXPIRE;
  13. //监控间隔检测时间,为超时时间的1/3
  14. private int monitorInterval = expireInterval/3;
  15. ....
  16. //保存了心跳检验的结果记录
  17. private Map<O, Long> running = new HashMap<O, Long>();
  18. ...
  19. private class PingChecker implements Runnable {
  20. @Override
  21. public void run() {
  22. while (!stopped && !Thread.currentThread().isInterrupted()) {
  23. synchronized (AbstractLivelinessMonitor.this) {
  24. Iterator<Map.Entry<O, Long>> iterator =
  25. running.entrySet().iterator();
  26. //avoid calculating current time everytime in loop
  27. long currentTime = clock.getTime();
  28. while (iterator.hasNext()) {
  29. Map.Entry<O, Long> entry = iterator.next();
  30. //进行超时检测
  31. if (currentTime > entry.getValue() + expireInterval) {
  32. iterator.remove();
  33. //调用超时处理方法,将处理事件交由调度器处理
  34. expire(entry.getKey());
  35. LOG.info("Expired:" + entry.getKey().toString() +
  36. " Timed out after " + expireInterval/1000 + " secs");
  37. }
  38. }
  39. }


check线程主要做的事件就是遍历每个节点的最新心跳更新时间,通过计算差值进行判断是否过期,过期调用expire方法。此方法由其子类实现

  1. //应用存活状态监控线程
  2. public class AMLivelinessMonitor extends AbstractLivelinessMonitor<ApplicationAttemptId> {
  3. //中央调度处理器
  4. private EventHandler dispatcher;
  5. ...
  6. @Override
  7. protected void expire(ApplicationAttemptId id) {
  8. //一旦应用过期,处理器处理过期事件处理
  9. dispatcher.handle(
  10. new RMAppAttemptEvent(id, RMAppAttemptEventType.EXPIRE));
  11. }
  12. }


产生应用超期事件,然后发给中央调度器去处理。之所以采用的这样的方式,是因为在RM中,所有的模块设计是以事件驱动的形式工作,最大程度的保证了各个模块间的解耦。不同模块通过不同的事件转变为不同的状态,可以理解为状态机的改变。最后用一张书中的截图简单的展示AM模块相关的调用过程。

全部代码的分析请点击链接https://github.com/linyiqun/hadoop-yarn,后续将会继续更新YARN其他方面的代码分析。

参考文献

《Hadoop技术内部–HDFS结构设计与实现原理》.蔡斌等

YARN源码分析(一)-----ApplicationMaster的更多相关文章

  1. Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(一)

    我们知道,如果想要在Yarn上运行MapReduce作业,仅需实现一个ApplicationMaster组件即可,而MRAppMaster正是MapReduce在Yarn上ApplicationMas ...

  2. Yarn源码分析之如何确定作业运行方式Uber or Non-Uber?

    在MRAppMaster中,当MapReduce作业初始化时,它会通过作业状态机JobImpl中InitTransition的transition()方法,进行MapReduce作业初始化相关操作,而 ...

  3. Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(二)

    本文继<Yarn源码分析之MRAppMaster上MapReduce作业处理总流程(一)>,接着讲述MapReduce作业在MRAppMaster上处理总流程,继上篇讲到作业初始化之后的作 ...

  4. Yarn源码分析之事件异步分发器AsyncDispatcher

    AsyncDispatcher是Yarn中事件异步分发器,它是ResourceManager中的一个基于阻塞队列的分发或者调度事件的组件,其在一个特定的单线程中分派事件,交给AsyncDispatch ...

  5. Yarn源码分析之MapReduce作业中任务Task调度整体流程(一)

    v2版本的MapReduce作业中,作业JOB_SETUP_COMPLETED事件的发生,即作业SETUP阶段完成事件,会触发作业由SETUP状态转换到RUNNING状态,而作业状态转换中涉及作业信息 ...

  6. Yarn源码分析1(Hadoop2.7.2)

    在Hadoop中,调度框架YARN(Yet Another Resource Negotiater)是基于事件的,调度的是MapReduce的Application.Application有一系列的状 ...

  7. Yarn源码分析之MRAppMaster:作业运行方式Local、Uber、Non-Uber

    基于作业大小因素,MRAppMaster提供了三种作业运行方式:本地Local模式.Uber模式.Non-Uber模式.其中, 1.本地Local模式:通常用于调试: 2.Uber模式:为降低小作业延 ...

  8. Yarn源码分析之参数mapreduce.job.reduce.slowstart.completedmaps介绍

    mapreduce.job.reduce.slowstart.completedmaps是MapReduce编程模型中的一个参数,这个参数的含义是,当Map Task完成的比例达到该值后才会为Redu ...

  9. YARN DistributedShell源码分析与修改

    YARN DistributedShell源码分析与修改 YARN版本:2.6.0 转载请注明出处:http://www.cnblogs.com/BYRans/ 1 概述 2 YARN Distrib ...

随机推荐

  1. activemq集群搭建Demo

    activemq5.14.5单节点安装Demo 第一步:创建集群目录 [root@node001 ~]# mkdir -p /usr/local/activemqCluster 复制单点至集群目录 [ ...

  2. Maven版本的ssm框架项目常见依赖pom.xml

    <properties> <junit.version>4.12</junit.version> <spring.version>4.3.1.RELEA ...

  3. WinKawaks如何载入游戏

    1 把下载的游戏放入roms目录 2 运行Kawaks,点击载入游戏,勾选"仅存在的",点击扫描所有游戏.然后会出现所有名字正确的游戏.你下载的zip如果有中文或者名字不对,都是无 ...

  4. c++中的对象复制

    (1)this指针 this是一个隐含于每个类的成员函数的特殊指针,该指针是一个指向正在被某个成员函数操作的对象的指针. 当一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,也就是说,当 ...

  5. SecureCRT 默认配置

    1.配置默认设置

  6. RxJava API使用示例

    概述 RxJava API示例代码,可离线查看rxjava1.0大部分API的marble图,描述,示例代码,并支持示例代码实时输出及展示执行结果. 详细 代码下载:http://www.demoda ...

  7. ip范围生成 C#

    #region ip /// <summary> /// ip rang ,ip /// </summary> /// <param name="str&quo ...

  8. java基础讲解07-----数组

    1.什么是数组 2.怎么使用数组 package test; public class ShuZu {            public static void main(String[] args ...

  9. MDK5.00中*** error 65: access violation at 0xFFFFFFFC : no 'write' permission的一种解决方法

    http://blog.csdn.net/coderfun/article/details/9417289 这是在调试过程中的修改方法,所以在每次运行的时候,都要设置. 先进入调试模式(crtl+F5 ...

  10. .net 非阻塞事件获取返回异步回调结果

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...