天天看點

Hadoop-0.20.0源代碼分析(02)

UserGroupInformation類定義了一個與檔案系統相關的使用者群組資訊抽象的内容,Hadoop架構實作了一個基于Unix系統的使用者群組資訊的實作類UnixUserGroupInformation,該類繼承自UserGroupInformation抽象類。

從UserGroupInformation抽象類與其子類UnixUserGroupInformation的屬性字段可以看出,抽象類所定義的功能資訊重心在,描述一個登入以後獲得的UserGroupInformation執行個體,而UnixUserGroupInformation類主要是側重于登入前的資訊的處理。首先看一下UnixUserGroupInformation類中定義的屬性:

public static final String DEFAULT_USERNAME = "DrWho";

public static final String DEFAULT_GROUP = "Tardis";

final static private HashMap<String, UnixUserGroupInformation> user2UGIMap = new HashMap<String, UnixUserGroupInformation>();

private String userName;

private String[] groupNames;

final private static String UGI_TECHNOLOGY = "STRING_UGI";

前面兩個是預設的Unix使用者名DEFAULT_USERNAME群組名DEFAULT_GROUP,另外其中使用者名userName群組名groupNames是根據UnixUserGroupInformation類構造方法設定的,這樣保證了即使無法得到使用者群組的資訊,也能夠使用預設的值去填充,比較适合用于測試,快速定位到使用者名群組名的設定處。

第二個屬性user2UGIMap是一個<使用者名, 使用者群組資訊執行個體>的Map,用來快速擷取到使用者群組的資訊。

最後一個UGI_TECHNOLOGY定義讀擷取使用者群組資訊的方式,顯然該類中預設使用從文本中讀取字元串的方式來構造。

對于UnixUserGroupInformation類執行個體的構造,該類給出了四個方法:

public UnixUserGroupInformation() {

}

public UnixUserGroupInformation(String userName, String[] groupNames) {

setUserGroupNames(userName, groupNames);

}

/**

* 根據一個或多個包含了“使用者名群組名”的字元串的來構造UnixUserGroupInformation執行個體。

*/

public UnixUserGroupInformation(String[] ugi) {

if (ugi==null || ugi.length < 2) {

throw new IllegalArgumentException( "Parameter does contain at least "+ "one user name and one group name");

}

public static UnixUserGroupInformation createImmutable(String[] ugi) {

return new UnixUserGroupInformation(ugi) {

public void readFields(DataInput in) throws IOException {

throw new UnsupportedOperationException();

}

};

}

String[] groupNames = new String[ugi.length-1];

System.arraycopy(ugi, 1, groupNames, 0, groupNames.length);

setUserGroupNames(ugi[0], groupNames);

}

該類實作了其抽象基類定義的兩個抽象方法,用來擷取使用者名群組名,如下所示:

public String[] getGroupNames() {

return groupNames;

}

public String getUserName() {

return userName;

}

UserGroupInformation抽象類實作了org.apache.hadoop.io.Writable接口,并沒有在其中實作序列化操作的兩個方法,是以在其子類UnixUserGroupInformationg給出了實作,如下:

/**

* 反序列化重組(this)對象

*/

public void readFields(DataInput in) throws IOException {

// 首先讀取UGI類型,預設就是從文本讀字元串的類型

String ugiType = Text.readString(in);

if (!UGI_TECHNOLOGY.equals(ugiType)) {

throw new IOException("Expect UGI prefix: " + UGI_TECHNOLOGY + ", but receive a prefix: " + ugiType);

}

// 從DataInput in流對象中讀取使用者群組的資訊

userName = Text.readString(in);

int numOfGroups = WritableUtils.readVInt(in);

groupNames = new String[numOfGroups];

for (int i = 0; i < numOfGroups; i++) {

groupNames[i] = Text.readString(in);

}

}

/**

* 序列化(this)對象,寫入到DataOutput out輸出流中

*/

public void write(DataOutput out) throws IOException {

// 将UGI_TECHNOLOGY寫入到DataOutput out中,寫入位置是位元組流的最前面

Text.writeString(out, UGI_TECHNOLOGY);

// 寫入操作

Text.writeString(out, userName);

WritableUtils.writeVInt(out, groupNames.length);

for (String groupName : groupNames) {

Text.writeString(out, groupName);

}

}

 下面兩個方法,将使用者群組的資訊以字元串的形式存到Hadoop的配置類Configuration執行個體中,也能從一個給定的Configuration類執行個體中讀取出來:

