* 在hadoop中获取了filesystem之后,便可以通过这个客户端进行相关的文件操作。文件操作涉及到分布式文件系统文件流得使用,现在通过一个简单的下载代码实例进行文件流打开方法即filesystem.open的源码分析*
1、download()方法:
/**
* 下载方法,用输入流的形式
* @throws IOException
*/
public static void download() throws IOException{
Path path = new Path("hdfs://master:9000/user/hadoop/Normaltest/gal.txt");
FSDataInputStream is=fs.open(path);
FileOutputStream os = new FileOutputStream("D:/hadoop/test.txt");
IOUtils.copy(is, os);
}
可以看到fs.open是返回一个FSDataInputStream对象进行文件的操作。
2、fs.open(path)
/**
* Opens an FSDataInputStream at the indicated Path.
* @param f the file to open
*/
public FSDataInputStream open(Path f) throws IOException {
return open(f, getConf().getInt("io.file.buffer.size", 4096));
}
可以看到此方法是通过定义了块大小后,继续调用实现类的open方法
3、 public FSDataInputStream open(Path f, final int bufferSize)
由代码可以看出运用了匿名类FileSystemLinkResolver的resolve方法解析,本质是调用了docall(),因此查看docall()中 dfs.open(getPathName(p), bufferSize, verifyChecksum)方法
4、 dfs.open(getPathName(p), bufferSize, verifyChecksum)
public DFSInputStream open(String src, int buffersize, boolean verifyChecksum)
throws IOException, UnresolvedLinkException {
checkOpen();
// Get block info from namenode
TraceScope scope = getPathTraceScope("newDFSInputStream", src);
try {
return new DFSInputStream(this, src, verifyChecksum);
} finally {
scope.close();
}
}
此方法通过构造方法返回了一个DFSInputStream,参数是this(DFS client)与src、是否检查校验和的标志(verifyChecksum)等
5、DFSInputStream(DFSClient dfsClient, String src, boolean verifyChecksum)
DFSInputStream(DFSClient dfsClient, String src, boolean verifyChecksum) throws IOException, UnresolvedLinkException {
this.dfsClient = dfsClient;
this.verifyChecksum = verifyChecksum;
this.src = src;
synchronized (infoLock) {
this.cachingStrategy = dfsClient.getDefaultReadCachingStrategy();
}
openInfo();
}
此构造函数进行对一些类成员变量赋值(dfsClient、verifyChecksum、src、cachingStrategy ),还调用了openinfo进行其他变量的赋值
6、 openInfo()
此方法主要调用了 fetchLocatedBlocksAndGetLastBlockLength方法,由名字可看出这个是获取文件组成块的位置和最后一块的块大小(因为除了最后一块,其他块大小在配置文件中已经指定,默认64M)
7、fetchLocatedBlocksAndGetLastBlockLength()
由图中的红方框出可知,最终是通过成员变量dfsClient来获取文件块位置信息,而此变量是namenode的rpc代理对象,可与namenode直接通信
8、 dfsClient.getLocatedBlocks(src, 0)
public LocatedBlocks getLocatedBlocks(String src, long start)
throws IOException {
return getLocatedBlocks(src, start, dfsClientConf.prefetchSize);
}
可以看到是调用了getLocatedBlocks方法
9、public LocatedBlocks getLocatedBlocks(String src, long start, long length)
public LocatedBlocks getLocatedBlocks(String src, long start, long length)
throws IOException {
TraceScope scope = getPathTraceScope("getBlockLocations", src);
try {
return callGetBlockLocations(namenode, src, start, length);
} finally {
scope.close();
}
}
紧跟着又调用了callGetBlockLocations方法
10、 static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,String src, long start, long length)
static LocatedBlocks callGetBlockLocations(ClientProtocol namenode,
String src, long start, long length)
throws IOException {
try {
return namenode.getBlockLocations(src, start, length);
} catch(RemoteException re) {
throw re.unwrapRemoteException(AccessControlException.class,
FileNotFoundException.class,
UnresolvedPathException.class);
}
}
最终是调用了namenode的getBlockLocations方法,由于是rpc机制,此方法的实现在服务器端。返回的数据包括文件包含的分块信息、如名字、块大小、块偏移、是否损坏的标志以及存在哪个主机等…拿到了所有文件块后最终的文件流也生成了
更多详细细节可参考文章: