实现文件是mahout-distribution-0.6/integration/src/main/java/org/apache/mahout/text/SequenceFilesFromDirectory.java
sequence化的意义
原始文档不能被hadoop处理,需要一个转化过程,这个过程就是sequence化
定义类
sequencefile格式是hadoop提供的,通常我们创建一个hadoop job类的时候是这么做的:
public class MyJob extends Configured implements Tool
{
。。。。。。
}
mahout提供了一个AbstractJob类,我们先看看其定义:
public abstract class AbstractJob extends Configured implements Tool
SequenceFilesFromDirectory将以AbstractJob方式生成。
输入参数处理
这个里边比较重要的一个方法是parseDirectories,会设置inputPath和outputPath:
this.inputPath = new Path(cmdLine.getValue(inputOption).toString());
this.outputPath = new Path(cmdLine.getValue(outputOption).toString());
如果输入中没有inputpath和outputpath,程序会退出并打印原因
parseDirectories是由parseArguments调用的,parseArguments还会将options放入GroupBuilder
sequencefile的writer使用的是ChunkedWriter,可以输入参数指定
PathFilter
hadoop的FileSystem在调用listStatus函数时,可以指定第二个参数为PathFilter并且会调用PathFilter的accept函数,代码如下:
public FileStatus[] listStatus(Path f, PathFilter filter) throws IOException {
ArrayList<FileStatus> results = new ArrayList<FileStatus>();
listStatus(results, f, filter);
return results.toArray(new FileStatus[results.size()]);
}
private void listStatus(ArrayList<FileStatus> results, Path f,
PathFilter filter) throws IOException {
FileStatus listing[] = listStatus(f);
if (listing != null) {
for (int i = 0; i < listing.length; i++) {
if (filter.accept(listing[i].getPath())) {
results.add(listing[i]);
}
}
}
}
这里使用的是PrefixAdditionFilter,我们查看其accept实现(在其父类SequenceFilesFromDirectoryFilter.java):
@Override
public final boolean accept(Path current) {
log.debug("CURRENT: {}", current.getName());
try {
for (FileStatus fst : fs.listStatus(current)) {
log.debug("CHILD: {}", fst.getPath().getName());
process(fst, current);
}
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
return false;
}
调用的是process函数,我们再来看看process函数怎么实现的,这次是在PrefixAdditionFilter中了:
@Override
protected void process(FileStatus fst, Path current) throws IOException {
FileSystem fs = getFs();
ChunkedWriter writer = getWriter();
if (fst.isDir()) {
String dirPath = getPrefix() + Path.SEPARATOR + current.getName() + Path.SEPARATOR + fst.getPath().getName();
fs.listStatus(fst.getPath(),
new PrefixAdditionFilter(getConf(), dirPath, getOptions(), writer, getCharset(), fs));
} else {
InputStream in = null;
try {
in = fs.open(fst.getPath());
StringBuilder file = new StringBuilder();
for (String aFit : new FileLineIterable(in, getCharset(), false)) {
file.append(aFit).append('\n');
}
String name = current.getName().equals(fst.getPath().getName())
? current.getName()
: current.getName() + Path.SEPARATOR + fst.getPath().getName();
writer.write(getPrefix() + Path.SEPARATOR + name, file.toString());
} finally {
Closeables.closeQuietly(in);
}
}
}
可以看到,如果fst是目录,则迭代处理,如果是文件,则进行写入操作,写入时候以prefix+文件名为key,文件内容为value
ChunkedWriter的write操作实际是调用SequenceFile.Writer完成的,下面ChunkedWriter源代码很清楚展示了这一点:
public void write(String key, String value) throws IOException {
if (currentChunkSize > maxChunkSizeInBytes) {
Closeables.closeQuietly(writer);
currentChunkID++;
writer = new SequenceFile.Writer(fs, conf, getPath(currentChunkID), Text.class, Text.class);
currentChunkSize = 0;
}
Text keyT = new Text(key);
Text valueT = new Text(value);
currentChunkSize += keyT.getBytes().length + valueT.getBytes().length; // Overhead
writer.append(keyT, valueT);
}
至于SequenceFile的写入过程比较复杂,先不深入了,留待以后有需要的时候再写