HDFS的写数据过程分析

HDFS的写数据过程分析

我们通过FileSystem类可以操控HDFS, 那我们就从这里开始分析写数据到HDFS的过程。
在我们向 HDFS 写文件的时候,调用的是 FileSystem.create(Path path)方法,我们查看这个方法的源码,通过跟踪内部的重载方法,可以找到
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Opens an FSDataOutputStream at the indicated Path with write-progress 
  3.  * reporting. 
  4.  * @param f the file name to open 
  5.  * @param permission 
  6.  * @param overwrite if a file with this name already exists, then if true, 
  7.  *   the file will be overwritten, and if false an error will be thrown. 
  8.  * @param bufferSize the size of the buffer to be used. 
  9.  * @param replication required block replication for the file. 
  10.  * @param blockSize 
  11.  * @param progress 
  12.  * @throws IOException 
  13.  * @see #setPermission(Path, FsPermission) 
  14.  */  
  15. public abstract FSDataOutputStream create(Path f,  
  16.     FsPermission permission,  
  17.     boolean overwrite,  
  18.     int bufferSize,  
  19.     short replication,  
  20.     long blockSize,  
  21.     Progressable progress) throws IOException;  
这个方法是抽象类,没有实现。那么我们只能向他的子类寻找实现。FileSystem 有个子类是 DistributedFileSystem,在我们的伪分布环境下使用的就是这个类。我们可以看到DistributedFileSystem 的这个方法的实现
点create方法 按ctrl+T 点击DistributedFileSystem 进进去了:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public FSDataOutputStream create(Path f, FsPermission permission,  
  2.   boolean overwrite,  
  3.   int bufferSize, short replication, long blockSize,  
  4.   Progressable progress) throws IOException {  
  5.   
  6.   statistics.incrementWriteOps(1);  
  7.   return new FSDataOutputStream  
  8.      (dfs.create(getPathName(f), permission,  
  9.                  overwrite, true, replication, blockSize, progress, bufferSize),  
  10.       statistics);  
  11. }  
