通过这两天的不断研究源码,发现与这个相关的hadoop源码是DFSClient、DistributedFileSystem、JobInProgress、JobClient、JobTracker等
与hive相关的源码是CliDriver、Driver、ExecDriver、TaskRunner、Task、MoveTask等。
之前设置了暂停100毫秒的方式后,后来也测试出将严重影响MR运行的效率,从198秒增加到498秒,这非常慢了。
最终通过hive提交job的位置发现,以ExecDriver代码为例,代码如下:
// Finally SUBMIT the JOB!
rj = jc.submitJob(job);
在第435行,hadoop版本我是1.0.3。
jc是一个JobClient的实例,submitJob方法如下:
public RunningJob submitJob(JobConf job) throws FileNotFoundException,
IOException {
try {
return submitJobInternal(job);
} catch (InterruptedException ie) {
throw new IOException("interrupted", ie);
} catch (ClassNotFoundException cnfe) {
throw new IOException("class not found", cnfe);
}
}
该方法调用了submitJobInternal方法,该方法代码较多,截取主要代码段如下:
//
// Now, actually submit the job (using the submit name)
//
printTokens(jobId, jobCopy.getCredentials());
status = jobSubmitClient.submitJob(
jobId, submitJobDir.toString(), jobCopy.getCredentials());
JobProfile prof = jobSubmitClient.getJobProfile(jobId);
if (status != null && prof != null) {
return new NetworkedJob(status, prof, jobSubmitClient);
} else {
throw new IOException("Could not launch job");
}
其中jobSubmitClient是一个接口类型的变量,JobTracker和LocalJobRunner是它的实现。
这里以JobTracker类为例,该方法实现也较多,截取主要代码如下;
JobInfo jobInfo = null;
UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
synchronized (this) {
if (jobs.containsKey(jobId)) {
// job already running, don't start twice
return jobs.get(jobId).getStatus();
}
jobInfo = new JobInfo(jobId, new Text(ugi.getShortUserName()),
new Path(jobSubmitDir));
}
// Create the JobInProgress, do not lock the JobTracker since
// we are about to copy job.xml from HDFS
JobInProgress job = null;
try {
job = new JobInProgress(this, this.conf, jobInfo, 0, ts);
} catch (Exception e) {
throw new IOException(e);
}
这里要主要了重点来了。创建JobInProgress这个对象时,最后有个finally方法段里有一个方法,如下:
FileSystem.closeAllForUGI(UserGroupInformation.getCurrentUser());
都会对文件系统进行关闭操作,包括DFSClient进行关闭、 DistributedFileSystem关闭还有相关的IO流也关闭。因为此方法是一个静态方法,里面的CACHE这个对象也是全局对象(只有一个)。代码如下:
public staticvoid closeAllForUGI(UserGroupInformation ugi)
throws IOException {
CACHE.closeAll(ugi);
}
这是当hive支持parallel模式时,就会同时创建多个JobInProgress对象,然后都会对CACHE进行关闭操作,这样就会导致正在执行的远程调用方法,被中断掉。也就出现了ClosedByInterruptException这个异常了。另外之前还有一个问题,报错信息是“Filesystem closed”这个消息,也是因为DFSClient不在运行状态导致,代码如下:
private void checkOpen()throws IOException {
if (!clientRunning) {
IOException result = new IOException("Filesystem closed");
throw result;
}
}
通过综上分析,感觉导致此异常的最终原因查明了,不过我还是要证实下我这个猜测是否正确。
采取的措施有二:
第一:我采用的方式是暂时将finally方法段里closeAllForUGI的调用注释掉,检验其是否还有ClosedByInterruptException异常出现。目前正在测试中,两天后给出测试结果。
其二:分析和评估下注释掉closeAllForUGI方法的调用后,有那些影响和隐患。
目前通过几天的测试、调试和研究后,发现还是没解决这个并发问题,暂没想到更好的解决方式了,所以将绕开这个问题,hive的parallel模式不采用,并修改调度程序使之并行化(有一定技巧在里面,需要在实战中提高这方面的意识)。
那么此问题通过了一个月的努力,就暂告一段落了。望以后随着对hadoop使用的不断加深和接触的业务越来越多广泛,能找出解决方式。
另外补充下,如果计算量较大,一个统计的数据量超过了2亿(这里指记录),那么hadoop在初次搭建的集群建议在10台以上,这样有几个好处:
第一:给优化腾出充足的时间
第二:方便业务的扩展
第三:体验hadoop(为什么这么说呢,个人通过使用和测试后,认为当集群的节点太少的话,运行太慢,能测试出很好的优化结果有难度。)