5.1 DFSClient实现
5.1.1 构造方法
5.1.2 关闭方法
5.1.3 文件系统管理与配置方法
5.1.4 HDFS文件与目录操作方法
5.1.5 HDFS文件读写方法
5.1 DFSClient实现
HDFS目前提供了三个客户端接口,这三个接口分别是DistributedFileSystem、FsShell和DFSAdmin。
DistributedFileSystem:用来给用户开发基于HDFS的应用程序提供API
FsShell:用户通过shell命令执行常见的文件系统操作,调用的就是该接口,例如创建文件、删除文件、创建目录等
DFSAdmin:系统管理员管理HDFS的工具,例如执行升级、管理安全模式等操作。
DFSClient类实现了分布式系统客户端功能,是用户使用HDFS各项功能的起点。DFSClient会连接到HDFS,对外提供管理文件/目录、读写文件以及管理与配置HDFS系统等功能。
对于管理文件/目录以及管理与配置HDFS系统这两个功能,DFSClient不需要与Datanode进行交互,而是直接通过远程接口ClientProtocol调用Namenode提供的服务即可。而文件的读写功能,除了调用ClientProtocol与Namenode交互外,还需要通过流式接口DataTransferProtocol与Datanode交互传输数据。
DFSClient提供的接口方法有以下几类:
(1)、DFSClient的构造方法和关闭方法
(2)、管理与配置文件系统相关方法
(3)、操作HDFS文件与目录方法
(4)、读写HDFS文件方法
5.1.1 构造方法
有5个构造函数,其中参数为Configuration的构造函数已经被废弃,其他4个构造函数都调用了最后一个构造函数,如下图
最后一个构造函数的代码如下:
/**
* Create a new DFSClient connected to the given nameNodeUri or rpcNamenode.
* If HA is enabled and a positive value is set for
* {@link DFSConfigKeys#DFS_CLIENT_TEST_DROP_NAMENODE_RESPONSE_NUM_KEY} in the
* configuration, the DFSClient will use {@link LossyRetryInvocationHandler}
* as its RetryInvocationHandler. Otherwise one of nameNodeUri or rpcNamenode
* must be null.
*/
@VisibleForTesting
public DFSClient(URI nameNodeUri, ClientProtocol rpcNamenode,
Configuration conf, FileSystem.Statistics stats)
throws IOException {
// Copy only the required DFSClient configuration
this.dfsClientConf = new Conf(conf);
if (this.dfsClientConf.useLegacyBlockReaderLocal) {
LOG.debug("Using legacy short-circuit local reads.");
}
this.conf = conf;//HDFS配置信息
this.stats = stats;//Client状态统计信息,包括Client读、写字节数等
this.socketFactory = NetUtils.getSocketFactory(conf, ClientProtocol.class);
//当Client读写数据时,如果Datanode出现故障,是否进行Datanode替换的策略
this.dtpReplaceDatanodeOnFailure = ReplaceDatanodeOnFailure.get(conf);
//获取当前用户信息
this.ugi = UserGroupInformation.getCurrentUser();
this.authority = nameNodeUri == null? "null": nameNodeUri.getAuthority();
this.clientName = "DFSClient_" + dfsClientConf.taskId + "_" +
DFSUtil.getRandom().nextInt() + "_" + Thread.currentThread().getId();
provider = DFSUtil.createKeyProvider(conf);
if (LOG.isDebugEnabled()) {
if (provider == null) {
LOG.debug("No KeyProvider found.");
} else {
LOG.debug("Found KeyProvider: " + provider.toString());
}
}
int numResponseToDrop = conf.getInt(
DFSConfigKeys.DFS_CLIENT_TEST_DROP_NAMENODE_RESPONSE_NUM_KEY,
DFSConfigKeys.DFS_CLIENT_TEST_DROP_NAMENODE_RESPONSE_NUM_DEFAULT);
NameNodeProxies.ProxyAndInfo<ClientProtocol> proxyInfo = null;
AtomicBoolean nnFallbackToSimpleAuth = new AtomicBoolean(false);
if (numResponseToDrop > 0) {
// This case is used for testing.
LOG.warn(DFSConfigKeys.DFS_CLIENT_TEST_DROP_NAMENODE_RESPONSE_NUM_KEY
+ " is set to " + numResponseToDrop
+ ", this hacked client will proactively drop responses");
proxyInfo = NameNodeProxies.createProxyWithLossyRetryHandler(conf,
nameNodeUri, ClientProtocol.class, numResponseToDrop,
nnFallbackToSimpleAuth);
}
if (proxyInfo != null) {
this.dtService = proxyInfo.getDelegationTokenService();
this.namenode = proxyInfo.getProxy();
} else if (rpcNamenode != null) {
// This case is used for testing.
Preconditions.checkArgument(nameNodeUri == null);
this.namenode = rpcNamenode;
dtService = null;
} else {
Preconditions.checkArgument(nameNodeUri != null,
"null URI");
proxyInfo = NameNodeProxies.createProxy(conf, nameNodeUri,
ClientProtocol.class, nnFallbackToSimpleAuth);
this.dtService = proxyInfo.getDelegationTokenService();
this.namenode = proxyInfo.getProxy();
}
String localInterfaces[] =
conf.getTrimmedStrings(DFSConfigKeys.DFS_CLIENT_LOCAL_INTERFACES);
//本地接口地址
localInterfaceAddrs = getLocalInterfaceAddrs(localInterfaces);
if (LOG.isDebugEnabled() && 0 != localInterfaces.length) {
LOG.debug("Using local interfaces [" +
Joiner.on(',').join(localInterfaces)+ "] with addresses [" +
Joiner.on(',').join(localInterfaceAddrs) + "]");
}
//读取数据后,是否立即从操作系统缓冲区中删除
Boolean readDropBehind = (conf.get(DFS_CLIENT_CACHE_DROP_BEHIND_READS) == null) ?
null : conf.getBoolean(DFS_CLIENT_CACHE_DROP_BEHIND_READS, false);
//预读取字节数
Long readahead = (conf.get(DFS_CLIENT_CACHE_READAHEAD) == null) ?
null : conf.getLong(DFS_CLIENT_CACHE_READAHEAD, 0);
//写数据后,是否立即从操作系统缓冲区中删除
Boolean writeDropBehind = (conf.get(DFS_CLIENT_CACHE_DROP_BEHIND_WRITES) == null) ?
null : conf.getBoolean(DFS_CLIENT_CACHE_DROP_BEHIND_WRITES, false);
this.defaultReadCachingStrategy =
new CachingStrategy(readDropBehind, readahead);
this.defaultWriteCachingStrategy =
new CachingStrategy(writeDropBehind, readahead);
this.clientContext = ClientContext.get(
conf.get(DFS_CLIENT_CONTEXT, DFS_CLIENT_CONTEXT_DEFAULT),
dfsClientConf);
/*hedgedReadThresholdMillis保存触发"hedged read"机制的时长,
当Client发现一个数据块读取操作太慢时(读取时长超过hedgedReadThresholdMillis),
那么Client会启动另外一个并发操作读取数据块的另外一个副本,
之后Client会返回先完成读取副本的数据。*/
this.hedgedReadThresholdMillis = conf.getLong(
DFSConfigKeys.DFS_DFSCLIENT_HEDGED_READ_THRESHOLD_MILLIS,
DFSConfigKeys.DEFAULT_DFSCLIENT_HEDGED_READ_THRESHOLD_MILLIS);
int numThreads = conf.getInt(
DFSConfigKeys.DFS_DFSCLIENT_HEDGED_READ_THREADPOOL_SIZE,
DFSConfigKeys.DEFAULT_DFSCLIENT_HEDGED_READ_THREADPOOL_SIZE);
if (numThreads > 0) {
this.initThreadsNumForHedgedReads(numThreads);
}
//简单验证安全层
this.saslClient = new SaslDataTransferClient(
conf, DataTransferSaslUtil.getSaslPropertiesResolver(conf),
TrustedChannelResolver.getInstance(conf), nnFallbackToSimpleAuth);
}
构造函数完成两个功能:
(1)、初始化成员变量
(2)、获取Namenode的RPCProxy引用,供DFSClient远程调用Namenode的RPC方法
5.1.2 关闭方法
关闭方法为close,代码如下:
/**
* Close the file system, abandoning all of the leases and files being
* created and close connections to the namenode.
*/
@Override
public synchronized void close() throws IOException {
try {
if(clientRunning) {
//关闭所有正在进行写操作的IO流。
closeAllFilesBeingWritten(false);
//设置为false标志,停止DFSClient对外服务
clientRunning = false;
//停止租约管理
getLeaseRenewer().closeClient(this);
// close connections to the namenode,关闭与Namenode的RPC连接
closeConnectionToNamenode();
}
} finally {
if (provider != null) {
provider.close();
}
}
}
5.1.3 文件系统管理与配置方法
HDFS管理员通过DFSAdmin工具管理与配置HDFS,DFSAdmin也是通过持有DistributedFileSystem对象的引用,然后进一步调用DFSClient类提供的方法执行管理与配置操作的,整个流程比较简单,我们用DFSClient.rollEdits()方法为例,流程图如下:
DFSClient文件系统管理与配置方法对应关系图
DFSClient中还有许多命令是直接建立与Datanode或者Namenode的RPC连接,然后调用对应的RPC方法实现的,例如getDatanode、shutdownDatanode()等操作。
5.1.4 HDFS 文件与目录操作方法
除了管理与配置HDFS文件系统外,DFSClient的另外一个重要功能就是操作HDFS文件与目录,例如setPermission()、rename()、getFileInfo()、delete()等对文件/目录树的增、删、改、查等操作。
5.1.5 HDFS文件读写方法
DFSClient对文件进行读写操作,涉及的方法比较多,后面会进行详细讲解。