为了完善HBase的管理功能,且利用Spark的数据处理优异性能的特性,就准备在Spark中集成HBase的管理功能。但是集成的过程中出现了一个比较奇怪的问题,异常信息如下:
Exception in thread "main" java.lang.NoSuchMethodError: org.apache.hadoop.hbase.HTableDescriptor.addFamily(Lorg/apache/hadoop/hbase/HColumnDescriptor;)Lorg/apache/hadoop/hbase/HTableDescriptor; at com.afmobi.bigdata.service.spark.hbase.SparkHBaseTest.main(SparkHBaseTest.java:41) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498)
这个通过异常信息,一看就知道是由于当前类中没有HTableDescriptor.addFamily(HColumnDescriptor family)这样的方法,该类HTableDescriptor是hbase-client-xxx.jar中的类,用于对HBase的表进行管理和操作,排查的步骤操作如下:
1、排查的第一步就是排查自己项目中使用的hbase-client-xxx.jar和CDH环境中的的hbase客户端使用的hbase-client-xxx.jar版本是否相同,确认是相同的版本,且将CDH环境中的hbase-client-xxx.jar下载到本地,通过反编译也发现该方法HTableDescriptor.addFamily(HColumnDescriptor family)是存在的;
2、在执行的spark-submit命令行显示的执行需要使用的hbase-client-xxx.jar,再次执行还是相同的异常;
3、通过查看Spark环境中运行的进程参数,Classpath中没有指定hbase-client-xxx.jar,于是将hbase-client-xxx.jar放到了spark的lib目录下,重启Spark,相同的异常;再尝试将hbase-client-xxx.jar增加到spark-conf/spark-env.sh环境配置脚本中,重启Spark,此时进程命令行可以显示的看到hbase-client-xxx.jar,不过还是想同的异常;
4、为了探个究竟,看看Spark中引用的HTableDescriptor,究竟是属于哪个hbase-client-xxx.jar,就在代码中增加了以下两行代码,希望可以打印出当前引用的hbase-client-xxx.jar的具体路径:
java.net.URL res = SparkHBaseTest.class.getClassLoader().getResource("org/apache/hadoop/hbase/HTableDescriptor.class"); System.out.println("HTableDescriptor came from " + res.getPath());
重新上传该应用,使用相同的命令执行,居然就执行成功了,心想应该是有某个步骤自己的操作,让Spark找到了正确的hbase-client-xxx.jar,于是就一步步回退:
1)删除Spark lib目录下的hbase-client-xxx.jar,重启Spark再次执行,执行成功;
2)将spark-conf/spark-env.sh环境配置脚本中显式指定的hbase-client-xxx.jar,重启Spark再次执行,执行成功;
3)去掉代码中那两行用于打印当前引用的hbase-client-xxx.jar的代码,重新上传代码,执行成功;
4)去掉spark-submit命令中显示指定的hbase-client-xxx.jar,执行成功;
5)重启整个CDH集群,再次执行,也是成功;
这个就有点让人郁闷了,花了几个小时的排查,原因没有找到,居然自己就好了。现在唯一通过说的通的就是集群在排查问题的那个时间段抽风了,于是回去看了一下Spark的执行历史、OOZIE的执行日志、系统的CPU内存以及CDH本身的监控,发现在执行出问题的那个时间段,内存、CPU、IO和网络等,是有较明显的变化,应该是当时集群正在处理一些任务,一些JAR没有CDH集群正确加载,才导致了上面的问题。