前言
继上一篇文章讲述完ApplicationMaster的相关用法,核心主题都是围绕着2个字"应用",当然在RM中还有另外一项比较重要的服务也很重要,他就是节点管理服务,在RM中是如何维系管理多个节点,对于应用管理的话,在RM中已经有了ApplicationMasterService这个服务对象了,那么对应于节点NodeManager来说,难道叫做NodeManagerService吗,听起来非常顺,其实他叫做?ResourceTrackerService,当然名称叫什么都无所谓啦,他扮演的功能就是类似于节点NodeManager大管家的角色了.OK,在这里我们就以NodeManager管理为核心线索,逐步分析RM在此方面的设计思想.
相关涉及类
在分析之前,还是需要了解一下相关类,在阅读本篇文章之前,可以建议大家阅读我的上一篇文章ApplicationMaster文章的分析,因为NM和AM管理许多思想共同,也有共同的父类,比如AbstractService这样的抽象服务类.下面是我归纳出的几个类.
1.NodeManager.java--节点管理类,这个类是yarn-resourcemanager包中的类,不是yarn-nodemanager中的同名类,这个类是本篇文章的核心角色类,
2.NodesListManager--节点列表管理类,这个类中管理了类似黑名单,白名单的节点列表形式。
3.NMLivelinessMonitor--节点存活状态监控线程类,与之前的AMLivelinessMonitor线程的原理类似,最简单的心跳更新检查。
4.ResourceTrackerService--节点服务管理对象,负责与各个NodeManager通信。包括NM在此服务上的注册请求处理,心跳更新操作等等。
下面是一张结构简图帮助大家宏观上理解RM中的NM管理:
NodeManager节点注册
我们从一个比较初始的状态出发,比如说节点注册开始,一步步的贯穿的去分析整个流程。节点注册操作,在NodeManager类自身中。这个类中定义的基本信息如下
-
- public class NodeManager implements ContainerManagementProtocol {
- private static final Log LOG = LogFactory.getLog(NodeManager.class);
- private static final RecordFactory recordFactory = RecordFactoryProvider.getRecordFactory(null);
-
- final private String containerManagerAddress;
-
- final private String nodeHttpAddress;
-
- final private String rackName;
-
- final private NodeId nodeId;
- final private Resource capability;
- Resource available = recordFactory.newRecordInstance(Resource.class);
- Resource used = recordFactory.newRecordInstance(Resource.class);
注册操作并没有独立出方法来,而是包含在了构造函数中,也就是说,当你构造新的NodeManager的时候,你已经在注册节点到ResourceTrackerService。
- public NodeManager(String hostName, int containerManagerPort, int httpPort,
- String rackName, Resource capability,
- ResourceTrackerService resourceTrackerService, RMContext rmContext)
- throws IOException, YarnException {
- this.containerManagerAddress = hostName + ":" + containerManagerPort;
- this.nodeHttpAddress = hostName + ":" + httpPort;
- this.rackName = rackName;
- this.resourceTrackerService = resourceTrackerService;
- this.capability = capability;
- Resources.addTo(available, capability);
-
- this.nodeId = NodeId.newInstance(hostName, containerManagerPort);
-
- RegisterNodeManagerRequest request = recordFactory
- .newRecordInstance(RegisterNodeManagerRequest.class);
-
- request.setHttpPort(httpPort);
- request.setNodeId(this.nodeId);
- request.setResource(capability);
- request.setNodeId(this.nodeId);
-
- resourceTrackerService.registerNodeManager(request);
- this.schedulerNode = new FiCaSchedulerNode(rmContext.getRMNodes().get(
- this.nodeId), false);
- .....
- }
顺着这行代码,来看一下服务端处理注册请求的方法。服务端的类对象是ResourceTrackerService。
-
- public class ResourceTrackerService extends AbstractService implements
- ResourceTracker {
-
- private static final Log LOG = LogFactory.getLog(ResourceTrackerService.class);
-
- private static final RecordFactory recordFactory =
- RecordFactoryProvider.getRecordFactory(null);
-
- private final RMContext rmContext;
-
- private final NodesListManager nodesListManager;
-
- private final NMLivelinessMonitor nmLivelinessMonitor;
-
- private final RMContainerTokenSecretManager containerTokenSecretManager;
- private final NMTokenSecretManagerInRM nmTokenSecretManager;
-
-
- private long nextHeartBeatInterval;
-
- private Server server;
- private InetSocketAddress resourceTrackerAddress;
-
- private static final NodeHeartbeatResponse resync = recordFactory
- .newRecordInstance(NodeHeartbeatResponse.class);
- private static final NodeHeartbeatResponse shutDown = recordFactory
- .newRecordInstance(NodeHeartbeatResponse.class);
-
-
- private int minAllocMb;
-
- private int minAllocVcores;
也是继承了抽象服务类,这里面包含的内容就多了许多,重点关注,节点列表管理器对象NodesListManager和NMLivelinessMonitor,这2者与本文叙述所相关。然后跳到节点注册请求处理操作。在节点注册请求进来的时候,首先会做一些请求的过滤条件的验证,过滤不符合要求的节点。
-
- @SuppressWarnings("unchecked")
- @Override
- public RegisterNodeManagerResponse registerNodeManager(
- RegisterNodeManagerRequest request) throws YarnException,
- IOException {
-
- NodeId nodeId = request.getNodeId();
- String host = nodeId.getHost();
- .....
-
-
-
- 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;
- }
-
-
-
- if (capability.getMemory() < minAllocMb
- || capability.getVirtualCores() < minAllocVcores) {
- String message =
- "NodeManager from " + host
- + " doesn't satisfy minimum allocations, Sending SHUTDOWN"
- + " signal to the NodeManager.";
- LOG.info(message);
- response.setDiagnosticsMessage(message);
- response.setNodeAction(NodeAction.SHUTDOWN);
- return response;
- }
- .....
2个条件,节点是否有效,有效的规则就是在NodeListManager中定义的,这个后面会提到,第二个是节点所剩资源是否足够启动NodeManager。如果这2个请求都过了的话,则表明可以进行注册,此节点将会被注册进行存活监控线程中。
- .....
-
-
- this.nmTokenSecretManager.removeNodeKey(nodeId);
-
- this.nmLivelinessMonitor.register(nodeId);
-
- String message =
- "NodeManager from node " + host + "(cmPort: " + cmPort + " httpPort: "
- + httpPort + ") " + "registered with capability: " + capability
- + ", assigned nodeId " + nodeId;
- LOG.info(message);
- response.setNodeAction(NodeAction.NORMAL);
- response.setRMIdentifier(ResourceManager.clusterTimeStamp);
- return response;
- }
注册操作在上篇文章都详细讲述过了,在基础监控类中声明了,如下:
-
- public abstract class AbstractLivelinessMonitor<O> extends AbstractService {
- ......
-
- private final Clock clock;
-
-
- private Map<O, Long> running = new HashMap<O, Long>();
-
-
- public synchronized void receivedPing(O ob) {
-
- if (running.containsKey(ob)) {
- running.put(ob, clock.getTime());
- }
- }
具体细节请求点击
YARN源码分析(一)
。
OK,回到之前没有说清楚的NodeListManager节点列表管理器类,这个类提供了节点有效性检查的方法
- .....
-
-
-
- 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;
- }
- .....
传入的是主机名,可以联系之前Decommision文章中提到的include,exclude名单列表的内容。
-
- public class NodesListManager extends AbstractService implements
- EventHandler<NodesListManagerEvent> {
-
- private static final Log LOG = LogFactory.getLog(NodesListManager.class);
-
- private HostsFileReader hostsReader;
- private Configuration conf;
-
- private Set<RMNode> unusableRMNodesConcurrentSet = Collections
- .newSetFromMap(new ConcurrentHashMap<RMNode,Boolean>());
-
- private final RMContext rmContext;
- .....
在这个类中写明了unsableNodes无法使用的节点列表名单,但是有效性检查的方法并没有使用到此变量。下面是真正的valid检测方法
-
- public boolean isValidNode(String hostName) {
- synchronized (hostsReader) {
-
- Set<String> hostsList = hostsReader.getHosts();
- Set<String> excludeList = hostsReader.getExcludedHosts();
- String ip = NetUtils.normalizeHostName(hostName);
-
- return (hostsList.isEmpty() || hostsList.contains(hostName) || hostsList
- .contains(ip))
- && !(excludeList.contains(hostName) || excludeList.contains(ip));
- }
- }
也是通过hostReader对象读取配置文件中的include,exclude主机名列表做判断。初始主机从这里读取出来
- @Override
- protected void serviceInit(Configuration conf) throws Exception {
-
- this.conf = conf;
-
-
-
- try {
- this.hostsReader =
- new HostsFileReader(
- conf.get(YarnConfiguration.RM_NODES_INCLUDE_FILE_PATH,
- YarnConfiguration.DEFAULT_RM_NODES_INCLUDE_FILE_PATH),
- conf.get(YarnConfiguration.RM_NODES_EXCLUDE_FILE_PATH,
- YarnConfiguration.DEFAULT_RM_NODES_EXCLUDE_FILE_PATH)
- );
-
- printConfiguredHosts();
- .....
OK,节点注册操作分析完毕。
节点HeartBeat心跳
心跳方法在NodeManager中有直接定义
- public class NodeManager implements ContainerManagementProtocol {
- ....
-
- public void heartbeat() throws IOException, YarnException {
- NodeStatus nodeStatus =
- org.apache.hadoop.yarn.server.resourcemanager.NodeManager.createNodeStatus(
- nodeId, getContainerStatuses(containers));
- nodeStatus.setResponseId(responseID);
- NodeHeartbeatRequest request = recordFactory
- .newRecordInstance(NodeHeartbeatRequest.class);
- request.setNodeStatus(nodeStatus);
-
- NodeHeartbeatResponse response = resourceTrackerService
- .nodeHeartbeat(request);
- responseID = response.getResponseId();
- }
也是远程调用ResourceTrackerService方法
-
- @SuppressWarnings("unchecked")
- @Override
- public NodeHeartbeatResponse nodeHeartbeat(NodeHeartbeatRequest request)
- throws YarnException, IOException {
-
-
- NodeStatus remoteNodeStatus = request.getNodeStatus();
-
-
-
-
-
-
-
-
- NodeId nodeId = remoteNodeStatus.getNodeId();
-
-
- RMNode rmNode = this.rmContext.getRMNodes().get(nodeId);
- if (rmNode == null) {
-
- String message = "Node not found resyncing " + remoteNodeStatus.getNodeId();
- LOG.info(message);
- resync.setDiagnosticsMessage(message);
- return resync;
- }
-
-
-
- this.nmLivelinessMonitor.receivedPing(nodeId);
-
-
-
- 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;
- }
-
- .....
-
-
-
- NodeHeartbeatResponse nodeHeartBeatResponse = YarnServerBuilderUtils
- .newNodeHeartbeatResponse(lastNodeHeartbeatResponse.
- getResponseId() + 1, NodeAction.NORMAL, null, null, null, null,
- nextHeartBeatInterval);
- rmNode.updateNodeHeartbeatResponseForCleanup(nodeHeartBeatResponse);
心跳方法也不是特别的复杂。