Hadoop YARN 框架 Resource Management 详解

一、概述

    本文将介绍ResourceManager在Yarn中的功能作用,从更细的粒度分析RM内部组成的各个组件功能和他们相互的交互方式。

二、ResourceManager的交互协议与基本职能

1、ResourceManager交互协议

    在整个Yarn框架中主要涉及到7个协议,分别是ApplicationClientProtocol、MRClientProtocol、ContainerManagementProtocol、ApplicationMasterProtocol、ResourceTracker、LocalizationProtocol、TaskUmbilicalProtocol,这些协议封装了各个组件交互的信息。ResourceManager现实功能需要和NodeManager以及ApplicationMaster进行信息交互,其中涉及到的RPC协议有ResourceTrackerProtocol、ApplicationMasterProtocol和ResourceTrackerProtocol。

  • ResourceTracker

    NodeManager通过该协议向ResourceManager中注册、汇报节点健康情况以及Container的运行状态,并且领取ResourceManager下达的重新初始化、清理Container等命令。NodeManager和ResourceManager这种RPC通信采用了和MRv1类似的“pull模型”(ResourceManager充当RPC server角色,NodeManager充当RPC client角色),NodeManager周期性主动地向ResourceManager发起请求,并且领取下达给自己的命令。

  • ApplicationMasterProtocol

    应用程序的ApplicationMaster同过该协议向ResourceManager注册、申请和释放资源。该协议和上面协议同样也是采用了“pull模型”,其中在RPC机制中,ApplicationMaster充当RPC client角色,ResourceManager充当RPC server角色。

  • ApplicationClientProtocol

  • 客户端通过该协议向ResourceManager提交应用程序、控制应用程序(如杀死job)以及查询应用程序的运行状态等。在该RPC 协议中应用程序客户端充当RPC client角色,ResourceManager充当RPC server角色。

整理一下ResourceManager与NodeManager、ApplicationMaster和客户端RPC协议交互的信息:

wKioL1O-VNujpZZVAAHCkb1a3G0251.jpg

上图中的ResourceTrackeServer、ApplicationMasterService 、ClientRMServer是ResourceManager中处理上述功能的组件。

1、ResourceManager基本职能

ResourceManager基本职能概括起来就以下几方面:

  • 与客户端进行交互,处理来自于客户端的请求,如查询应用的运行情况等。

  • 启动和管理各个应用的ApplicationMaster,并且为ApplicationMaster申请第一个Container用于启动和在它运行失败时将它重新启动。

  • 管理NodeManager,接收来自NodeManager的资源和节点健康情况汇报,并向NodeManager下达管理资源命令,例如kill掉某个container。

  • 资源管理和调度,接收来自ApplicationMaster的资源申请,并且为其进行分配。这个是它的最重要的职能。


三、ResourceManager内部组成架构分析

    ResourceManager在底层代码实现上将各个功能模块分的比较细,各个模块功能具有很强的独立性。下图所示的是ResourceManager中的大概的功能模块组成:

wKioL1O_joPj4oBtAAOzwfCDdiQ566.jpg

