在spark中经过hdfs的java接口并发写文件出现了数据丢失的问题,一顿操做后发现原来是FileSystem的缓存机制。补一课先java
FileSystem.get(config)是如何建立一个hadoop的FileSystem。
分为3个步骤。
1. 初始化全部支持的FileSystem(没有实例话,只是缓存类)
2. 经过uri的scheme拿到相应FileSystem
3. 缓存机制(若是不关闭的话,默认是开启)
下面详细分析一下各步骤流程
1. 初始化
经过java提供的ServiceLoader来录入全部可能的FileSystem,就像这样
ServiceLoader<FileSystem> serviceLoader = ServiceLoader.load(FileSystem.class); for (FileSystem fs : serviceLoader) { SERVICE_FILE_SYSTEMS.put(fs.getScheme(), fs.getClass()); }
待初始化的类经过配置文件声明,配置能够在hadoop-hdfs.jar里找到
捎带一嘴,java提供的ServiceLoader有点像乞丐版spring的依赖反转。spring
2.scheme
经过对Uri的解析来判断建立一个什么FileSystem,
例如
hdfs://master:9200/test的scheme就是hdfs。
而后经过scheme和已经缓存好的FileSystem映射,找到须要实例化的类。缓存
例如scheme是hdfs,那么就会建立一个DistributedFileSystem。
3. 缓存
FileSystem类中有一个Cache内部类,用于缓存已经被实例化的FileSystem。注意这个跟链接池仍是有区别的,Cache中的缓存只是一个map,能够被多个线程拿到。这就会有一个问题,当你多线程同时get FileSystem的时候,可能返回的是同一个对象。因此切记,在多线程场景中,不要随意调用FileSystem.close,你关的链接可能会影响到其余正在使用的线程。多线程
注意: 当你在其余框架上拿fileSystem对象须要额外注意,例如在spark上进行 FileSystem.get(),若是你想自定义某些配置,设置hdfs的副本数(dfs.replication) 之类,你必须在configuration中关闭FileSystem的缓存机制,也就是设置并发
configuration.set("fs.hdfs.impl.disable.cache","true")
这很重要,由于你不肯定spark是否在你以前建立了一个FileSystem,而你获得的可能不是你想要的。框架
参考资料oop
// 遇到的相同问题spa