TaskTracker节点向JobTracker汇报当前节点的运行时信息时候,是将运行状态信息同心跳报告一起发送给JobTracker的,主要包括TaskTracker的基本信息、节点资源使用信息、各任务状态等。所以信息被序列化为TaskTrackerStatus实例对象。每次发送心跳报告的时候,会重新构造一个Status对象,并重置这些信息,而且需要主要的是每次发送的status对象的大小是不一定的,因为很多信息的发送是有时间间隔的。这些操作主要位于方法transmitHeartBeat的上半部分代码:
1 HeartbeatResponse transmitHeartBeat(long now) throws IOException { 2 // 计算是否发送任务计数器信息,间隔时间为${COUNTER_UPDATE_INTERVAL}对应的值为60s,不支持配置 3 boolean sendCounters; 4 if (now > (previousUpdate + COUNTER_UPDATE_INTERVAL)) { 5 sendCounters = true; 6 previousUpdate = now; 7 } 8 else { 9 sendCounters = false; 10 } 11 12 // 13 // Check if the last heartbeat got through... 14 // if so then build the heartbeat information for the JobTracker; 15 // else resend the previous status information. 16 // 17 if (status == null) { 18 synchronized (this) { 19 status = new TaskTrackerStatus(taskTrackerName, localHostname, 20 httpPort, 21 cloneAndResetRunningTaskStatuses( 22 sendCounters), 23 failures, 24 maxMapSlots, 25 maxReduceSlots); 26 } 27 } else { 28 LOG.info("Resending ‘status‘ to ‘" + jobTrackAddr.getHostName() + 29 "‘ with reponseId ‘" + heartbeatResponseId); 30 } 31 32 // 33 // Check if we should ask for a new Task 34 // 计算节点资源使用信息 35 boolean askForNewTask; 36 long localMinSpaceStart; 37 synchronized (this) { 38 askForNewTask = 39 ((status.countOccupiedMapSlots() < maxMapSlots || 40 status.countOccupiedReduceSlots() < maxReduceSlots) && 41 acceptNewTasks); 42 localMinSpaceStart = minSpaceStart; 43 } 44 if (askForNewTask) { 45 askForNewTask = enoughFreeSpace(localMinSpaceStart); 46 long freeDiskSpace = getFreeSpace(); 47 long totVmem = getTotalVirtualMemoryOnTT(); 48 long totPmem = getTotalPhysicalMemoryOnTT(); 49 long availableVmem = getAvailableVirtualMemoryOnTT(); 50 long availablePmem = getAvailablePhysicalMemoryOnTT(); 51 long cumuCpuTime = getCumulativeCpuTimeOnTT(); 52 long cpuFreq = getCpuFrequencyOnTT(); 53 int numCpu = getNumProcessorsOnTT(); 54 float cpuUsage = getCpuUsageOnTT(); 55 56 status.getResourceStatus().setAvailableSpace(freeDiskSpace); 57 status.getResourceStatus().setTotalVirtualMemory(totVmem); 58 status.getResourceStatus().setTotalPhysicalMemory(totPmem); 59 status.getResourceStatus().setMapSlotMemorySizeOnTT( 60 mapSlotMemorySizeOnTT); 61 status.getResourceStatus().setReduceSlotMemorySizeOnTT( 62 reduceSlotSizeMemoryOnTT); 63 status.getResourceStatus().setAvailableVirtualMemory(availableVmem); 64 status.getResourceStatus().setAvailablePhysicalMemory(availablePmem); 65 status.getResourceStatus().setCumulativeCpuTime(cumuCpuTime); 66 status.getResourceStatus().setCpuFrequency(cpuFreq); 67 status.getResourceStatus().setNumProcessors(numCpu); 68 status.getResourceStatus().setCpuUsage(cpuUsage); 69 } 70 //add node health information 添加节点健康状态 71 TaskTrackerHealthStatus healthStatus = status.getHealthStatus(); 72 synchronized (this) { 73 if (healthChecker != null) { 74 healthChecker.setHealthStatus(healthStatus); 75 } else { 76 healthStatus.setNodeHealthy(true); 77 healthStatus.setLastReported(0L); 78 healthStatus.setHealthReport(""); 79 } 80 } 81 82 ...... 83 ...//发送心跳报告 84 ..... 85 synchronized (this) { 86 for (TaskStatus taskStatus : status.getTaskReports()) { 87 if (taskStatus.getRunState() != TaskStatus.State.RUNNING && 88 taskStatus.getRunState() != TaskStatus.State.UNASSIGNED && 89 taskStatus.getRunState() != TaskStatus.State.COMMIT_PENDING && 90 !taskStatus.inTaskCleanupPhase()) { 91 if (taskStatus.getIsMap()) { 92 mapTotal--; 93 } else { 94 reduceTotal--; 95 } 96 myInstrumentation.completeTask(taskStatus.getTaskID()); 97 runningTasks.remove(taskStatus.getTaskID()); 98 } 99 } 100 101 ..... 102 // 其他代码 103 }
transmitHeartBeat
1、创建TaskTrackerStatus实例对象status,创建代码如下:
status = new TaskTrackerStatus(taskTrackerName, localHostname, httpPort, cloneAndResetRunningTaskStatuses(sendCounters), failures, maxMapSlots,maxReduceSlots);
创建status对象的时候参数分别是: taskTrackerName-->当前节点名称,value为{"tracker_" + localHostname + ":" + taskReportAddress},其中taskReportAddress是为task服务的监听地址。
localHostname-->当前节点的指定的host名称,配置参数变量为slave.host.name,如果不指定该参数,那么从mapred.tasktracker.dns.interface和mapred.tasktracker.dns.nameserver指定的dns中获取,默认为本地hostname。
httpPort-->Http监听的端口号
cloneAndResetRunningTaskStatuses(sendCounters)-->根据是否进行任务计数器信息发送标志,clone真正运行的task状态信息
failures-->当前节点上失败的任务次数,用于判断当前节点的完整性,当该值达到最大标准的时候,JobTracker不会再给该节点分配任务信息。
maxMapSlots, maxReduceSlots-->该节点运行的最大slot个数。
2、判断是否允许分配任务给该节点,这个是先通过判断当前节点的空闲slot个数,然后通过判断当前节点的磁盘剩余量来达到的。代码如下:
askForNewTask = ((status.countOccupiedMapSlots() < maxMapSlots || status.countOccupiedReduceSlots() < maxReduceSlots) && acceptNewTasks);askForNewTask = enoughFreeSpace(localMinSpaceStart); // 其中localMinSpaceStart为配置中给定的${mapred.local.dir.minspacestart},默认为0
当满足第一个条件:使用的slot个数小于总slot个数的时候,那么给JobTracker发送节点资源使用情况。当满足第二个条件的时候,允许JobTracker给当前节点分配任务。
3、初始化资源使用情况,主要是设置一系列的磁盘、内存等资源信息等,代码如下:
long freeDiskSpace = getFreeSpace(); // 获取剩余的磁盘大小 long totVmem = getTotalVirtualMemoryOnTT(); // 获取总的虚拟内存 long totPmem = getTotalPhysicalMemoryOnTT(); // 获取总的物理内存 long availableVmem = getAvailableVirtualMemoryOnTT(); // 获取可用的虚拟内存 long availablePmem = getAvailablePhysicalMemoryOnTT(); // 获取可用的物理内存 long cumuCpuTime = getCumulativeCpuTimeOnTT(); // 获取累积cpu时间 long cpuFreq = getCpuFrequencyOnTT(); // 获取cpu频率 int numCpu = getNumProcessorsOnTT(); // 获取总的进程数 float cpuUsage = getCpuUsageOnTT(); // 获取cpu可用比例 status.getResourceStatus().setAvailableSpace(freeDiskSpace); status.getResourceStatus().setTotalVirtualMemory(totVmem); status.getResourceStatus().setTotalPhysicalMemory(totPmem); status.getResourceStatus().setMapSlotMemorySizeOnTT(mapSlotMemorySizeOnTT); // 设置map阶段slot允许的内存大小, ${mapred.cluster.map.memory.mb} status.getResourceStatus().setReduceSlotMemorySizeOnTT(reduceSlotSizeMemoryOnTT); // 设置reduce阶段slot允许的内存大小, ${mapred.cluster.reduce.memory.mb} status.getResourceStatus().setAvailableVirtualMemory(availableVmem); status.getResourceStatus().setAvailablePhysicalMemory(availablePmem); status.getResourceStatus().setCumulativeCpuTime(cumuCpuTime); status.getResourceStatus().setCpuFrequency(cpuFreq); status.getResourceStatus().setNumProcessors(numCpu); status.getResourceStatus().setCpuUsage(cpuUsage);
4、获取当前节点的监控状态,获取当前节点的监控状态是有线程NodeHealthCheckerService来周期性的检查的,可以通过配置一个监控脚本来实现,默认为不实现。详细分析见TaskTracker源码分析(TaskTracker节点健康状况监控)
5、发送心跳报告
6、处理当前真正运行的Task,处理规则是:只要task不是出于运行、就绪、提交挂起或者cleanup阶段,那么就将该task设置为完成状态,从真正运行的task列表中移除,并针对该task是map阶段或者reduce阶段,分别对map/reduce solt进行操作。
7、完成发送。
发送的状态对象是org.apache.hadoop.mapred.TaskTrackerStatus,主要属性有:
String trackerName; // task tracker 节点名称 String host; // 主机名 int httpPort; // http web监听端口 int failures; // 在该节点上失败的task次数 List<TaskStatus> taskReports; // 当前节点上真正运行的各个人物的状态 volatile long lastSeen; // 上次汇报时间 private int maxMapTasks; // 当前节点上允许的最大map slot个数 private int maxReduceTasks; // 当前节点上允许的最大reduce slot个数 private TaskTrackerHealthStatus healthStatus; // 当前节点的健康状态对象 public static final int UNAVAILABLE = -1; // 是否不可用 private ResourceStatus resStatus; // 当前节点的资源对象
其中ResourceStatus和TaskTrackerHealthStatus分别表示当前节点的资源信息和状态信息,是一个简单的model类。在这里不做分析。
TaskStatus类全称为org.apache.hadoop.mapred.TaskStatus。主要保存当前TaskTracker上运行的所有任务的运行状态,基本属性如下:
private final TaskAttemptID taskid; // task任务id private float progress; // 任务执行进度,0-1.0 private volatile State runState; // 任务运行所处状态,详见TaskStatus.State枚举类 private String diagnosticInfo; // 诊断信息,一般为异常信息或者错误信息 private String stateString; // 字符串信息的运行状态 private String taskTracker; // 所属task tracker名称 private int numSlots; // 运行该task所需的slot个数,默认为1 private long startTime; // 任务启动时间 private long finishTime; // 任务完成时间 private long outputSize = -1L; // 输出数据量 private volatile Phase phase = Phase.STARTING; // 任务运行阶段,详见TaskStatus.Phase枚举类 private Counters counters; // 该任务中定义的计数器(包括系统自带计数器和用户自定义计数器) private boolean includeCounters; // 是否包含计数器,计数器没个60s发送一次,也就是说每隔60s,发送的数据中包含一次计数器 private SortedRanges.Range nextRecordRange = new SortedRanges.Range(); // 下一个要处理的数据区间,用于定位坏记录所在的空间
===================================
ResourceStatus实例对象resStatus的属性是由抽象类ResourceCalculatorPlugin来获取的,如果不指定该抽象类的具体实现类,那么获取的value值全部都是-1。在linux平台上,默认实现为LinuxResourceCalculatorPlugin类。
// 创建获取资源的对象 Class<? extends ResourceCalculatorPlugin> clazz = fConf.getClass(TT_RESOURCE_CALCULATOR_PLUGIN, null, ResourceCalculatorPlugin.class); resourceCalculatorPlugin = ResourceCalculatorPlugin.getResourceCalculatorPlugin(clazz, fConf);
另外,用户可以自定义该实现类,配置参数为${mapreduce.tasktracker.resourcecalculatorplugin},默认为空。获取代码如下:
public static ResourceCalculatorPlugin getResourceCalculatorPlugin( Class<? extends ResourceCalculatorPlugin> clazz, Configuration conf) { if (clazz != null) { return ReflectionUtils.newInstance(clazz, conf); // 如果已经配置了class,那么直接使用配置的class } // No class given, try a os specific class try { String osName = System.getProperty("os.name"); // 获取操作系统 if (osName.startsWith("Linux")) { // 如果是linux return new LinuxResourceCalculatorPlugin(); // 使用已经实现的一种 } } catch (SecurityException se) { // Failed to get Operating System name. return null; } // Not supported on this system. return null; }
在LinuxResourceCalculatorPlugin中,其实获取系统的资源信息都是通过读取proc虚拟文件系统中的一些信息来达成的,比如从/proc/meminfo中读取内存,从/proc/cpuinfo中读取cpu信息等。