本人第一次用markdown画流程图,而且还有点小复杂,上图将就着看吧,结合代码理解起来相对简单。。 上图的流程主要体现的是hadoop fs xxx
提交到集群后执行的逻辑,当然这里主要是进入具体代码后的逻辑,进入代码前,以上命令主要是调用的$HADOOP_HOME/bin hadoop
这个脚本,通过这个脚本里面逻辑是:
if [ "$COMMAND" = "fs" ] ; then
CLASS=org.apache.hadoop.fs.FsShell
...
exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@"
显然通过hadoop fs xxx
调用脚本的时候,后面的fs
和xxx
均是脚本的参数,只是第一个fs
是脚本运行判断调用哪块逻辑的参数,而后面的参数xxx
中则是用于给具体java执行类(这里是FsShell
)的参数。
上图流程是hadoop fs xxx
这一系列命令提交执行的基本流程,这一块中涉及到的处理类主要都是继承自Command.java
,主要涉及类的作用如下:
FsShell.java 业务入口
CommandFactory.java 各种不同类型请求的处理类工厂
Command.java FsCommand的父类,是一个抽象类
FsCommand.java 抽象类,代码注释说的很清楚:Base class for all "hadoop fs" commands 所有类型请求均继承自它
PathData.java 文件系统中某个路径对应的逻辑实体类,封装了这个路径所属文件系统信息、路径信息、路径下的文件信息以及文件是否存在等
以上是一些针对hadoop fs
整体处理逻辑设计的基础类,对于具体的请求类型常用的如:get
、ls
、put
、rm
等命令,处理类可以在FsCommand.java
的registerCommands
方法中查看到往工厂中注册的类:
factory.registerCommands(AclCommands.class);
factory.registerCommands(CopyCommands.class);
factory.registerCommands(Count.class);
factory.registerCommands(Delete.class);
factory.registerCommands(Display.class);
factory.registerCommands(FsShellPermissions.class);
factory.registerCommands(FsUsage.class);
factory.registerCommands(Ls.class);
factory.registerCommands(Mkdir.class);
factory.registerCommands(MoveCommands.class);
factory.registerCommands(SetReplication.class);
factory.registerCommands(Stat.class);
factory.registerCommands(Tail.class);
factory.registerCommands(Test.class);
factory.registerCommands(Touch.class);
factory.registerCommands(SnapshotCommands.class);
factory.registerCommands(XAttrCommands.class);
}
对于开篇的流程图中,每个不同的业务处理类,主要核心的处理逻辑都在processPath(PathData src)
方法中,其它的方法主要是一些流程控制、验证以及异常处理等,这里我们挑选get
方法对应的CopyCommands.class
进行介绍,这个CopyCommands.class
类是对于文件复制类命令的合集,其中有registerCommands
,这个方法主要就是注册各类需要处理的类到工厂类中(存在工厂类中的一个HashMap
中,后续需要用时直接通过命令行参数如get
字符串进行map.get()
获取),其调用顺序是FsShell::run
->FsShell::init
->FsShell::registerCommands
->CommandFactory::registerCommands
,在工厂类CommandFactory::registerCommands
中通过反射调用FsShell::registerCommands
中入参FsCommand
的registerCommands
方法,这个方法也是通过反射的方式调用各个入参类中的registerCommands
方法,这就是CopyCommands.class
中registerCommands
调用的路径,其它处理类调用注册逻辑一样。
继续来看CopyCommands.class
,其中除了registerCommands
,主要就是提供了七个静态公共类,我们这里对于get
请求,处理类就是Get.class
,继承了一个类叫CommandWithDestination
,这里查看Get.class
的代码,只是实现了processOptions
对参数进行设置,核心逻辑在它的父类中,继续进入父类CommandWithDestination
中,发现父类果然是继承自FsCommand
,这里主要看它的核心处理方法processPath
,调用顺序:processPath(PathData src)
->processPath(PathData src, PathData dst)
->copyFileToTarget(PathData src, PathData target)
,到了copyFileToTarget
方法这里的话,逻辑就十分明了了,贴出该部分代码:
throws IOException {
final boolean preserveRawXattrs =
checkPathsForReservedRaw(src.path, target.path);
src.fs.setVerifyChecksum(verifyChecksum);
InputStream in = null;
try {
in = src.fs.open(src.path);
copyStreamToTarget(in, target);
preserveAttributes(src, target, preserveRawXattrs);
} finally {
IOUtils.closeStream(in);
}
}
该部分代码先获取了系统设置目标路径下是否需要保存源文件的元数据信息(例如创建时间、所属用户、所属用户组等),然后获取源文件的输出流,并调用copyStreamToTarget
方法将输出流中内容写入到目标路径中,最后再调用preserveAttributes
进行基本信息元数据信息的填充。
参考文章:
https://blog.csdn.net/strongyoung88/article/details/68952248