基于计算机资源分析Hadoop的默认counter
前言
由于项目中,需要统计每个业务组使用的计算机资源,如cpu,内存,io读写,网络流量。所以需要阅读源码查看Hadoop的默认counter。
MapReduce Counter可以观察MapReduce job运行期的一些细节数据,Counter有"组group"的概念,用于表示逻辑上相同范围的所有数值。
cpu
如何衡量mapreduce的任务的计算量呢,如果按照任务的运行时间,有些任务的大部分时间可能卡在最后一个reduce,或者运行期间有资源抢占问题,造成运行时间较高。如果按照任务的map数和reduce数,也是不准确的,因为有些map和reduce处理的数据量很少,运行时间很短。
hadoop任务的运行使用的cpu时间,才是衡量任务的计算量,hadoop提供的counter:"Map-Reduce Framework:CPU time spent (ms)",就是任务运行耗费的cpu时间,这个cpu时间是如何统计出来的,是hadoop在运行期间,每个task会从/proc/<pid>/stat读取对应进程的用户cpu时间和内核cpu时间,他们的和就是cpu时间。
附:task获取cpu时间的源码:org.apache.hadoop.mapred.Task.updateResourceCounters--> org.apache.hadoop.util.LinuxResourceCalculatorPlugin.getProcResourceValues(获取cpu和内存资源)--> org.apache.hadoop.util.ProcfsBasedProcessTree.getProcessTree.
内存
hadoop默认counter,获取内存信息,有以下参数:
"Map-Reduce Framework:Physical memory (bytes) snapshot" 每个task会从/proc/<pid>/stat读取对应进程的内存快照,这个是进程的当前物理内存使用大小。
"Map-Reduce Framework:Virtual memory (bytes) snapshot" 每个task会从/proc/<pid>/stat读取对应进程的虚拟内存快照,这个是进程的当前虚拟内存使用大小。
"Map-Reduce Framework:Total committed heap usage (bytes)" 每个task的jvm调用Runtime.getRuntime().totalMemory()获取jvm的当前堆大小。
附:task获取内存的源码:org.apache.hadoop.mapred.Task.updateResourceCounters
io读写
hadoop读写文件,都是使用org.apache.hadoop.fs.FileSystem.open一个文件,如果是hdfs文件,就有hdfs://开头的文件url,如果是本地文件,就是file://开头的文件url。所以每个task的文件读写情况,都可以从FileSystem.getAllStatistics()获取,而hadoop使用FileSystemCounters记录了FileSystem的一切io读写大小,FileSystemCounters分析如下:
"FileSystemCounters:HDFS_BYTES_READ" job执行过程中,只有map端运行时,才从HDFS读取数据,这些数据不限于源文件内容,还包括所有map的split元数据。所以这个值应该比FileInputFormatCounters.BYTES_READ 要略大些。
"FileSystemCounters:HDFS_BYTES_WRITTEN" job执行过程中,累计写入HDFS的数据大小,reduce在执行完毕后,会写入到HDFS(存在只有map,没有reduce的情况,该情况是map执行完毕把结果写入到HDFS)。
"FileSystemCounters:FILE_BYTES_READ" 累计读取本地磁盘的文件数据大小,map和reduce端有排序,排序时需要读写本地文件。
"FileSystemCounters:FILE_BYTES_WRITTEN" 累计写入本地磁盘的文件数据大小,map和reduce端有排序,排序时需要读写本地文件,还有reduce做shuffle时,需要从map端拉取数据,也存在写入本地磁盘文件的情况。
附:FileSystemCounters相关代码:org.apache.hadoop.mapred.Task.updateResourceCounters--> org.apache.hadoop.mapred.Task.FileSystemStatisticUpdater.updateCounters
FileSystemCounters的counter对于io读写的数据,已经很齐全,但是hadoop还有一些细微的io读写的counter:
"File Input Format Counters:Bytes Read" job执行过程中,Map端从HDFS读取的输入的split的源文件内容大小,但是不包括map的split元数据,所以这个值和"FileSystemCounters:HDFS_BYTES_READ"略小,但是很接近。如果map输入的源文件是压缩文件,它的值只是压缩文件解压前的大小(附:代码位于org.apache.hadoop.mapred.MapTask.TrackedRecordReader.fileInputByteCounter)。
"Map-Reduce Framework:Map input bytes" job执行过程中,Map端从HDFS读取的输入的split的源文件内容大小,如果源文件是压缩文件,它的值是压缩文件解压后的大小(附:代码位于org.apache.hadoop.mapred.MapTask.TrackedRecordReader.inputByteCounter)。
"File Output Format Counters:Bytes Written" job执行过程中,会分为map和reduce,但是也可能存在只有map的情况,但是job执行完毕后,一般都要把结果写入到hdfs,该值是结果文件的大小,如果是压缩文件,它的值只是压缩文件解压前的大小(附:代码位于org.apache.hadoop.mapred.MapTask.DirectMapOutputCollector.fileOutputByteCounter和org.apache.hadoop.mapred.ReduceTask.NewTrackingRecordWriter.fileOutputByteCounter)。
但是这些细微的counter,没有统计map和reduce排序时文件读写的情况,所以要衡量job任务的io读写情况,我觉得最合适的还是使用FileSystemCounters的counter。
io读写流量大致可以通过上述FileSystemCounters四个参数求和而得,存在不足就是:
"FileSystemCounters:HDFS_BYTES_WRITTEN",它只是一个副本的hdfs的写入大小,而hdfs的块副本是可以调整的,所以io读写流量,还需要"FileSystemCounters:HDFS_BYTES_WRITTEN" * 副本数。
map和reduce都是用户自定义的,存在可能是用户代码绕过hadoop框架,不使用org.apache.hadoop.fs.FileSystem.open文件,这部分io读写流量,是无法被统计的。
网络流量
hadoop任务产生网络流量的阶段:map输入从hdfs拉取数据,reduce shuffle时从map端拉取数据,reduce完成往hdfs写入结果(如果没有reduce,就是map完成往hdfs写入结果)。
job和hdfs交互产生的流量,可以通过io读写分析的两个counter获取:"FileSystemCounters:HDFS_BYTES_READ"和"FileSystemCounters:HDFS_BYTES_WRITTEN"
而reduce shuffle时从map端拉取数据产生的流量,对应的counter是:
"Map-Reduce Framework:Reduce shuffle bytes" 它是reduce往map拉取中间结果的累计数据大小,如果map产生的中间结果是压缩文件,它的值是压缩文件解压前的大小(附:代码位于 org.apache.hadoop.mapred.ReduceTask.reduceShuffleBytes)。
网络流量大致可以通过上述三个参数求和而得,存在不足就是:
"FileSystemCounters:HDFS_BYTES_READ"和"FileSystemCounters:HDFS_BYTES_WRITTEN",它没有考虑hadoop对hdfs的本地化优化,hdfs读写块时,如果发现客户端和目标块在同一个节点,会直接通过本地读写,有些块如果在本地,hadoop会直接通过本地文件系统读写,不通过网络读写。
"FileSystemCounters:HDFS_BYTES_WRITTEN",它只是一个副本的hdfs的写入大小,而hdfs的块副本是可以调整的,所以网络流量,还需要"FileSystemCounters:HDFS_BYTES_WRITTEN" * 副本数。
map和reduce都是用户自定义的,存在可能是用户代码绕过hadoop框架,自行产生网络通信,这部分流量是无法被统计。
--------------------------------------分割线 --------------------------------------
--------------------------------------分割线 --------------------------------------