注意返回值 FSDataOutputStream。这个返回值对象调用了自己的构造方法, 构造方法的第一个参数是 dfs.create()方法。 我们关注一下这里的 dfs 对象是谁,create 方法做了什么事情。现在进入这个方法的实现,
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Create a new dfs file with the specified block replication  
  3.  * with write-progress reporting and return an output stream for writing 
  4.  * into the file.   
  5.  *  
  6.  * @param src stream name 
  7.  * @param permission The permission of the directory being created. 
  8.  * If permission == null, use {@link FsPermission#getDefault()}. 
  9.  * @param overwrite do not check for file existence if true 
  10.  * @param createParent create missing parent directory if true 
  11.  * @param replication block replication 
  12.  * @return output stream 
  13.  * @throws IOException 
  14.  * @see ClientProtocol#create(String, FsPermission, String, boolean, short, long) 
  15.  */  
  16. public OutputStream create(String src,   
  17.                            FsPermission permission,  
  18.                            boolean overwrite,   
  19.                            boolean createParent,  
  20.                            short replication,  
  21.                            long blockSize,  
  22.                            Progressable progress,  
  23.                            int buffersize  
  24.                            ) throws IOException {  
  25.   checkOpen();  
  26.   if (permission == null) {  
  27.     permission = FsPermission.getDefault();  
  28.   }  
  29.   FsPermission masked = permission.applyUMask(FsPermission.getUMask(conf));  
  30.   LOG.debug(src + ": masked=" + masked);  
  31.   final DFSOutputStream result = new DFSOutputStream(src, masked,  
  32.       overwrite, createParent, replication, blockSize, progress, buffersize,  
  33.       conf.getInt("io.bytes.per.checksum"512));  
  34.   beginFileLease(src, result);  
  35.   return result;  
  36. }  
    final DFSOutputStream result = new DFSOutputStream(src, masked,
返回值创建的对象。这个类有什么神奇的地方吗?我们看一下他的(点击DFSOutputStream)源码,
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Create a new output stream to the given DataNode. 
  3.  * @see ClientProtocol#create(String, FsPermission, String, boolean, short, long) 
  4.  */  
  5. DFSOutputStream(String src, FsPermission masked, boolean overwrite,  
  6.     boolean createParent, short replication, long blockSize, Progressable progress,  
  7.     int buffersize, int bytesPerChecksum) throws IOException {  
  8.   this(src, blockSize, progress, bytesPerChecksum, replication);  
  9.   
  10.   computePacketChunkSize(writePacketSize, bytesPerChecksum);  
  11.   
  12.   try {  
  13.     // Make sure the regular create() is done through the old create().  
  14.     // This is done to ensure that newer clients (post-1.0) can talk to  
  15.     // older clusters (pre-1.0). Older clusters lack the new  create()  
  16.     // method accepting createParent as one of the arguments.  
  17.     if (createParent) {  
  18.       namenode.create(  
  19.         src, masked, clientName, overwrite, replication, blockSize);  
  20.     } else {  
  21.       namenode.create(  
  22.         src, masked, clientName, overwrite, false, replication, blockSize);  
  23.     }  
  24.   } catch(RemoteException re) {  
  25.     throw re.unwrapRemoteException(AccessControlException.class,  
  26.                                    FileAlreadyExistsException.class,  
  27.                                    FileNotFoundException.class,  
  28.                                    NSQuotaExceededException.class,  
  29.                                    DSQuotaExceededException.class);  
  30.   }  
  31.   streamer.start();  
  32. }  
可 以 看 到 , 这 个 类 是 DFSClient 的 内 部 类 。 在 类 内 部 通 过 调用namenode.create()方法创建了一个输出流。我们再看一下 namenode 对象是什么类型
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public final ClientProtocol namenode;  
可以看到 namenode 其实是 ClientProtocal 接口。那么,这个对象是什么时候创建的?点击outline点击下面这个DFSClient:
[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. /**  
  2.  * Create a new DFSClient connected to the given nameNodeAddr or rpcNamenode. 
  3.  * Exactly one of nameNodeAddr or rpcNamenode must be null. 
  4.  */  
  5. DFSClient(InetSocketAddress nameNodeAddr, ClientProtocol rpcNamenode,  
  6.     Configuration conf, FileSystem.Statistics stats)  
  7.   throws IOException {  
  8.   this.conf = conf;  
  9.   this.stats = stats;  
  10.   this.nnAddress = nameNodeAddr;  
  11.   this.socketTimeout = conf.getInt("dfs.socket.timeout",   
  12.                                    HdfsConstants.READ_TIMEOUT);  
  13.   this.datanodeWriteTimeout = conf.getInt("dfs.datanode.socket.write.timeout",  
  14.                                           HdfsConstants.WRITE_TIMEOUT);  
  15.   this.timeoutValue = this.socketTimeout;  
  16.   this.socketFactory = NetUtils.getSocketFactory(conf, ClientProtocol.class);  
  17.   // dfs.write.packet.size is an internal config variable  
  18.   this.writePacketSize = conf.getInt("dfs.write.packet.size"64*1024);  
  19.   this.maxBlockAcquireFailures = getMaxBlockAcquireFailures(conf);  
  20.   
  21.   this.hdfsTimeout = Client.getTimeout(conf);  
  22.   ugi = UserGroupInformation.getCurrentUser();  
  23.   this.authority = nameNodeAddr == null"null":  
  24.     nameNodeAddr.getHostName() + ":" + nameNodeAddr.getPort();  
  25.   String taskId = conf.get("mapred.task.id""NONMAPREDUCE");  
  26.   this.clientName = "DFSClient_" + taskId + "_" +   
  27.       r.nextInt()  + "_" + Thread.currentThread().getId();  
  28.   
  29.   defaultBlockSize = conf.getLong("dfs.block.size", DEFAULT_BLOCK_SIZE);  
  30.   defaultReplication = (short) conf.getInt("dfs.replication"3);  
  31.   
  32.   if (nameNodeAddr != null && rpcNamenode == null) {  
  33.     this.rpcNamenode = createRPCNamenode(nameNodeAddr, conf, ugi);  
  34.    this.namenode = createNamenode(this.rpcNamenode, conf);  
  35.   } else if (nameNodeAddr == null && rpcNamenode != null) {  
  36.     //This case is used for testing.  
  37.     this.namenode = this.rpcNamenode = rpcNamenode;  
  38.   } else {  
  39.     throw new IllegalArgumentException(  
  40.         "Expecting exactly one of nameNodeAddr and rpcNamenode being null: "  
  41.         + "nameNodeAddr=" + nameNodeAddr + ", rpcNamenode=" + rpcNamenode);  
  42.   }  
  43.   // read directly from the block file if configured.  
  44.   this.shortCircuitLocalReads = conf.getBoolean(  
  45.       DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_KEY,  
  46.       DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_DEFAULT);  
  47.   if (LOG.isDebugEnabled()) {  
  48.     LOG.debug("Short circuit read is " + shortCircuitLocalReads);  
  49.   }  
  50.   this.connectToDnViaHostname = conf.getBoolean(  
  51.       DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME,  
  52.       DFSConfigKeys.DFS_CLIENT_USE_DN_HOSTNAME_DEFAULT);  
  53.   if (LOG.isDebugEnabled()) {  
  54.     LOG.debug("Connect to datanode via hostname is " + connectToDnViaHostname);  
  55.   }  
  56.   String localInterfaces[] =  
  57.     conf.getStrings(DFSConfigKeys.DFS_CLIENT_LOCAL_INTERFACES);  
  58.   if (null == localInterfaces) {  
  59.     localInterfaces = new String[0];  
  60.   }  
  61.   this.localInterfaceAddrs = getLocalInterfaceAddrs(localInterfaces);  
  62.   if (LOG.isDebugEnabled() && 0 != localInterfaces.length) {  
  63.     LOG.debug("Using local interfaces [" +  
  64.         StringUtils.join(",",localInterfaces)+ "] with addresses [" +  
  65.         StringUtils.join(",",localInterfaceAddrs) + "]");  
  66.   }  
  67. }  

可以看到34行namenode 对象是在 DFSClient 的构造函数调用时创建的,即当 DFSClient 对象存在的时候,namenode 对象已经存在了。

至此,我们可以看到,使用 FileSystem 对象的 api 操纵 HDFS,其实是通过 DFSClient 对象访问 NameNode 中的方法操纵 HDFS 的。 这里的 DFSClient 是 RPC 机制的客户端, NameNode是 RPC 机制的服务端的调用对象,整个调用过程如图 


在整个过程中,DFSClient 是个很重要的类,从名称就可以看出,他表示 HDFS 的 Client,是整个 HDFS 的 RPC 机制的客户端部分。我们对 HDFS 的操作,是通过 FileSsytem 调用的DFSClient 里面的方法。FileSystem 是封装了对 DFSClient 的操作,提供给用户使用的
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值