YARN调度使用状态机(StateMachine)来驱动,我们以RM(ResourceManager)为例来看状态机如何驱动其运行。作为YARN的框架核心管理者整个集群的计算资源,对 于 宿 主 机 而 言,执 行 着ResourceManager 的 Java 虚拟机是个独立的进程。用户每向这个 Hadoop 平台提交一个应用程序作业,即 App ,资源管理器就会设法在某个NM 节点上为其另起一个 Java 虚拟机以运行 App 管理者( AppMaster , AM )。
RM的 “成分”
server\resourcemanager\ResourceManager.java
@SuppressWarnings("unchecked")
public class ResourceManager extends CompositeService implements Recoverable {
//长期运行的服务,会被加入到CompositeService中,Recoverable用于系统恢复
@VisibleForTesting
protected RMContextImpl rmContext;
private Dispatcher rmDispatcher; //异步消息派发
@VisibleForTesting
protected AdminService adminService; //管理员服务
//运行在活跃状态下的RM的服务
protected RMActiveServices activeServices;
protected RMSecretManagerService rmSecretManagerService; //私密管理服务
protected ResourceScheduler scheduler; //资源调度器
protected ReservationSystem reservationSystem;//预定系统
private ClientRMService clientRM;//Client资源服务
protected ApplicationMasterService masterService; //服务于现有的 AM 提供服务和管理
protected NMLivelinessMonitor nmLivelinessMonitor; //子节点运行监视器
protected NodesListManager nodesListManager;//节点列表管理
protected RMAppManager rmAppManager; //集群App管理
protected ApplicationACLsManager applicationACLsManager;//应用权限控制列表管理
protected QueueACLsManager queueACLsManager;//队列控制链管理
private WebApp webApp;//WebApp的网站服务
private AppReportFetcher fetcher = null; //抓取应用报告
protected ResourceTrackerService resourceTracker;//资源跟踪统计服务
private JvmPauseMonitor pauseMonitor;//虚拟机暂停监视器
private boolean curatorEnabled = false;
private CuratorFramework curator; //ZooKeeper高级管理骨架
private Configuration conf; //运行配置
private UserGroupInformation rmLoginUGI;//用户组权限认证管理
}
RM 上“活跃服务"
server\resourcemanager\ResourceManager.java
@Private
public class RMActiveServices extends CompositeService {
//租约代理
private DelegationTokenRenewer delegationTokenRenewer;
private EventHandler<SchedulerEvent> schedulerDispatcher; //调度派发
private ApplicationMasterLauncher applicationMasterLauncher; //AM加载器
private ContainerAllocationExpirer containerAllocationExpirer;
private ResourceManager rm;
private RMActiveServiceContext activeServiceContext;
......
RMStateStore rmStore = null; // 创建 RMStateStore ,以保存各种副本
try {
rmStore.setResourceManager(rm);
rmStore.init(conf);
rmStore.setRMDispatcher(rmDispatcher); //设置派发器
}
rmContext.setStateStore(rmStore);
//注册事件处理器
// Register event handler for NodesListManager
nodesListManager = new NodesListManager(rmContext);
rmDispatcher.register(NodesListManagerEventType.class, nodesListManager);
addService(nodesListManager);
rmContext.setNodesListManager(nodesListManager);
// 初始化调度器
scheduler = createScheduler();
scheduler.setRMContext(rmContext);
addIfService(scheduler);
rmContext.setScheduler(scheduler);
schedulerDispatcher = createSchedulerEventDispatcher();
addIfService(schedulerDispatcher);
rmDispatcher.register(SchedulerEventType.class, schedulerDispatcher);
//创建三种 Dispatch 目标对象(接受来自 RM 的事件),并向 RM 的 Dispatcher 登记
// Register event handler for RmAppEvents
rmDispatcher.register(RMAppEventType.class, new ApplicationEventDispatcher(rmContext));
// Register event handler for RmAppAttemptEvents
rmDispatcher.register(RMAppAttemptEventType.class,new ApplicationAttemptEventDispatcher(rmContext));
// Register event handler for RmNodes
rmDispatcher.register( RMNodeEventType.class, new NodeEventDispatcher(rmContext));
//跟踪资源的使用
resourceTracker = createResourceTrackerService();
addService(resourceTracker);
rmContext.setResourceTrackerService(resourceTracker);
//用来为 NM 节点上的 AM 提供服务
masterService = createApplicationMasterService();
addService(masterService) ;
rmContext.setApplicationMasterService(masterService);
//用来管理 AM
rmAppManager = createRMAppManager();
// Register event handler for RMAppManagerEvents
rmDispatcher.register(RMAppManagerEventType.class, rmAppManager);
//用来为客户提供资源服务
clientRM = createClientRMService();
addService(clientRM);
rmContext.setClientRMService(clientRM);
//用来在 NM 节点上启动运行 AM
applicationMasterLauncher = createAMLauncher();
rmDispatcher.register(AMLauncherEventType.class,
applicationMasterLauncher);
addService(applicationMasterLauncher);
......
}
RM 顶 层 的 rmDispatcher ,用 来向 ApplicationEventDispatcher , ApplicationAttemptEventDispatcher , NodeEventDispatcher 等对象分发事件,它 们 其 实 并 非 Dispatcher ,而 是Dispatch 的 目 标;它 们 实 现 的 是 EventHandler 界 面,而 不 是 Dispatcher 界 面。这 里 在RMActiveServices 中 又 有 个 schedulerDispatcher ,这 显 然 与 调 度 器 有 关,其 类 型 是SchedulerEventDisp atcher 。但是它实现的也是 EventHandler 界面,而不是 Dispatcher 界面;从形式上看,它是用来处理 SchedulerEvent 类事件的,而不是帮助分发事件的路由器。这里 的 ApplicationEventDispatcher 和 ApplicationAttemptEventDispatcher ,前 者 有 关Application ,用来 驱 动 代 表 着 具 体 App 的 ApplicationImpl 对 象 中 的 状 态 机;后 者 有 关ApplicationAttempt ,用来驱动代表着一次具体运行尝试的 RMAppAttemptImpl 对象中的状态机。
@Private
public static final class NodeEventDispatcher implements
EventHandler<RMNodeEvent> {
private final RMContext rmContext;
public NodeEventDispatcher(RMContext rmContext) {
this.rmContext = rmContext;
}
@Override
public void handle(RMNodeEvent event) {
NodeId nodeId = event.getNodeId(); //取出子节点ID
RMNode node = this.rmContext.getRMNodes().get(nodeId); //获得子节点
if (node != null) {
try {
//调用RMNode的handler,实际是,RMNodeImpl
((EventHandler<RMNodeEvent>) node).handle(event);
} catch (Throwable t) {
LOG.error("Error in handling event type " + event.getType()
+ " for node " + nodeId, t);
}
}
}
对于集群中的每一个节点, RM 维持着一个与之对应的 RMNodeImpl 对象,在这个对象中有一个状态机,代表着该节点的当前状态,而NodeEventDispatcher 就是这些状态机的驱动者,它根据到来的事件 event 内容中的 NodeId
确定这是要分发到哪一个节点的状态机,然后找到代表着那个节点的 RMNode 对象 node ,即RMNodeImpl ,就以 event 为 参 数 直 接 调 用 其 handle ()函 数。
public interface RMNode { } //定义了一些基本节点操作
@Private
@Unstable
@SuppressWarnings("unchecked")
public class RMNodeImpl implements RMNode, EventHandler<RMNodeEvent> {
......
//合成状态机
private static final StateMachineFactory<RMNodeImpl,
NodeState,
RMNodeEventType,
RMNodeEvent> stateMachineFactory
= new StateMachineFactory<RMNodeImpl,
NodeState,
RMNodeEventType,
RMNodeEvent>(NodeState.NEW)
//Transitions from NEW state
//添加跳变规则
.addTransition(NodeState.NEW, NodeState.RUNNING,
RMNodeEventType.STARTED, new AddNodeTransition())
.addTransition(NodeState.NEW, NodeState.NEW,
RMNodeEventType.RESOURCE_UPDATE,
new UpdateNodeResourceWhenUnusableTransition())
......
.addTransition(NodeState.SHUTDOWN, NodeState.SHUTDOWN,
RMNodeEventType.FINISHED_CONTAINERS_PULLED_BY_AM,
new AddContainersToBeRemovedFromNMTransition())
// 创建拓扑表
.installTopology();
}
//处理来自RM转发的事件
public void handle(RMNodeEvent event) {
LOG.debug("Processing " + event.getNodeId() + " of type " + event.getType());
try {
writeLock.lock();
NodeState oldState = getState();
try {
//驱动状态机处理事件
stateMachine.doTransition(event.getType(), event);
} catch (InvalidStateTransitionException e) {
LOG.error("Can't handle this event at current state", e);
LOG.error("Invalid event " + event.getType() +
" on Node " + this.nodeId);
}
finally {
writeLock.unlock();
}
}
}
至于 NodeEventDispatcher ,则与 ResourceTrackerService 和 RMNodeImpl 有关。 RM 为NodeEventDispatcher 登记要 接 收 的 事 件 类 型 是 RMNodeEventType ,这 种 事 件 主 要 来 自ResourceTrackerService ,反映着集群中各节点的状态变化,例如失去连接然后又恢复了,或者机器重启了,等等。
以AddNodeTransition 为例,单狐跳转
public static class AddNodeTransition implements
SingleArcTransition<RMNodeImpl, RMNodeEvent> {
@Override
public void transition(RMNodeImpl rmNode, RMNodeEvent event) {
// Inform the scheduler
RMNodeStartedEvent startEvent = (RMNodeStartedEvent) event;
List<NMContainerStatus> containers = null;
NodeId nodeId = rmNode.nodeId;
RMNode previousRMNode =
rmNode.context.getInactiveRMNodes().remove(nodeId);
.......
//最后调用RMNode的Dispatcher继续派发新的事件,处理新的任务,周而复始
rmNode.context.getDispatcher().getEventHandler()
.handle(new NodeAddedSchedulerEvent(rmNode, containers)); //资源调度事件
rmNode.context.getDispatcher().getEventHandler().handle(
new NodesListManagerEvent(
NodesListManagerEventType.NODE_USABLE, rmNode));
}
}
server\resourcemanager\scheduler\fair\FairScheduler.java
@Override
public void handle(SchedulerEvent event) {
switch (event.getType()) {
case NODE_ADDED:
if (!(event instanceof NodeAddedSchedulerEvent)) {
throw new RuntimeException("Unexpected event type: " + event);
}
NodeAddedSchedulerEvent nodeAddedEvent = (NodeAddedSchedulerEvent)event;
addNode(nodeAddedEvent.getContainerReports(),
nodeAddedEvent.getAddedRMNode());
break;
......
}
ResourceTrackerService ,反映着集群中各节点的状态变化,以心跳推送的形式接收来自NM的报告
server\resourcemanager\ResourceTrackerService.java
@SuppressWarnings("unchecked")
@Override
public NodeHeartbeatResponse nodeHeartbeat(NodeHeartbeatRequest request)
throws YarnException, IOException {
NodeStatus remoteNodeStatus = request.getNodeStatus();
/**
* Here is the node heartbeat sequence...
* 1. Check if it's a valid (i.e. not excluded) node
* 2. Check if it's a registered node
* 3. Check if it's a 'fresh' heartbeat i.e. not duplicate heartbeat
* 4. Send healthStatus to RMNode
* 5. Update node's labels if distributed Node Labels configuration is enabled
*/
NodeId nodeId = remoteNodeStatus.getNodeId();
......
return nodeHeartBeatResponse;
}
RM 中最 重 要 的 活 跃 服 务 提 供 者 应 该 是 “资 源 调 度 器 ( scheduler )”,这 是 个 实 现 了ResourceScheduler 界面的某类对象,通过 createScheduler ()创建,具体的类型则在配置文件中加以指定。调度器手里的资源只能来集群中的众多 NodeManager 节点。 ResourceManager 内部有个 ResourceTrackerService 类的对象,它跟踪管理着 NodeManager 节点所知道的资源变动。另有一个 NodesListManager 类的对象nodesListManager 则维持着一个节点清单,记录着哪些节点当前是可用的,哪些则是不可用的。而 ResourceTrackerService 和 NodesListManager 所掌握的信息,则来自众多 NodeManager 节点的心跳报告,即伴随着每次“心跳”提供的报告。这些信息,最终都要汇集到资源调度器,因为这些资源都要由资源调度器分配出去。