1. 程式人生 > >多個HDFS叢集的fs.defaultFS配置一樣,造成應用一直連線同一個叢集的問題分析

多個HDFS叢集的fs.defaultFS配置一樣,造成應用一直連線同一個叢集的問題分析

## 背景 應用需要對兩個叢集中的同一目錄下的HDFS檔案個數和檔案總大小進行比對,在測試環境中發現,即使兩邊HDFS目錄下的資料不一樣,應用日誌顯示兩邊始終比對一致,分下下來發現,應用連的一直是同一個叢集。大資料叢集:CDH6.2.1 ## 定位分析 ### 應用程式碼片段 ```` Configuration mainconf = new Configuration(); mainconf.addResource(new Path(main_prefix+"/core-site.xml")); mainconf.addResource(new Path(main_prefix+"/hdfs-site.xml")); //Main叢集hdfs操作,獲取指定目錄下檔案 getHdfsDirectoryTailList("指定目錄",mainConf) Configuration slaveconf=new Configuration(); slaveconf.addResource(new Path(slave_prefix+"/core-site.xml")); slaveconf.addResource(new Path(slave_prefix+"/hdfs-site.xml")); //Slave叢集hdfs操作,獲取指定目錄下檔案 getHdfsDirectoryTailList("指定目錄",slaveConf) ```` ```` public static List getHdfsDirectoryTailList(String path,Configuration conf) { List tailList = new ArrayList(); try { FileSystem hdfs = FileSystem.get(URI.create(path), conf); if(!hdfs.exists(new Path(path))){ return tailList; } FileStatus[] fs = hdfs.listStatus(new Path(path)); Path[] listPath = FileUtil.stat2Paths(fs); for (Path p : listPath) { String[] tailSplit = p.toString().split("\\/"); String tail = tailSplit[tailSplit.length - 1]; if (tail.equals("_SUCCESS")) { continue; } tailList.add(tail); } } catch (IOException e) { logger.error("Extract: getHdfsDirectoryTailList exception", e); throw e; } return tailList; } ```` 檢查兩個叢集配置及應用程式碼,確認沒有問題 ### fs.hdfs.impl.disable.cache引數 fs.hdfs.impl.disable.cache引數之前又遇到過,預設值為false,表示使用cache,懷疑又是cache的問題,所以FileSystem.get(URI.create(path), conf),第一次獲取的是master叢集,第二次使用了cache,所以一直連的是master叢集。測試方法,在core-site.xml里加上下面配置,表示不使用cache,加上之後,應用能正常連線兩個叢集了。 ```` fs.hdfs.impl.disable.cache
true
```` ### FileSystem.get原始碼分析 那麼明明使用了兩個叢集,為什麼會使用到Cache呢,分析FileSystem.get原始碼便知道原因了 ```` public static FileSystem get(URI uri, Configuration conf) throws IOException { String scheme = uri.getScheme(); String authority = uri.getAuthority(); if (scheme == null && authority == null) { // use default FS return get(conf); } if (scheme != null && authority == null) { // no authority URI defaultUri = getDefaultUri(conf); if (scheme.equals(defaultUri.getScheme()) // if scheme matches default && defaultUri.getAuthority() != null) { // & default has authority return get(defaultUri, conf); // return default } } String disableCacheName = String.format("fs.%s.impl.disable.cache", scheme); if (conf.getBoolean(disableCacheName, false)) { LOGGER.debug("Bypassing cache to create filesystem {}", uri); return createFileSystem(uri, conf); } return CACHE.get(uri, conf); } ```` 應用在獲取FileSystem時,提供了完整的hdfs目錄,同時沒有設定fs.hdfs.impl.disable.cache為true,所以建立slave叢集的filesystem物件時,會使用CACHE.get(uri, conf)獲取,Cache內部使用一個HashMap來維護filesystem物件,很容易想到,當HashMap的key相同時,便返回了同一個filesystem物件,那麼Cache中的key是什麼樣的呢,程式碼如下: ```` FileSystem get(URI uri, Configuration conf) throws IOException{ Key key = new Key(uri, conf); return getInternal(uri, conf, key); } static class Key { final String scheme; final String authority; final UserGroupInformation ugi; final long unique; // an artificial way to make a key unique Key(URI uri, Configuration conf) throws IOException { this(uri, conf, 0); } Key(URI uri, Configuration conf, long unique) throws IOException { scheme = uri.getScheme()==null ? "" : StringUtils.toLowerCase(uri.getScheme()); authority = uri.getAuthority()==null ? "" : StringUtils.toLowerCase(uri.getAuthority()); this.unique = unique; this.ugi = UserGroupInformation.getCurrentUser(); } @Override public int hashCode() { return (scheme + authority).hashCode() + ugi.hashCode() + (int)unique; } static boolean isEqual(Object a, Object b) { return a == b || (a != null && a.equals(b)); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof Key) { Key that = (Key)obj; return isEqual(this.scheme, that.scheme) && isEqual(this.authority, that.authority) && isEqual(this.ugi, that.ugi) && (this.unique == that.unique); } return false; } @Override public String toString() { return "("+ugi.toString() + ")@" + scheme + "://" + authority; } } } ```` 可以看到Key由四個要素構成,其中前2個跟URI相關,我們兩叢集的fs.defaultFS值均為CDH高可用叢集建立時的預設值hdfs://nameservice1,應用比對的是兩邊叢集的相同目錄,ugi為安全認證的使用者,應用使用的是同一個,unique為0,因此Key相同,第二次獲取filesystem物件時,直接返回了第一次建立的filesystem物件,最終造成了應用雖然使用了不同的叢集配置檔案,但最中獲取的是同一個filesystem物件。 ## 解決 fs.hdfs.impl.disable.cache引數本身不建議修改,修改叢集的fs.defaultFS,使不同叢集的fs.defaultFS