public static void saveToConf(Configuration conf, String attr, UnixUserGroupInformation ugi ) {

conf.set(attr, ugi.toString()); // 通過鍵值對的形式存儲

}

public static UnixUserGroupInformation readFromConf(Configuration conf, String attr) throws LoginException {

String[] ugi = conf.getStrings(attr); // 從conf中讀取使用者群組資訊字元串

if(ugi == null) {

return null;

}

UnixUserGroupInformation currentUGI = null;

if (ugi.length>0 ){

currentUGI = user2UGIMap.get(ugi[0]); // 根據從配置執行個體中讀取到的使用者名,從user2UGIMap中擷取一個UnixUserGroupInformation執行個體

}

if (currentUGI == null) {

try {

currentUGI = new UnixUserGroupInformation(ugi); // 如果user2UGIMap中不存在從conf中讀取使用者對應的UnixUserGroupInformation執行個體,直接根據字元串資訊構造一個

user2UGIMap.put(currentUGI.getUserName(), currentUGI); // 同時加入到user2UGIMap中

} catch (IllegalArgumentException e) {

throw new LoginException("Login failed: "+e.getMessage());

}

}

return currentUGI;

}

與執行Unix系統的Shell指令相關的三個方法,介紹如下:

/**

* 模拟Unix系統的Shell指令whoami來擷取目前使用者名

*/

static String getUnixUserName() throws IOException {

String[] result = executeShellCommand(new String[]{Shell.USER_NAME_COMMAND}); // 調用executeShellCommand()方法

if (result.length!=1) {

throw new IOException("Expect one token as the result of " +

Shell.USER_NAME_COMMAND + ": " + toString(result));

}

return result[0];

}

/**

* 模拟Unix系統的Shell指令groups來擷取和目前使用者相關的組清單資訊

*/

private static String[] getUnixGroups() throws IOException {

return executeShellCommand(Shell.getGROUPS_COMMAND());

}

/* 根據輸入的Unix系統Shell指令,模拟Unix系統執行 */

private static String[] executeShellCommand(String[] command) throws IOException {

String groups = Shell.execCommand(command);

StringTokenizer tokenizer = new StringTokenizer(groups);

int numOfTokens = tokenizer.countTokens();

String[] tokens = new String[numOfTokens];

for (int i=0; tokenizer.hasMoreTokens(); i++) {

tokens[i] = tokenizer.nextToken();

}

return tokens;

}

 上面三個方法能夠模拟執行Unix系統的Shell指令,與Hadoop架構中實作的工具類org.apache.hadoop.util.Shell類密切相關,執行Unix系統的Shell指令的實作有一點點複雜,可以參考其工具類實作。

上面分析與登入系統之前使用者群組資訊的擷取實作。當一個使用者具備了充分的資訊,可以登入檔案系統進行特定的操作。下面就分析執行登入的過程了,有三個實作方法,基本原理都是一緻的,下面給出這三個方法的聲明及其說明:  /**

* 通過模拟Unix系統執行Shell指令擷取使用者名群組名資訊,執行登入動作。

*/

public static UnixUserGroupInformation login() throws LoginException;

/**

* 調用readFromConf方法從conf中擷取UGI;

* 若不為UGI != null,則根據save來決定是否将UGI存儲到conf中;

* 若UGI == null,調用無參Login()方法登入,傳回不為null的UGI,并根據save來決定是否将UGI存儲到conf中。

*/

public static UnixUserGroupInformation login(Configuration conf, boolean save) throws LoginException;

/**

* 設定save = false,調用login(conf, save)執行登入動作,也就是不把使用者群組資訊儲存到conf中。

*/

public static UnixUserGroupInformation login(Configuration conf) throws LoginException;

 最後,該類還有一個用于比較兩個UGI是否相同的方法public boolean equals(Object other)。

 總結一下,UnixUserGroupInformation類主要對登入前的使用者名群組名資訊進行格式化,使用兩種方式來擷取:

1、通過從Hadoop的配置類執行個體中擷取到使用者名群組名;

2、模拟執行Unix系統Shell指令擷取到 使用者名群組名。