HDFS二次开发常见问题
问题描述
客户开发了一个HDFS应用,此应用存在多个线程,需同时往HDFS组件上写数据。在业务运行时,发现有业务线程和HDFS交互时,报如下异常:
...... Exception in thread "main" java.io.IOException: Filesystem closed at org.apache.hadoop.hdfs.DFSClient.checkOpen(DFSClient.java:498) at org.apache.hadoop.hdfs.DFSClient.getFileInfo(DFSClient.java:1869) at org.apache.hadoop.hdfs.DistributedFileSystem$26.doCall(DistributedFileSystem.java:1474) at org.apache.hadoop.hdfs.DistributedFileSystem$26.doCall(DistributedFileSystem.java:1470) at org.apache.hadoop.fs.FileSystemLinkResolver.resolve(FileSystemLinkResolver.java:81) at org.apache.hadoop.hdfs.DistributedFileSystem.getFileStatus(DistributedFileSystem.java:1 470) at org.apache.hadoop.fs.FileSystem.exists(FileSystem.java:1444) ......
原因分析
经分析,捕捉到异常的业务应用线程确实有建立到HDFS组件连接,并且这个过程中此业务应用线程并没close这个连接实例,连接初始化API示意代码:
FileSystem hdfs1 = FileSystem.get(conf);
再排查此业务应用代码,发现在其他线程中有close连接实例的操作,关闭连接实例的API示意代码:
hdfs1.close();
深入分析发现:多个线程通过HDFS提供API申请并获取到HDFS连接实例,但实际上是同一个连接实例,所以在同一个业务进程内,一旦一个线程close一个HDFS连接实例,其他线程即无法再使用先前所申请的连接实例。
如下是对这个问题的演示DEMO:
...... FileSystem hdfs1 = FileSystem.get(conf); FileSystem hdfs2 = FileSystem.get(conf); System.out.println("/user/tester1/spark-core is " + hdfs1.exists(new Path("/user/tester1/spark-core"))); System.out.println("/user/tester1/hive-date is " + hdfs2.exists(new Path("/user/tester1/hive-date"))); hdfs1.close(); System.out.println("/user/tester1/hive-date is " + hdfs2.exists(new Path("/user/tester1/hive-date"))); // 这里会失败,因为上面已经把连 接实例关闭了。
解决办法
属于HDFS机制,并不是bug。业务应用需根据自己的实际业务场景来确定如何使用HDFS连接。一般建议如下,供参考:
-
业务进程空间中,统一建立和管理(例如close)一个连接实例,各个线程共用,但不要直接管理此连接实例;
-
若实在有必要给一个线程单独分配一个连接实例,可以按照HDFS机制,在创建连接实例时,指定不采用缓存中的连接实例,具体API的使用DEMO如下:
c. ...... d. FileSystem hdfs1 = FileSystem.get(conf); e. conf.setBoolean("fs.hdfs.impl.disable.cache", true);//指定不采用缓存中的连接实例 f. FileSystem hdfs2 = FileSystem.get(conf); g. h. System.out.println("/user/tester1/spark-core is " i. + hdfs1.exists(new Path("/user/tester1/spark-core"))); j. System.out.println("/user/tester1/hive-date is " k. + hdfs2.exists(new Path("/user/tester1/hive-date"))); l. m. hdfs1.close(); n. System.out.println("/user/tester1/hive-date is " + hdfs2.exists(new Path("/user/tester1/hive-date"))); // 这里不会再抛出问题描述中的异 常。
转载自: