在Hadoop中,我們若是想直接存取HDFS之中的資料或進行一些檔案的操作,可以透過它所提供的FileSystem API來達成,下述程式是一個簡單範例:
import java.io.IOException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; public class GetFileSystem { public static void main(String[] args) throws IOException { String uri = "hdfs://shen:9000/user/"; Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(URI.create(uri), conf); } }
上述程式在執行的過程會執行三次的shell command,分別為:
whoami bash -c groups whoami
而內部的運作方式,是當上述程式執行「FileSystem.get()」方法之後,它會利用FileSystem類別本身的一個static物件「FileSystem.Cache」(如下述程式),去執行所屬的「FileSystem.Cache.get()」方法,並透過Map去找尋是否有Cache住的FileSystem instance,所以在找尋的過程中會產生一個「FileSystem.Cache.Key」物件,而它的Constructor會呼叫「UserGroupInformation.login()」靜態方法,並再交由「UnixUserGroupInformation.login()」去執行登入的動作,而這個登入的動作就會透過「org.apache.hadoop.util.Shell」去執行上述「whoami」和「bash -c groups」兩個指令。
FileSystem.java (lines:1382~1397)
static class Cache { private final Map<Key, FileSystem> map = new HashMap<Key, FileSystem>(); synchronized FileSystem get(URI uri, Configuration conf) throws IOException{ Key key = new Key(uri, conf); FileSystem fs = map.get(key); if (fs == null) { fs = createFileSystem(uri, conf); if (map.isEmpty() && !clientFinalizer.isAlive()) { Runtime.getRuntime().addShutdownHook(clientFinalizer); } fs.key = key; map.put(key, fs); } return fs; }
那為何又會有第三個「whoami」指令?
這是因為如果從Cache中找不到對應的FileSystem的話,它會執行「private static createFileSystem()」方法去產生一個對應的FileSystem instance,並執行一些初始化的動作(如下述程式),而如何產生對應的FileSystem會取決於URI scheme來決定,由於上述的範例是要存取HDFS,所以scheme為hdfs,並經由「$HADOOP_HOME/src/core/core-default.xml」的組態檔可得知,HDFS對應的FileSystem class是「org.apache.hadoop.hdfs.DistributedFileSystem」(它繼承於FileSystem),所以重點就在於此類別中的「initialize()」所為何事?
FileSystem.java (lines:1369~1379)
private static FileSystem createFileSystem(URI uri, Configuration conf ) throws IOException { LOG.warn(uri.getScheme()); Class<?> clazz = conf.getClass("fs." + uri.getScheme() + ".impl", null); if (clazz == null) { throw new IOException("No FileSystem for scheme: " + uri.getScheme()); } FileSystem fs = (FileSystem)ReflectionUtils.newInstance(clazz, conf); fs.initialize(uri, conf); return fs; }
在「org.apache.hadoop.hdfs.DistributedFileSystem」中執行「initialize()」方法會產生一個「org.apache.hadoop.hdfs.DFSClient」物件,它準備用來和HDFS進行連線的工作,而它的Constructor又會呼叫「UnixUserGroupInformation.login()」去執行登入的動作,所以才又有第二次的「whoami」指令,那為何第二次沒有執行「bash -c groups」指令?這是因為「UnixUserGroupInformation」本身也會Cache,所以執行第二次的「whoami」指令主要就是要從Cache中再取出「UnixUserGroupInformation」並傳回給「org.apache.hadoop.hdfs.DFSClient」,之所以如此才會依序執行「whoami」、「bash -c groups」和「whoami」三個指令,所以其實HDFS純粹透過Shell來取得使用者的身份和群組資訊。
UnixUserGroupInformation.java (lines:238~277)
public static UnixUserGroupInformation login() throws LoginException { try { String userName; // if an exception occurs, then uses the // default user try { userName = getUnixUserName(); } catch (Exception e) { userName = DEFAULT_USERNAME; } // check if this user already has a UGI object in the ugi map UnixUserGroupInformation ugi = user2UGIMap.get(userName); if (ugi != null) { return ugi; } /* get groups list from UNIX. * It's assumed that the first group is the default group. */ String[] groupNames; // if an exception occurs, then uses the // default group try { groupNames = getUnixGroups(); } catch (Exception e) { groupNames = new String[1]; groupNames[0] = DEFAULT_GROUP; } // construct a Unix UGI ugi = new UnixUserGroupInformation(userName, groupNames); user2UGIMap.put(ugi.getUserName(), ugi); return ugi; } catch (Exception e) { throw new LoginException("Login failed: "+e.getMessage()); } }
2010.01.04 updated
.HADOOP-4998在討論是否實作一個native OS runtime for Hadoop,如此就不用依賴上述Shell command來取得OS的相關資源.
Bash -c string 說明
If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.