在Hadoop分佈式處理中,如果需要在map和reduce任務中共享一些只讀的數據,可以將這些數據配置在配置信息中(JobConf)。但是,根據《Hadoop權威指南》中所述,如果各計算結點間需要共享的只讀數據量較大,由於配置信息的大小受到各計算結點的內存大小的限制,因此最好是採用Distributed Cache。可以在Job啓動前,利用如下代碼在Distributed Cache中添加文件或是存檔:
DistributedCache.addCacheFile(new URI("/myapp/data.data"), jobconf); //添加文件
DistributedCache.addCacheArchive(new URI("/myapp/archive.zip"), jobconf); //如果是要共享一個目錄,先將其打包成.zip, .tar.gz等的壓縮文件
在Mapper或是Reducer中可以利用如下代碼獲得所共享的文件或是存檔信息:
public static class MapClass extends MapReduceBase
implements Mapper<K, V, K, V> {
private Path[] localArchives;
private Path[] localFiles;
public void configure(JobConf job) {
// Get the cached archives/files
localArchives = DistributedCache.getLocalCacheArchives(job); //獲得共享的文檔的本地路徑列表,同時DistributedCache機制爲將存檔自動解壓,
//因此所獲得的路徑是一個目錄
localFiles = DistributedCache.getLocalCacheFiles(job); //獲得共享的文件的本地路徑列表
}
public void map(K key, V value,
OutputCollector<K, V> output, Reporter reporter)
throws IOException {
// Use data from the cached archives/files here
// ...
// ...
output.collect(k, v);
}
}
但是,在利用DistributedCache共享archives時,所獲得的路徑並不完全是原來的壓縮文件解壓後得到的目錄,其中還含有其他內容,因此在操作時需要注意。
以下以共享MapFile爲例,MapFile顧名思義就是一種磁盤版的Map結構,便於根據Key值進行快速查找,在磁盤上MapFile以一個目錄的形式存放
圖中P爲最上層的目錄,其下存在一個MapFile文件爲part-00000,它包括data和index文件。
現在在MapReduce時將P加入DistributedCache中,首先將P壓縮成zip文件,
在Job啓動前的代碼中加入如下語句:
DistributedCache.addCacheArchive(new URI(args[4]), conf); //其中args[4]是P.zip文件文件在路徑名,一般都要先將它放到HDFS中
然後在Mapper或是Reducer代碼添加如下代碼獲得MapFile的Readers
public static class MapClass extends MapReduceBase
implements Mapper<K, V, K, V>
{
private MapFile.Reader[] readers = null;
private Path P_PATH;
private Path[] localArchives;
private String p_path_name;
public void configure(JobConf conf) {
p_path_name = conf.get("P_path_name");
try{
localArchives = DistributedCache.getLocalCacheArchives(conf);}
catch(IOException e){
System.out.println("get cache files error");
}
System.out.println("num of LocalArchives is " + localArchives.length);
if(localArchives.length > 0 ) {
P_PATH = localArchives[0];
}
Path path = this.P_PATH;
try{
File zipDir = new File(path.toString());
String[] zipFiles = zipDir.list();
String mapfileDir = null;
for(int i = 0; i < zipFiles.length; ++i)
{
if(zipFiles[i].equals(this.p_path_name))
{
mapfileDir = "file://" + path.toString() + "/" + zipFiles[i]; //查找真正存放mapfiles的目錄,加上"file://"表示是在本地文件系統, DistributedCache會將共享的目錄和文件放到本地文件系統上
}
System.out.println("zipFiles[i] is " + zipFiles[i]);
}
System.out.println("mapfileDir is " + mapfileDir);
Path mapFilePath = new Path(mapfileDir);
FileSystem newFs = mapFilePath.getFileSystem(conf);
System.out.println("mapfile path is " + mapFilePath.toString());
this.readers = MapFileOutputFormat.getReaders(newFs, mapFilePath, conf); //獲得MapFile readers,在一個目錄下存在多個mapFile可以一次獲取多個readers
}catch(IOException e) {
System.out.println("fail to create mapfile readers");
}
}
在本例中利用DistributedCache.getLocalCacheArchives(conf)獲得的路徑名爲....../P.zip,但它是一個目錄,其下存在
P.zip ------文件,原始的zip文件
P ------目錄,zip解壓得到,要找的就是這個目錄
其中包括part-00000,這是真正的 mapfile
.P.zip.crc ------zip壓縮時的校驗文件
因此在獲得P.zip路徑後,還需要進一步查找,在能確定最終解壓後的目錄所在,這是利用DistributedCache共享archives時需要注意的問題。