1、用户交互模块

    用户交互模块即上图显示的User Service管理模块。在这里边还可以看到根据不同的用户类型启用了不同的服务进行处理,AdminService处理管理员相关请求,ClientRMService处理普通客户相关请求,这样使得管理员不会因为普通客户请求太多而造成堵塞。下面看看这2个服务的具体实现代码:

  • ClientRMService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public  class  ClientRMService  extends  AbstractService  implements
     ApplicationClientProtocol {
   private  static  final  ArrayList<ApplicationReport> EMPTY_APPS_REPORT =  new  ArrayList<ApplicationReport>();
 
   private  static  final  Log LOG = LogFactory.getLog(ClientRMService. class );
 
   final  private  AtomicInteger applicationCounter =  new  AtomicInteger( 0 );
   final  private  YarnScheduler scheduler; //调度器
   final  private  RMContext rmContext; //RM上下文对象,其包含了RM大部分运行时信息,如节点列表、队列列表、应用程序列表等
   private  final  RMAppManager rmAppManager; //app管理对象
 
   private  Server server; //一个RPC Server
   protected  RMDelegationTokenSecretManager rmDTSecretManager;
 
   private  final  RecordFactory recordFactory = RecordFactoryProvider.getRecordFactory( null );
   InetSocketAddress clientBindAddress;
   //访问控制对象,例如,一些应用程序在提交时设置了查看权限的话,其他普通用户就无法查看。
   private  final  ApplicationACLsManager applicationsACLsManager;
   private  final  QueueACLsManager queueACLsManager;
   ......
    @Override
   protected  void  serviceStart()  throws  Exception {
     Configuration conf = getConfig();
     YarnRPC rpc = YarnRPC.create(conf);
     this .server =    //实现RPC协议ApplicationClientProtocol 
       rpc.getServer(ApplicationClientProtocol. class this ,
             clientBindAddress,
             conf,  this .rmDTSecretManager,
             conf.getInt(YarnConfiguration.RM_CLIENT_THREAD_COUNT, 
                 YarnConfiguration.DEFAULT_RM_CLIENT_THREAD_COUNT));
     
     // Enable service authorization?
     if  (conf.getBoolean(
         CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION, 
         false )) {
       refreshServiceAcls(conf,  new  RMPolicyProvider());
     }
     
     this .server.start();
     ......
   }

从上面ClientRMService的基本代码架构我们可以看出:

(1)ClientRMService是一个RPC Server,主要为来自于普通客户端的各种RPC请求。从代码实现的角度看,它是ApplicationClientProtocol协议的一个实现。

(2)之前我们已经说了,普通用户可以通过该服务来获得正在运行应用程序的相关信息,如进度情况、应用程序列表等。上面代码中都将ResourceManager运行信息封装在RMContxt接口中了,下面来看看这个接口的一个实现对象RMContextImpl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public  class  RMContextImpl  implements  RMContext {
   //中央异步调度器。RM中的各个服务和组件以及它们处理和输出的事件类型都是通过中央异步调度器组织在一起的,这样可以有效提高系统的吞吐量。
   private  final  Dispatcher rmDispatcher;
 
   private  final  ConcurrentMap<ApplicationId, RMApp> applications //应用程序列表
     new  ConcurrentHashMap<ApplicationId, RMApp>();
 
   private  final  ConcurrentMap<NodeId, RMNode> nodes //节点列表
     new  ConcurrentHashMap<NodeId, RMNode>();
   
   private  final  ConcurrentMap<String, RMNode> inactiveNodes //非活跃节点列表
     new  ConcurrentHashMap<String, RMNode>();
   //正在运行中的AP心跳监控对象
   private  AMLivelinessMonitor amLivelinessMonitor; //正在运行中的AP心跳监控对象
   //运行完毕后的AM心跳监控对象
   private  AMLivelinessMonitor amFinishingMonitor;
   //用于存储ResourceManager运行状态
   private  RMStateStore stateStore =  null ;
   //用于Container的超时监控,应用程序必须在一定时间内(默认10Min)使用分配到的Container去运行task,否则会被回收
   private  ContainerAllocationExpirer containerAllocationExpirer;
   //下面变量都是与安全管理相关的对象
   private  final  DelegationTokenRenewer delegationTokenRenewer;
   private  final  AMRMTokenSecretManager amRMTokenSecretManager;
   private  final  RMContainerTokenSecretManager containerTokenSecretManager;
   private  final  NMTokenSecretManagerInRM nmTokenSecretManager;
   private  final  ClientToAMTokenSecretManagerInRM clientToAMTokenSecretManager;
   private  ClientRMService clientRMService;
   private  RMDelegationTokenSecretManager rmDelegationTokenSecretManager;
   ......
   }
  • AdminService

AdminService和ClientRMService一样都是作为RPC的服务端,它针对的处理管理员RPC请求,负责访问权限的控制,中Yarn中管理员权限的设定可以在yarn-site.xml中yarn.admi.acl项进行设置,该项的默认值是*,也就是说如果不进行设置的话就当所有的用户都是管理员。从代码上看,它是ResourceManagerAdministrationProtocol协议的一个实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  class  AdminService  extends  AbstractService  implements  ResourceManagerAdministrationProtocol {
 
   private  static  final  Log LOG = LogFactory.getLog(AdminService. class );
 
   private  final  Configuration conf;
   private  final  ResourceScheduler scheduler;
   private  final  RMContext rmContext;
   private  final  NodesListManager nodesListManager;
   
   private  final  ClientRMService clientRMService;
   private  final  ApplicationMasterService applicationMasterService;
   private  final  ResourceTrackerService resourceTrackerService;
   
   private  Server server;
   private  InetSocketAddress masterServiceAddress;
   private  AccessControlList adminAcl;
   
   private  final  RecordFactory recordFactory = 
     RecordFactoryProvider.getRecordFactory( null );
     .....
     }

AdminService代码和ClientRMService比较相似,它各类功能对象也差不多。

2、NodeManager管理

    NodeManager主要是通过NMLivelinessMonitor、ResourceTrackerService和NodeListManager这3大组件来对NodeManager的生命周期、心跳处理以及黑名单处理。

(1)ResourceTrackerService

    ResourceTrackerService是RPC协议ResourceTracker的一个实现,它作为一个RPC Server端接收NodeManager的RPC请求,请求主要包含2种信息,注册NodeManager和处理心跳信息。NodeManger启动时第一件事就是像ResourceManager注册,注册时NodeManager发给ResourceTrackerService的RPC包主要包含NodeManager所在节点的可用资源总量、对外开放的htpp端口、节点的host和port等信息,具体代码看ResourceTrackerService#registerNodeManager方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@SuppressWarnings ( "unchecked" )
   @Override
   public  RegisterNodeManagerResponse registerNodeManager(
       RegisterNodeManagerRequest request)  throws  YarnException,
       IOException {
 
     NodeId nodeId = request.getNodeId(); //从NodeManager带来的NodeID
     String host = nodeId.getHost(); //NodeManager所在节点的host
     int  cmPort = nodeId.getPort();  //NodeManager所在节点的port
     int  httpPort = request.getHttpPort(); //对外开放的http端口
     Resource capability = request.getResource(); //获得NodeManager所在节点的资源上限
 
     RegisterNodeManagerResponse response = recordFactory
         .newRecordInstance(RegisterNodeManagerResponse. class );
 
     // Check if this node is a 'valid' node
     //检测节点host名称的的合法性
     if  (! this .nodesListManager.isValidNode(host)) {
       String message =
           "Disallowed NodeManager from  "  + host
               ", Sending SHUTDOWN signal to the NodeManager." ;
       LOG.info(message);
       response.setDiagnosticsMessage(message);
       response.setNodeAction(NodeAction.SHUTDOWN);
       return  response;
     }
     .....
     }

ResourceTrackerService另外一种功能就是处理心跳信息了,当NodeManager启动后,它会周期性地调用RPC函数ResourceTracker#nodeHeartbeat汇报心跳,心跳信息主要包含该节点的各个Container的运行状态、正在运行的Application列表、节点的健康状况等,随后ResourceManager为该NodeManager返回需要释放的Container列表、Application列表等信息。其中心跳信息处理的流程:首先,从NodeManager发来的心跳包中获得节点的状态状态信息,然后检测该节点是否已经注册过,然后检测该节点的host名称是否合法,例如是否在excluded列表中,然后再检测该次心跳是不是第一次心跳信息,这点非常重要,因为关系到心跳的重复发送与应答的相关问题。其实ResourceTrackerService和NodeManager的心跳处理机制和之前Hadoop1.x中的JobTracker与TaskTacker之间的心跳处理很想象,具体请看我之前写的一篇blog:http://zengzhaozheng.blog.51cto.com/8219051/1359887 ,再然后,为NodeManager返回心跳应答信息,最后,想RMNode发送该NodeManager的状态信息并且保存最近一次心跳应答信息。再具体看看ResourceTracker#nodeHeart方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
  public  NodeHeartbeatResponse nodeHeartbeat(NodeHeartbeatRequest request)
       throws  YarnException, IOException {
     //从RPC Clinet中获得nodeManager所在节点的健康状况
     NodeStatus remoteNodeStatus = request.getNodeStatus();
     /**
      * Here is the node heartbeat sequence...
      * 1. Check if it's a registered node
      * 2. Check if it's a valid (i.e. not excluded) node 
      * 3. Check if it's a 'fresh' heartbeat i.e. not duplicate heartbeat 
      * 4. Send healthStatus to RMNode
      */
 
     NodeId nodeId = remoteNodeStatus.getNodeId();
 
     // 1. Check if it's a registered node
     RMNode rmNode =  this .rmContext.getRMNodes().get(nodeId);
     if  (rmNode ==  null ) {
       /* node does not exist */
       String message =  "Node not found resyncing "  + remoteNodeStatus.getNodeId();
       LOG.info(message);
       resync.setDiagnosticsMessage(message);
       return  resync;
     }
 
     // Send ping
     this .nmLivelinessMonitor.receivedPing(nodeId);
 
     // 2. Check if it's a valid (i.e. not excluded) node
     if  (! this .nodesListManager.isValidNode(rmNode.getHostName())) {
       String message =
           "Disallowed NodeManager nodeId: "  + nodeId +  " hostname: "
               + rmNode.getNodeAddress();
       LOG.info(message);
       shutDown.setDiagnosticsMessage(message);
       this .rmContext.getDispatcher().getEventHandler().handle(
           new  RMNodeEvent(nodeId, RMNodeEventType.DECOMMISSION));
       return  shutDown;
     }
     
     // 3. Check if it's a 'fresh' heartbeat i.e. not duplicate heartbeat
     NodeHeartbeatResponse lastNodeHeartbeatResponse = rmNode.getLastNodeHeartBeatResponse();
     if  (remoteNodeStatus.getResponseId() +  1  == lastNodeHeartbeatResponse
         .getResponseId()) {
       LOG.info( "Received duplicate heartbeat from node "
           + rmNode.getNodeAddress());
       return  lastNodeHeartbeatResponse;
     else  if  (remoteNodeStatus.getResponseId() +  1  < lastNodeHeartbeatResponse
         .getResponseId()) {
       String message =
           "Too far behind rm response id:"
               + lastNodeHeartbeatResponse.getResponseId() +  " nm response id:"
               + remoteNodeStatus.getResponseId();
       LOG.info(message);
       resync.setDiagnosticsMessage(message);
       // TODO: Just sending reboot is not enough. Think more.
       this .rmContext.getDispatcher().getEventHandler().handle(
           new  RMNodeEvent(nodeId, RMNodeEventType.REBOOTING));
       return  resync;
     }
 
     // Heartbeat response
     NodeHeartbeatResponse nodeHeartBeatResponse = YarnServerBuilderUtils
         .newNodeHeartbeatResponse(lastNodeHeartbeatResponse.
             getResponseId() +  1 , NodeAction.NORMAL,  null null null null ,
             nextHeartBeatInterval);
     rmNode.updateNodeHeartbeatResponseForCleanup(nodeHeartBeatResponse);
 
     populateKeys(request, nodeHeartBeatResponse);
 
     // 4. Send status to RMNode, saving the latest response.
     this .rmContext.getDispatcher().getEventHandler().handle(
         new  RMNodeStatusEvent(nodeId, remoteNodeStatus.getNodeHealthStatus(),
             remoteNodeStatus.getContainersStatuses(), 
             remoteNodeStatus.getKeepAliveApplications(), nodeHeartBeatResponse));
 
     return  nodeHeartBeatResponse;
   }

(2)NodeListManager

    NodeListManager主要分管黑名单(include列表)和白名单(exlude列表)管理功能,分别有yarnresouecemanager.nodes.include-path和yarnresourcemanager.nodes.exclude-path指定。黑名单列表中的nodes不能够和RM直接通信(直接抛出RPC异常),管理员可以对这两个列表进行编辑,然后使用$HADOOP_HOME/bin/yarn rmadmin-refreshNodes动态加载修改后的列表,使之生效。

(3)NMLivelinessMonitor

    NMLivelinessMonitor主要是分管心跳异常请求。该服务会周期性地遍历集群中的所有NodeManager,如果某个NodeManager在一定时间内(默认10min,可以有参数yarn.nm.liveness-monitor.expiry-interval-ms配置)没有进行心跳汇报,那么则认为它已经死掉,同时在该节点上运行的Container也会被置为运行失败释放资源。那么这些被置为失败的Container是不会直接被RM分配执行的,RM只是负责将这些被置为失败的Container信息告诉它们所对应的ApplicationMaster,需不需要重新运行它说的算,如果需要从新运行的话,该ApplicationMaster要从新向RM申请资源,然后由ApplicationMaster与对应的NodeManager通信以从新运行之前失败的Container。

2、ApplicationMaster管理模块

2、ApplicationMaster管理模块

    ApplicationMaster的管理主要是用过ResouceManager内部的3个组件来完成:ApplicationMasterLauncher、AMLivelinessMonitor、ApplicationMasterService。

(1)先说说ApplicationMaster和ResourceManager整个的交互流程:

步骤一:

    当ResourceManager接收到客户端提交应用程序请求时就会立马向资源管理器申请一个资源用于启动该应用程序所对应的ApplicationMaster,申请到资源后由ApplicationMasterLaucher与对应的NodeManager进行通信,要求该NodemManager在其所在节点启动该ApplicationMaster。

步骤二:

    ApplicationMaster启动完毕后,ApplicationMasterLuacher通过事件的形式将刚刚启动的ApplicationMaster注册到AMLivelinessMonitor,以启动心跳监控。

步骤三:

    ApplicationMaster启动后主动向ApplicationMasterService注册,并将自己所在host、端口等信息向其汇报。

步骤四:

    ApplicationMaster在运行的过程中不断向ApplicationMasterService发送心跳。

步骤五:

    ApplicationMasterService每次收到ApplicationMaster的心跳信息后,会同时AMLivelinessMonitor更新其最近一次发送心跳的时间。

步骤六:

    当应用程序运行完毕后,ApplicationMaster向ApplicationMasterService请求注销自己。

步骤七:

    ApplicationMasterService收到注销请求后,会将该应用程序的运行状态标注为完成,并且同时AMLivelinessMonitor移除对该ApplicationMaster的心跳监控。

(2)内置管理组件的详细说明

    这里展开说说这3个组件的一些运行机理。

  • ApplicationMasterLaucher

    ApplicationMasterLaucher是以线程池方式实现的一个事件处理器,其主要处理AMLaucherEvent类型的事件,包括启动(LAUNCH)和清除(CLEANUP)一个ApplicationMaster的事件。

    当接收到LAUNCH类型的事件,ApplicationMasterLaucher立马会和对应的NodeManager进行通信,并且带上启动该ApplicationMaster所需要的各种信息,包括:启动命令、JAR包、环境变量等信息。NodeManager接收到来自ApplicationMasterLaucher的启动命令就会启动ApplicationMaster。

    当接收到CLEANUP类型事件,ApplicationMasterLaucher立马会和对应的NodeManager进行通信,要求NodeManager杀死该ApplicationMaster,并释放掉资源。

  • AMLivelinessMonitor

    AMLivelinessMonitor的功能和NMLivelinessMonitor的功能几乎一样,只不过AMLivelinessMonitor监控的是ApplicationMaster,而NMLivelinessMonitor监控的是NodeManager。

    AMLivelinessMonitor会周期性地遍历集群中的所有ApplicationMaster,如果某个ApplicationMaster在一定时间内(默认10min,可以有参数yarn.am.liveness-monitor.expiry-interval-ms配置)没有进行心跳汇报,那么则认为它已经死掉,同时该ApplicationMaster关联运行的Container也会被置为运行失败释放资源。如果Application运行失败,则有RresourceManager重新为它申请资源,并且在另外的节点上启动它(AM启动尝试次数由参数yarn.resourcemanager.am.max-attempts控制,默认2)。那么这些被置为失败的Container是不会直接被RM分配执行的,RM只是负责将这些被置为失败的Container信息告诉它们所对应的ApplicationMaster,需不需要重新运行它说的算,如果需要从新运行的话,该ApplicationMaster要从新向RM申请资源,然后由ApplicationMaster与对应的NodeManager通信以从新运行之前失败的Container。

  • ApplicationMasterService

    ApplicationMasterService的主要职能是处理来自ApplicationMaster的心跳请求,另外也还处理Application的注册和清理请求。注册是Application启动完成后发生的,它向ApplicationMasterService发送注册请求包,包含:ApplicationMaster所在的节点、RPC端口、tracking url等信息。

    处理心跳信息是周期型行为,只要ApplicationMaster还在运行此类请求都会发生。ApplicationMaster向ApplicationMasterService发送的心跳请求包,包含信息:请求资源类型的描述、待释放的container列表等。ApplicationMasterService返回的心跳应答信息包含:ApplicationMasterService为之分配的Container、失败的Container等信息。

    清理请求是在ApplicationMaster运行完毕后,向ApplicationMasterService发送的,主要是叫其回收释放资源。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值