HBase和Mapreduce整合的代码网上有很多可以参考,在部署jar程序时遇到一个工程问题,值得注意,联系到之前在做spark时遇到过的一个类似的问题,这里详细介绍一下问题和解决方式
任务本身是读取hdfs上的数据,提取所需要的字段然后写入到hbase中,是一个常见的HBase和MapReduce结合的应用程序,在完成代码编写打包提交之后,运行代码时任务正常提交到了集群,并且Map任务顺利执行,没有出现异常,但是当任务运行到reduce阶段的时候出现了HBase的jar包中的一个类classnofound 的报错,一开始以为是没有将jar添加到classpath中,仔细检查运行脚本,发现所需要的jar已经添加了classpath路径中了,并且在之前的任务重有使用相同的类,不存在是特殊的类未加入到classpath中,再仔细分析发现自己疏忽了一个点造成了找不到类的错误
先介绍一点mapreduce运行时的原理,当我们向集群提交job的时候,yarn接受job并为任务分配资源,并将我们提交的jar包分发到节点上,节点上执行我们提交的代码也就是我们提交的jar,但是这里有一个问题,yarn只会分发我们提交的jar包,我们应用程序所依赖的jar不会自动发送,当我们的任务依赖外部jar时,需要我们为每个节点都提供外部jar,一种做法是将HBase的lib的路径添加到hadoop的classpath路径下,如果需要引入外部包则将外部包拷贝到hadoop中每个节点下的lib文件下,另一种做法是打包时将所依赖的jar包一并打包。这两种方式都不是很好的解决方法,第二种会导致我的任务jar包异常的大,一个简单的任务都会占用很大的空间,第一种虽然不需要占用很大的空间,但是将jar直接放入hadoop中的lib容易造成jar的冲突问题,而且jar不方便管理,曾经在spark项目中由于直接导入hbase的jar到lib文件中导致spark异常无法正常启动。
这里介绍一种hadoop本身提供的缓存机制来很方便的解决这样的问题,mapreduce提供了分布式缓存的方法来讲jar缓存到hdfs上,在运行时将jar添加到classpath中,所有的节点都去读取hdfs上的jar文件,只需要将需要的jar上传一次即可,解决引入第三方jar文件不方便的问题
操作方法:
在hdfs上创建文件夹 hdfs dfs -mkdir /usr/jar_classpath
将第三方jar文件上传到文件夹下 hdfs dfs -put *.jar /usr/jar_classpath
下面是一个工具方法,可以将文件夹下的所有jar添加到classpath中:
1 public static void addJarToClassPath(Configuration conf) throws Exception{ 2 FileSystem fs = FileSystem.get(conf); 3 Path jobClassPath = new Path(conf.get("job.class.path", 4 "/spdbccc/job_classpath")); 5 logger.info("job.class.path =" +jobClassPath); 6 if (!fs.exists(jobClassPath)) { 7 throw new Exception(jobClassPath + " not exists!"); 8 } 9 FileStatus[] jars = fs.listStatus(jobClassPath); 10 if (jars == null || jars.length == 0) { 11 return; 12 } 13 for (FileStatus fileStatus : jars) { 14 DistributedCache.addFileToClassPath(fileStatus.getPath(), conf, fs); 15 } 16 DistributedCache.createSymlink(conf); 17 }
addJarToClassPath