一次让人峰回路转, 颜面扫地的trouble shooting, Apache Pig

最近集群计算迁移, 某Pig脚本在新集群上运行失败, 排除了文件损坏等问题, 还是每次复现失败. 日志如下:

表面原因是FileSystem 被关闭, 难道是Pig版本代码的bug, 关闭了不该关闭的FileSystem?


第一次尝试

带着这个思路, 写了btrace脚本跟踪DFSStream的close方法. 脚本如下:

import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class PigTrace {

	@TLS
	private static String jstack = null;

	@OnMethod(clazz = "org.apache.pig.backend.hadoop.executionengine.physicalLayer.relationalOperators.POStore",
	//
	location = @Location(value = Kind.THROW))
	public static void getNext() {
		print(timestamp("yyyy-MM-dd HH:mm:ss.SSS"));
		println("throw ===start");
		println(jstack);
		println("throw ===end");
	}
	
	@OnMethod(clazz = "org.apache.hadoop.hdfs.DFSOutputStream", method = "closeThreads", location = @Location(Kind.RETURN))
	public static void closing() {
		jstack = jstackStr();
		print(timestamp("yyyy-MM-dd HH:mm:ss.SSS"));
		println(jstack);
	}
	


	@OnMethod(clazz = "org.apache.hadoop.hdfs.DFSOutputStream", method = "close", location = @Location(Kind.RETURN))
	public static void closeing1() {
		jstack = jstackStr();
		print(timestamp("yyyy-MM-dd HH:mm:ss.SSS"));
		println(jstack);
	}
}

很简单, 每次调用close方法或者closeThreads方法时. 都打印对应的jstack.

上线, 观察结果


所有的关闭都是org.apache.hadoop.util.ShutdownHookManager 进行的, 也就是没有业务代码直接关闭了DFSOutputStream.

这里插入一个小插曲, 众所周知的btrace常用的功能就是等Java进程启动后根据PID attach到java进程. 但对于hadoop运行任务来说, 每个Task进程是随机分配到多台服务的,定位非常麻烦, 而且也不一定来得及手动attach上去. 因此推荐使用第二种方法, 在java进程启动的时候就加载javaagent的方式启动btrace, hadoop的配置如下:

        <property>
                <name>mapred.map.child.java.opts</name>
                <value>-Xmx1G -javaagent:/tmp/btrace/btrace-agent.jar=script=/tmp/btrace/PigTrace.class,stdout=aa.log,noServer=true</value>
        </property>

其中,

        stdout=aa.log是重定向标准输出, 但如果不添加该选项, btrace的输出不知道跑到哪里去了. 添加了之后, btrace结果仍然会显示在hadoop任务的stdout中, 可以在jobtracker中直接查看.

       noServer=true是让btrace不再开启server模式. 众所周知, btrace会让目标java进程开启2020端口, 与btrace shell进行通信, 输出结果. 我们这种方式, 仅仅需要将结果输出到stdout. 如果不使用该选项,可能会出现端口被占用的错误, 如下:

那是为什么? 难道是Task进程OOM? 但是我们的mapred.map.child.java.opts参数已经指定了OOM时dump heap, 没有任何的heap dump.

不会是出现某种特殊情况进程直接exit吧?

第二次尝试

	@OnExit
	public static void onExit(int code){
		println(strcat("process exit, code: ",str(code)));
	}

使用btrace跟踪exit调用. 打印stacktrace, 看情况如何.

上线, 结果没有任何输出. 没有exit? ? ? 那进程是如何退出的? 到底发生了什么? 不过我还是怀疑内存问题, 这次来个狠的, 直接dump 内存吧.

第三次尝试

	@OnTimer(60000)
	public static void dumpOnTime(){
		dumpHeap(strcat("/tmp/heap.", strcat(str(getpid()), strcat(".", timestamp("yyyy-MM-dd_HH.mm.ss.SSS")))));
	}

每隔60s dump 一次java heap. 等 任务失败后分析最后一次dump.

插曲, 默认情况下, btrace dump heap时必须在当前进程的目录下, 不能使用绝对路径.hadoop task的work dir 是随机分配的,dump结果很难找, 也可能被tasktracker清理. 我修改了btrace 源代码, 取消了该限制, 代码放在了github上: https://github.com/haitaoyao/btrace

错误如下:



再次上线, heap dump出来了. 拉回来分析, 发现结果如下:

占用内存最多的是SpillableMemoryManager,这个监听内存占用,如果超过一定值,就将监控的Spillable的数据spill到磁盘上, 减少内存占用. 可Task进程并没有OOM啊?进程也没有exit. 那为什么失败? 难道, 是被kill的?

可Java 的sun.misc.SignalHandler 不支持捕捉Kill 信号. OMG~~


同事提醒了一声, 是不是内存超了被tasktracker kill了?

shit.... 仔细翻log, 果然是:

TaskTree [pid=22145,tipID=attempt_201303071829_3239_m_000001_0] is running beyond physical memory-limits. Current usage : 1097891840bytes. Limit : 1073741824bytes. Killing task. TaskTree [pid=22145,tipID=attempt_201303071829_3239_m_000001_0] is running beyond physical memory-limits. Current usage : 1097891840bytes. Limit : 1073741824bytes. Killing task. Dump of the process-tree for attempt_201303071829_3239_m_000001_0 :         |- PID PPID PGRPID SESSID CMD_NAME USER_MODE_TIME(MILLIS) SYSTEM_TIME(MILLIS) VMEM_USAGE(BYTES) RSSMEM_USAGE(PAGES) FULL_CMD_LINE 
        |- 22145 26343 22145 22145 (java) 26635 238 1689505792 268040 /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.9.x86_64/jre/bin/java -Djava.library.path=/usr/lib/hadoop-0.20-mapreduce/lib/native/Linux-amd64-64:/tmp/hadoop-mapred/mapred/local/taskTracker/hadoop/jobcache/job_201303071829_3239/attempt_201303071829_3239_m_000001_0/work -Xmx1G -Djava.io.tmpdir=/tmp/hadoop-mapred/mapred/local/taskTracker/hadoop/jobcache/job_201303071829_3239/attempt_201303071829_3239_m_000001_0/

打脸! 为啥一开始没有仔细看log? !

恍然惊醒, 在hadoop的配置如下:

        <property>
                <name>mapreduce.map.memory.physical.mb</name>
                <value>1024</value>
        </property>
        <property>
                <name>mapred.map.child.java.opts</name>
                <value>-Xmx1G</value>
        </property>

开启了org.apache.hadoop.util.LinuxMemoryCalculatorPlugin, 当map进程超过1024MB时认为内存超限, 直接kill了task. 而给map进程设置的Heap内存是1G,可Java进程还有Off heap 内存呢, 比如PermGen等...

泪奔了... 修改了配置Heap为800MB, 一切正常...

附: java进程内存占用

转自: http://www.ibm.com/developerworks/java/library/j-codetoheap/index.html


一次狗屎, 峰回路转, 颜面扫地的trouble shooting....







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值