本文档从代码出发,分析了 YARN 中 Container 启动的整个过程,希望给出这个过程的一个整体的概念。
文档分为两个部分:第一部分是全局,从头至尾地把 Container 启动的整个流程串联起来;第二部分是细节,简要分析了 Container 启动流程中涉及到的服务、接口和类。
注意:
AM 与 NM 们通过 NMClientAsync 通信,后者需要调用方提供一个回调类,NM 会在合适的时机调用回调类中的方法来通知 AM 。回调类 被AM实现为 NMCallbackHandler ,其中最重要的两个函数是:
AM 与 RM 通过 AMRMClientAsync 通信。
首先,通过 AMRMClientAsync.registerApplicationMaster() 向 RM 注册自己。
然后 AM 开始提交对 Container 的需求,在申请到需要数量的Container之前,先调用 setupContainerAskForRM()
设置对 Container 的具体需求(优先级、资源等),然后调用 AMRMClientAsync.addContainerRequest()
把需求提交给 RM ,最终该方法会把需求存到一个集合(AMRMClient.ask
)里面。
AMRMClientAsync
同样需要调用方提供一个回调类,AM 实现为 RMCallbackHandler
。这个回调类主要实现了两个方法:
onContainersAllocated()
, 获得新申请的 Container ,创建一个新线程,设置 ContainerLaunchContext , 最终调用 NMClientAsync.startContainerAsync()
来启动 Container。onContainersCompleted()
, 检查已完成的 Container 的数量是否达到了需求,没有的话,继续添加需求。AM 需要与 RM 进行心跳,对于使用了 AMRMClientAsync
的 AM (如 DistributedShell ),心跳是通过 AMRMClientAsync
的一个线程实现。最终调用了 AMRMClientImpl.allocate()
,其主要动作就是从 ask
集合中拿到 Container 需求,组装成一个 AllocateRequest
,再通过 RPC 调用 RM 的相关方法进行申请。
具体一点,是通过ApplicationMasterProtocol.allocate()
,用 「Google protocol Buffer」格式进行 RPC 调用。
总结上面说的,AM 有三个主要流程与 Container 的创建密切相关,这两个流程并行:
分析清楚了这三个主流程,也就清楚了 YARN Container 的启动逻辑。
再看 RM 这边,在 AM 向 RM 注册时,RM 最终会生成一个代表这个 APP 的实例,我们先不分析注册的具体过程,只要知道在我们的情景下,最终是生成了一个 FicaSchedulerApp 。
AM 与 RM 进行心跳,发送的信息中含有:
ApplicationMasterService 是 RM 的一个组成部分。RM启动时,会初始化这个服务,并根据配置,把相应的调度器 YarnScheduler 传进来。它实现了 ApplicationMasterProtocol 接口,负责对来自 AM 的 RPC 请求进行回应。在我们的情景中, ApplicationMasterService.allocate() 方法会被调用,核心逻辑是:
我们使用的是 FIFO 调度器,FifoScheduler.allocate() 方法的主要做两件事情:
FicaSchedulerApp.newlyAllocatedContainers 这个数据结构中存放的,正是最近申请到的 Container 。那么,这个 List 中的元素是怎么来的呢,这要从 NM 的心跳说起。
NM 需要和 RM 进行心跳,让 RM 更新自己的信息。心跳的信息包含:
NM 启动时会向 RM 注册自己,RM 生成对应的 RMNode 结构,代表这个 NM ,存放了这个 NM 的资源信息以及其他一些统计信息。
负责具体心跳的,在 NM 这边是 NodeStatusUpdater 服务,在 RM 那边则是 ResourceTrackerService 服务。心跳的信息包括这个 NM 的状态,其中所有 Container 的状态等。
心跳最终通过 RPC 调用到了 ResourceTrackerService.nodeHeartbeat() 。其核心逻辑就是触发一个 RMNodeStatusEvent(RMNodeEventType.STATUS_UPDATE)
事件,这个事件由 NM 注册时生成的 RMNode 处理。
RMNode 接收 RMNodeStatusEvent(RMNodeEventType.STATUS_UPDATE)
消息,更新自己的状态机,然后调用 StatusUpdateWhenHealthyTransition.transition ,该方法从参数中获得这个 NM 所有的 Container 的信息,根据其状态分成两组:a) 刚申请到还未使用的,b) 运行完毕需要回收的,这两组 Container 的信息存放在 RMNode 的一个队列中。接着,发出一个消息: NodeUpdateSchedulerEvent(SchedulerEventType.NODE_UPDATE)
。这个消息,由调度器处理。
NODE_UPDATE
消息RM 接收到 NM 的心跳后,会发出一个 SchedulerEventType.NODE_UPDATE
的消息,改消息由调度器处理。FifoScheduler 接收到这个消息后,调用了 FifoScheduler.nodeUpdate() 方法。与 Container 申请相关的主要逻辑如下:
从 RMNode 中获取出那些「刚申请还未使用」的 Container (NM 与 RM 心跳是获得),发出消息:RMContainerEventType.LAUNCHED
,该消息由 RMContainer 处理;
从 RMNode 中获取出那些「已经使用完待回收」的 Container,进行回收(具体回收过程略);
在这个 NM 上申请新的 Container:
FicaSchedulerApp.getResourceRequest()
拿到资源请求(ResourceRequest)FicaSchedulerApp.allocate()
,根据传进来的参数,封装出一个 RMContainer 添加到 newlyAllocatedContainers
中。然后触发事件 RMContainerEventType.START
。该事件之后会由 RMContainer 处理。FicaSchedulerNode.allocateContainer()
RMContainer 对 RMContainerEventType 事件进行处理处理:
RMContainerEventType.START
: 状态从 NEW 变为 ALLOCATED,最终触发事件 RMAppAttemptEvent(type=CONTAINER_ALLOCATED)
, 改事件由 RMAppAttemptImpl
处理。RMContainerEventType.LAUNCHED
: 状态从 ACQUIED 变为 RUNNING 。RMAppAttemptImpl 对 RMAppAttemptEvent 事件进行处理,该事件告诉就是告诉 AppAttempt ,你这个APP有 Container 申请好了,AppAttempt 检查自己的状态,如果当前还没有运行 AM ,就把这个 Container 拿来运行 AM。
到此,我们已经理清楚了 FicaSchedulerApp.newlyAllocatedContainers 中元素的来源,也就理清楚了,AM 与 RM 心跳中获得的那些「新申请」的 Container 的来源。
回顾一下:
AM的三个主流程
总结上面说的,AM 有三个主要流程与 Container 的创建密切相关,这两个流程并行:
- 提交需求,通过心跳,把需求发送给 RM;
- 获取Container,通过心跳,拿到申请好的 Container;
- 每申请到一个 Container ,与 NM 通信,启动这个Container;
基于上面的分析,第1,2两个流程已经清楚。下面我们来具体看看 NM 具体是怎么启动一个 Container的。
AM 设置好 ContainerLaunchContext , 调用 NMClientAsync.startContainerAsync() 启动Container。
NMClientAsync 中有一个名叫 events 的事件队列,同时,NMClientAsync 还启动这一个线程,不断地从 events 中取出事件进行处理。
startContainerAsync() 方法被调用时,会生成一个 ContainerEvent(type=START_CONTAINER)
事件放入 events 队列。对于这个事件,处理逻辑是调用 NMClient.startContainer() 同步地启动 Container ,然后调用回调类中的 onContainerStarted() 方法。
NMClient 最终会调用 ContainerManagementProtocol.startContainers() ,以 Google Protocol Buffer 格式,通过 RPC 调用 NM 的对应方法。NM 处理后会返回成功启动的 Container 列表。
NM 中负责响应来自 AM 的 RPC 请求的是 ContainerManagerImpl ,它是 NodeManager 的一部分,负责 Container 的管理,在 Nodemanager 启动时,该服务被初始化。该类实现了接口 ContainerManagementProtocol ,接到 RPC 请求后,会调用 ContainerManagerImpl.startContainers() 。改函数的基本逻辑是:
ContainerEvent(type=INIT_CONTAINER)
事件,这个事件由 ContainerImpl 处理ContainersLaucherEvent(type=LAUNCH_CONTAINER)
,处理这个事件的是 ContainersLauncher 。ContainerLauncher 是 ContainerManager 的一个子服务,收到 ContainersLaucherEvent(type=LAUNCH_CONTAINER)
事件后,组装出一个 ContainerLaunch 类并使用 ExecutorService 执行。
ContainerLaunch 类负责一个 Container 具体的 Lanuch 。基本逻辑如下:
ContainerEvent(type=CONTAINER_LAUNCHED)
,该事件由 ContainerImpl 处理。ContainerExecutor 是 NodeManager 的一部分,负责 Container 中具体工作的执行。该类是抽象类,可以有不同的实现,如 DefaultContainerExecutor ,DockerContainerExecutor ,LinuxContainerExecutor 等。根据 YARN 的配置,NodeManager 启动时,会初始化具体的 ContainerExecutor 。
ContainerExecutor 最主要的方法是 launchContainer() ,该方法阻塞,直到执行的命令结束。
DefaultContainerExecutor 是默认的 ContainerExecutor ,支持 Windows 和 Linux 。它的 launchContainer() 的逻辑是:
至此,Container 在 NM 中已经启动,AM 中 NMCallback 回调类中的 onContainerStarted() 方法被调用。
AM 异步与 NM 通信的类,使用方法见注释:
/**
* <code>NMClientAsync</code> handles communication with all the NodeManagers
* and provides asynchronous updates on getting responses from them. It
* maintains a thread pool to communicate with individual NMs where a number of
* worker threads process requests to NMs by using {@link NMClientImpl}. The max
* size of the thread pool is configurable through
* {@link YarnConfiguration#NM_CLIENT_ASYNC_THREAD_POOL_MAX_SIZE}.
*
* It should be used in conjunction with a CallbackHandler. For example
*
* <pre>
* {@code
* class MyCallbackHandler implements NMClientAsync.CallbackHandler {
* public void onContainerStarted(ContainerId containerId,
* Map<String, ByteBuffer> allServiceResponse) {
* [post process after the container is started, process the response]
* }
*
* public void onContainerStatusReceived(ContainerId containerId,
* ContainerStatus containerStatus) {
* [make use of the status of the container]
* }
*
* public void onContainerStopped(ContainerId containerId) {
* [post process after the container is stopped]
* }
*
* public void onStartContainerError(
* ContainerId containerId, Throwable t) {
* [handle the raised exception]
* }
*
* public void onGetContainerStatusError(
* ContainerId containerId, Throwable t) {
* [handle the raised exception]
* }
*
* public void onStopContainerError(
* ContainerId containerId, Throwable t) {
* [handle the raised exception]
* }
* }
* }
* </pre>
*
* The client‘s life-cycle should be managed like the following:
*
* <pre>
* {@code
* NMClientAsync asyncClient =
* NMClientAsync.createNMClientAsync(new MyCallbackhandler());
* asyncClient.init(conf);
* asyncClient.start();
* asyncClient.startContainer(container, containerLaunchContext);
* [... wait for container being started]
* asyncClient.getContainerStatus(container.getId(), container.getNodeId(),
* container.getContainerToken());
* [... handle the status in the callback instance]
* asyncClient.stopContainer(container.getId(), container.getNodeId(),
* container.getContainerToken());
* [... wait for container being stopped]
* asyncClient.stop();
* }
* </pre>
*/
调用方提供的回调类。
BlockingQueue<ContainerEvent> events
一个名为 events 的队列,存放 Container 的事件。入队由方法 startContainerAsync() 和 stopContainerAsync() 负责,出队由下面说的「Container 事件执行线程」负责。
ConcurrentMap<ContainerId, StatefulContainer> containers
存放 Container 的 Map。新增由方法 startContainerAsync() 负责,删除由下面说的「Container 事件执行线程」负责。
该线程不断地从 events 队列中取出事件,根据事件类型做不同的操作,主要的两个操作是:
ContainerEventType.START_CONTAINER
,调用 NMClient.startContainer() 同步地启动 Container ,然后调用回调类中的 onContainerStarted() 方法,如果 Container 状态已经完成,则从 containers 中删除。ContainerEventType.STOP_CONTAINER
,调用 NMClient.stopContainer() 同步地停止 Container ,然后调用回调类中的 onContainerStopped() 方法,如果 Container 状态已经完成,则从 containers 中删除。异步地启动一个 Container 。首先把参数中的 Container 放入 Map containers 中,然后往 events 队列添加一个事件:ContainerEvent(type=START_CONTAINER)
。
异步地停止一个 Container 。往 evnets 队列中添加一个事件 ContainerEvent(type=STOP_CONTAINER)
。
真正负责与 NM 通信的类。
ConcurrentMap<ContainerId, StartedContainer> startedContainers
名为 startedContainers 的 Map ,存放启动了的 Container 。
同步地启动一个 Container 。首先把 Container 添加到 startedContainers 中,然后调用 ContainerManagementProtocol.startContainers() 。
AM 必须提供 Container 启动需要的所有信息,包括 ID,Node Id,Token 以及 ContainerLaunchContext 等。
NM 则返回成功启动的 Container 列表。
同步地停止一个 Container , 先调用 ContainerManagementProtocol.stopContainers() 停止 Container, 然后从 startedContainers 中删除。
AM 与 NM 直接的通信接口,通过改接口,可以启动、停止 Container ,获取 Container 状态等等。
基于 Google Protocol Buffer 协议,通过 RPC 调用 NM 中对应的方法,实现 Container 的启动。 NM 中对应的方法是 ContainerManagerImpl.startContainers() 和 ContainerManagerImpl.stopContainers() 。
AM 或者说 NMClient 提供需要启动或停止的 Container 列表(如果是启动的话还需要提供具体的启动信息),NM 则返回成功启动或停止的 Container 列表。
NodeManager 的一部分,负责 Container 的管理,在 Nodemanager 启动时,该服务被初始化。
该类实现了接口 ContainerManagementProtocol ,它对来自于 AM (具体是来自于NMClient)的 RPC 调用进行回应。
启动传入的 Container 列表。主要逻辑是:
初始化一个 Container 的事件,改事件由 ContainerManager 触发,由 ApplicationImpl 处理。事实上是封装了这个事件:ApplicationEvent(type=INIT_CONTAINER)
。
具体参考注释:
/**
* Event sent from {@link ContainerManagerImpl} to {@link ApplicationImpl} to
* request the initialization of a container. This is funneled through
* the Application so that the application life-cycle can be checked, and container
* launches can be delayed until the application is fully initialized.
*
* Once the application is initialized,
* {@link ApplicationImpl.InitContainerTransition} simply passes this event on as a
* {@link ContainerInitEvent}.
*
*/
NM 视角下的一个 Application 。
Map<ContainerId, Container> containers
存放该 APP 的 Container 。
处理 ApplicationEvent 事件,根据事件的类型,几个主要的处理逻辑如下:
INIT_CONTAINER
:如果 APP 已经完成初始化,就触发一个新事件 ContainerEvent(type=INIT_CONTAINER)
,该事件由 ContainerImpl 处理。INIT_APPLICATION
:初始化 ApplicationImpl 的一些元素和服务。NodeManager 视角下的一个 Container 。该类的实例由 AM 通过 RPC 调用 ContainerManagerImpl.startContainers() 方法创建。
处理 ContainerEvent 事件,根据事件类型,几个主要的处理逻辑如下:
INIT_CONTAINER
注释如下:
* State transition when a NEW container receives the INIT_CONTAINER
* message.
*
* If there are resources to localize, sends a
* ContainerLocalizationRequest (INIT_CONTAINER_RESOURCES)
* to the ResourceLocalizationManager and enters LOCALIZING state.
*
* If there are no resources to localize, sends LAUNCH_CONTAINER event
* and enters LOCALIZED state directly.
*
* If there are any invalid resources specified, enters LOCALIZATION_FAILED
* directly.
主要逻辑就是:
ContainersLaucherEvent(type=LAUNCH_CONTAINER)
,该事件由 ContainersLauncher 处理。ContainerLauncher 是 ContainerManager 的一个子服务,负责 Launch Container 。该服务处理 ContainersLauncherEvent 事件。
根据事件类型,主要处理逻辑是:
LAUNCH_CONTAINER
:组装出一个 ContainerLaunch 类并使用 ExecutorService 执行。负责一个 Container 具体的 Lanuch 。基本逻辑如下:
ContainerEvent(type=CONTAINER_LAUNCHED)
,该事件由 ContainerImpl 处理。ContainerExecutor 是 NodeManager 的一部分,负责 Container 中具体工作的执行。该类是抽象类,可以有不同的实现,如 DefaultContainerExecutor ,DockerContainerExecutor ,LinuxContainerExecutor 等。根据 YARN 的配置,NodeManager 启动时,会初始化具体的 ContainerExecutor , 默认是 DefaultContainerExecutor 。
最主要的方法是 launchContainer() ,改方法阻塞,直到执行的命令结束。
默认的 ContainerExecutor ,支持 Windows 和 Linux 。
AMRMClient 的异步版本。
用法见注释:
/**
* <code>AMRMClientAsync</code> handles communication with the ResourceManager
* and provides asynchronous updates on events such as container allocations and
* completions. It contains a thread that sends periodic heartbeats to the
* ResourceManager.
*
* It should be used by implementing a CallbackHandler:
* <pre>
* {@code
* class MyCallbackHandler implements AMRMClientAsync.CallbackHandler {
* public void onContainersAllocated(List<Container> containers) {
* [run tasks on the containers]
* }
*
* public void onContainersCompleted(List<ContainerStatus> statuses) {
* [update progress, check whether app is done]
* }
*
* public void onNodesUpdated(List<NodeReport> updated) {}
*
* public void onReboot() {}
* }
* }
* </pre>
*
* The client‘s lifecycle should be managed similarly to the following:
*
* <pre>
* {@code
* AMRMClientAsync asyncClient =
* createAMRMClientAsync(appAttId, 1000, new MyCallbackhandler());
* asyncClient.init(conf);
* asyncClient.start();
* RegisterApplicationMasterResponse response = asyncClient
* .registerApplicationMaster(appMasterHostname, appMasterRpcPort,
* appMasterTrackingUrl);
* asyncClient.addContainerRequest(containerRequest);
* [... wait for application to complete]
* asyncClient.unregisterApplicationMaster(status, appMsg, trackingUrl);
* asyncClient.stop();
* }
* </pre>
*/
一个名为 responseQueue
的队列(LinkedBlockingQueue<AllocateResponse> responseQueue
),存放从 RM 获得的回应。
与 RM 进行心跳的线程。改线程根据设置的心跳间隔,循环地调用 AMRMClient.allocate() 方法与 RM 进行通信,得到 RM 的回应后,把回应放到 responseQueue 队列中。
在 AMRMClientAsync 的的构造方法中,使用方必须提供一个「回调类」。该线程循环从 responseQueue 队列中获取来自 RM 的回应,根据回应的类型,调用回调类中相应的方法。比如当发现回应中包含有「已经分配好的 Container」,就调用回调类中的 onContainerAllocated() 方法。
调用方(AM)初始化 AMRMClientAsync 后,首先要通过改方法注册自己。
该方法中,调用 AMRMClient.registerApplicationMaster() 进行真正的注册,然后开启心跳线程
HeartbeatThread。
调用链是: AMRMClientImpl.addContainerRequest() -> AMRMClientImpl.addResourceRequest() ->
addResourceRequestToAsk() , 最终会把对 Container 的要求加到一个类型为 ContainerRequest 的集合里面,该集合的名字,叫 ask。
实际与 RM 进行心跳通信的类。
一个名为 ask 的集合,保存着 AM 对 Container 的需求, 这些需求通过 addResourceRequest() 方法添加到该集合中。
每次心跳的执行方法是 allocate() ,其注释如下:
* Request additional containers and receive new container allocations.
* Requests made via <code>addContainerRequest</code> are sent to the
* <code>ResourceManager</code>. New containers assigned to the master are
* retrieved. Status of completed containers and node health updates are also
* retrieved. This also doubles up as a heartbeat to the ResourceManager and
* must be made periodically. The call may not always return any new
* allocations of containers. App should not make concurrent allocate
* requests. May cause request loss.
该方法的具体实现是 AMRMClientImpl.allocate() ,主要逻辑是从 ask 集合中拿到 Container 需求,组装
成一个 AllocateRequest ,最终调用 ApplicationMasterProtocol.allocate() 申请 Container 。
AM 与 RM 通信接口。
其中最重要的方法是 allocate() ,该方法同时也承担着 AM 与 RM 心跳的责任。该方法的主要逻辑,就是把
AllocateRequest 转换为「Google protocol Buffer」格式,通过 RPC 调用传到 RM 。在 RM 一端,则是由
ApplicationMasterService 接收消息。该类同样也是实现了接口 ApplicationMasterProtocol ,所以这时 ApplicationMasterService.allocate() 会被执行。
传递的与 Container 有关的信息:
ApplicationMasterService 是 RM 的一个组成部分。它实现了 ApplicationMasterProtocol 接口,负责对来自 AM 的 RPC 请求进行回应。
RM启动时,会初始化 ApplicationMasterService ,并根据配置,把相应的调度器 YarnScheduler 传进来。
核心逻辑是:
表示在 FIFO|Capacity 调度器视角下的一个「Application Attempt」,继承自
SchedulerApplicationAttempt 。
这个类跟踪一个 APP 调度相关的信息,包括 Container 需求列表,优先级等等。
更新位于 AppSchedulingInfo 中的 Container 需求列表。
获取位于 AppSchedulingInfo 中的 Container 需求列表。
List<RMContainer> newlyAllocatedContainers
,里面存放的就是新近申请到的 Container 。List 元素的增加和获取,主要分别通过下面两个方法实现。
基本上就是从 newlyAllocatedContainers 中把 Container 取出来,封装后返回。
基本上就是根据传进来的参数,封装出一个 RMContainer 添加到 newlyAllocatedContainers 中。然后发出
一个 RMContainerEventType.START 的消息,该消息之后会由 RMContainer 处理。
继承自 SchedulerNode 表示在 FIFO|Capacity 调度器视角下的一个 Node Manager 。
Map<ContainerId, RMContainer> launchedContainers
,存放 Launched 的 Container 。
把 RMContainer 添加到 launchedContainers
中。
与 NM 进行心跳。
接收来自 NM 的 RPC 心跳请求。核心逻辑就是触发出一个「更新 NM 信息」事件 :RMNodeStatusEvent(RMNodeEventType.STATUS_UPDATE)
,这个消息由 RMNode处理。
表示在 RM 的视角下的一个 NM ,存放了这个 NM 的资源信息以及其他一些统计信息。
RMNode 接收 RMNodeStatusEvent(RMNodeEventType.STATUS_UPDATE)
消息,主要处理逻辑是:
NodeUpdateSchedulerEvent(SchedulerEventType.NODE_UPDATE)
。改消息会由具体的调度器处理。在 RM 视角下的某个 APP 的 Container。维护了一个状态机,处理各种发给自己代表的 Container 的 RMContainerEvent 事件。
RMContainer 对 RMContainerEventType 事件进行处理处理,根据事件类型,部分处理逻辑如下:
RMContainerEventType.START
: 状态从 NEW 变为 ALLOCATED,最终触发事件 RMAppAttemptEvent(type=CONTAINER_ALLOCATED)
, 该事件由 RMAppAttemptImpl 处理。RMContainerEventType.LAUNCHED
: 状态从 ACQUIED 变为 RUNNING 。RMAppAttemptImpl 对 RMAppAttemptEvent 事件进行处理,该事件就是告诉 AppAttempt ,你这个APP有 Container 申请好了,AppAttempt 检查自己的状态,如果当前还没有运行 AM ,就把这个 Container 拿来运行 AM。
FIFO调度器,继承和接口关系是:
interface YarnScheduler
interface ResourceScheduler extends YarnScheduler
abstract class AbstractYarnScheduler implements ResourceScheduler
class FifoScheduler extends AbstractYarnScheduler
来自 AMRMClient(或者说来自 AM )的 Container 分配申请最终会传到这里。主要逻辑是:
处理 SchedulerEventType.NODE_UPDATE
消息:
RMContainerEventType.LAUNCHED
,该消息由 RMContainer 处理;RMContainerEventType.START
的消息,该消息之后会由 MContainer 处理。参见注释:
* The <code>ApplicationMaster</code> needs to send a heartbeat to the
* <code>ResourceManager</code> at regular intervals to inform the
* <code>ResourceManager</code> that it is up and alive. The
* {@link ApplicationMasterProtocol#allocate} to the <code>ResourceManager</code> from the
* <code>ApplicationMaster</code> acts as a heartbeat.
*
* <p>
* For the actual handling of the job, the <code>ApplicationMaster</code> has to
* request the <code>ResourceManager</code> via {@link AllocateRequest} for the
* required no. of containers using {@link ResourceRequest} with the necessary
* resource specifications such as node location, computational
* (memory/disk/cpu) resource requirements. The <code>ResourceManager</code>
* responds with an {@link AllocateResponse} that informs the
* <code>ApplicationMaster</code> of the set of newly allocated containers,
* completed containers as well as current state of available resources.
* </p>
*
* <p>
* For each allocated container, the <code>ApplicationMaster</code> can then set
* up the necessary launch context via {@link ContainerLaunchContext} to specify
* the allocated container id, local resources required by the executable, the
* environment to be setup for the executable, commands to execute, etc. and
* submit a {@link StartContainerRequest} to the {@link ContainerManagementProtocol} to
* launch and execute the defined commands on the given allocated container.
* </p>
*
原文地址:http://blog.csdn.net/gaopenghigh/article/details/45507765