大数据学习---day05----hadoop02--------1补充配置(hadoop的环境变量配置,修改shell客户端默认操作的文件系统为hdfs系统,集群的批量启动(hdfs-site.xml中各参数的详解),查看那么node和datanode的日志信息,namenode和datanode的交互过程)2HDFS的客户端操作(上传,下载等等)
1.补充配置
1.1 Hadoop的环境变量的配置
vi /etc/profile
$PATH:表示取出前面的环境变量的配置,此处前面有java环境变量的配置,所以不需要再配置,若没有则需要
“:” 表示连接符号,功能和windows中的%一样
export: 类似public的作用,扩大作用范围的
1.2 修改shell客户端默认操作的文件系统为hdfs系统
hdfs文件系统提供了一套操作文件系统的shell客户端命令,但是其默认操作的是本地系统,如下
不加上namenode的路径,操作的是本地系统
加上namenode的地址,操作的就是hdfs的文件系统
但是每次都这样写就很麻烦,所以进行相应的配置,如下:
在core-site.xml文件中添加如下:
<property> <name>fs.defaultFS</name> <value>hdfs://doit01:9000/</value> </property>
这个时候执行 hdfs dfs -ls / 就能直接操作hdfs文件系统了
1.3 集群的批量启动
1.3.1 过程
直接在配置文件中的slaves文件中加入datanode的名字,如feng01 feng02 feng03
在执行start-dfs.sh, stop-dfs.sh的时候会读取这个文件 ,在配置的节点上分别启动datanode
1.3.2 知识点
在配置文件中有个slaves文件,slaves文件里记录的是集群里所有的datanode的主机名,那么它到底是怎么样作用的呢?slaves文件只作用在NameNode上面,比如我在slaves里面配置了
host1
host2
host3
三台机器,这时候如果突然间新增了一台机器,比如是host4,会发现在NN上host4也自动加入到集群里面了,HDFS的磁盘容量上来了,这下子不是出问题了?假如host4不是集群的机器,是别人的机器,然后配置的时候指向了NN,这时候NN没有做判断岂不是把数据也有可能写到host4上面?这对数据安全性影响很大。所以可以在hdfs-site.xml里面加限制。
dfs.hosts
/home/hadoop-2.0.0-cdh4.5.0/etc/hadoop/slaves
这相当于是一份对于DN的白名单,只有在白名单里面的主机才能被NN识别。配置了这个之后,就能排除阿猫阿狗的DN了。其实slaves文件里不一定要写主机名,最终的都是通过IP来判断,完全可以写一个IP就行。
dfs.hosts 預設值 : N/A 說明 : 預設不指定的狀況下,只要 datanodes 在 hdfs-site.xml 指定 namenode,在 mapred-site.xml 指定 jobtracker 的位址就可以加入這個 cluster。但是為了安全的考量,系統管理者可能要決定只有特定的 nodes 可以加入。此值是指定一個檔案位置,名字可自取,例如 : /etc/hadoop/conf/dfs-hosts,並列出所有可以連結 namenode 的機器清單。不在清單上的機器是沒有權限的。在 mapred-site.xml 裡也有個類似的值 mapred.hosts 來指定可以連 jobtracker 的機器清單。 dfs.hosts.exclude 預設值 : N/A 說明 : 當需要汰換或移除多台機器時會用到。理論上一台機器無預期的當機,Hadoop 會偵測並把該機器上的 blocks 搬到其他的 datanodes 上,並不需要系統管理員做額外的動作。但是停掉多台機器的情況下是有風險的,假設備份個數為 3 並停掉三台機器,則有一定的機率某些 blocks 正好只在這三台機器上,移掉之後資料也救不回來了。正確的做法是先告訴 namenode 這些機器將被移除,讓 namenode 把上面的資料全部備份到其他的 datanodes 上,再進行停機。跟 dfs.hosts 一樣,指定一個檔案位置,名字可自取,例如 : /etc/hadoop/conf/dfs-exclude-hosts,並列出所有需汰換的機器清單。設定後要執行以下的指令通知 namenode 做搬資料的動作。
hdfs-site.xml文件参数详解
NameNode dfs.name.dir 預設值 : ${hadoop.tmp.dir}/dfs/name 說明 : 指定本機上存取 fsimage 及 editlog 的目錄,這個目錄非常的重要,如果損毀就無法存取 HDFS 的資料了,所以不建議放在 ${hadoop.tmp.dir} 目錄下。更好的做法是用 “," 指定多個目錄,Hadoop 會複製 fsimage 的資料到所有的目錄下,如果其中一個目錄損毀 Hadoop 會自動使用正常的目錄並把對的資料再複製到損毀的目錄下。 指定多個目錄後在 HDFS portal 會看到多個目錄,正常狀況會是 Active,當損毀時會變成 Inactive dfs.namenode.logging.level 預設值 : info 說明 : 這個值是指定 namenode 的 logging level。其他的值有 dir : 看 namenode server 的變化 block : 看 blocks 新增刪除或 replication 的變化 all : 顯示全部的 log 除非是為了 debug,不然不建議用其他的等級,會造成 Hadoop 的 log 檔案太大。 dfs.http.address 預設值 : 0.0.0.0:50070 說明 : Web UI 用的 port。除非是為了 security 的考量才會需要改 binding 的 IP/Port,不然不需要改這個值。 dfs.https.enable 預設值 : false 說明 : namenode 預設並沒有啟動 https,在設定 https 的 IP/Port 之前要先確定這個值設為 true。 dfs.https.address 預設值 : 0.0.0.0:50470 說明 : Web UI 用的 port,用 https protocol。除非是為了 security 的考量才會需要改 binding 的 IP/Port,不然不需要改這個值。 dfs.replication 預設值 : 3 說明 : 預設 blocks 的備份數量。如果不需要太多的備份或 cluster 比較小,可以改為 2。Client 端也可以根據使用狀況自行更改這個值。只是如果所設的值小於 dfs.replication,在執行 hadoop fsck 指令時會看到這個 block 被標示為 Under-Replicated Blocks。至於備份的機制可以參考 Hadoop 參數設定 – core-site.xml 裡的 topology.script.file.name 說明。 dfs.replication.min 預設值 : 1 說明 : 不需要特別改這個值。因為並不是所有在 HDFS 上的資料都需要有 3 份備份,這可以由 client 來決定。如果對資料備份非常敏感可以把這個值設為跟 dfs.replication 一樣。 dfs.replication.max 預設值 : 512 說明 : 因為 client 可以自行決定每個 block 要有幾份備份,為了怕誤操作導致備份過多而影響整個 cluster 的使用量,建議給一個小一點的值,例如 10。 dfs.block.size 預設值 : 67108864(byte) 說明 : 預設每個 block 是 64MB。如果確定存取的檔案都很大可以改為 134217728(128MB)。Client 也可自行決定要使用的 block size 而不需要更改整個 cluster 的設定。 1 hadoop fs -D dfs.block.size=134217728 -put local_name remote_location dfs.safemode.threshold.pct 預設值 : 0.999f 說明 : Hadoop 在啟動時預設會進入 safe mode,也就是唯讀模式,這時是不能寫入資料的。只有當 99.9% 的 blocks 達到最小的 dfs.replication.min 數量(預設是 1)才會離開 safe mode。在 dfs.replication.min 設的比較大或 data nodes 數量較多時會等比較久。 下面討論兩個極端的狀況 設為大於 1 : 表示永遠不會離開 safe mode,這在當 Hadoop cluster 需要做 migration 時很好用,即可繼續提供讀取服務,又可防止使用者寫入資料導至 migration 不完全。 設為 0 : 表示不會啟動 safe mode。在 local 測試時會非常的方便,不然常常需要等一段時間或直接執行 1 hadoop dfsadmin -safemode leave 才能離開 safe mode。 dfs.hosts 預設值 : N/A 說明 : 預設不指定的狀況下,只要 datanodes 在 hdfs-site.xml 指定 namenode,在 mapred-site.xml 指定 jobtracker 的位址就可以加入這個 cluster。但是為了安全的考量,系統管理者可能要決定只有特定的 nodes 可以加入。此值是指定一個檔案位置,名字可自取,例如 : /etc/hadoop/conf/dfs-hosts,並列出所有可以連結 namenode 的機器清單。不在清單上的機器是沒有權限的。在 mapred-site.xml 裡也有個類似的值 mapred.hosts 來指定可以連 jobtracker 的機器清單。 dfs.hosts.exclude 預設值 : N/A 說明 : 當需要汰換或移除多台機器時會用到。理論上一台機器無預期的當機,Hadoop 會偵測並把該機器上的 blocks 搬到其他的 datanodes 上,並不需要系統管理員做額外的動作。但是停掉多台機器的情況下是有風險的,假設備份個數為 3 並停掉三台機器,則有一定的機率某些 blocks 正好只在這三台機器上,移掉之後資料也救不回來了。正確的做法是先告訴 namenode 這些機器將被移除,讓 namenode 把上面的資料全部備份到其他的 datanodes 上,再進行停機。跟 dfs.hosts 一樣,指定一個檔案位置,名字可自取,例如 : /etc/hadoop/conf/dfs-exclude-hosts,並列出所有需汰換的機器清單。設定後要執行以下的指令通知 namenode 做搬資料的動作。 1 hadoop dfsadmin -refreshNodes 進度可以在 web UI 上看到,當該 datanodes 的狀態顯示為 “Decommissioned" 表示可以安全的移除機器了。 dfs.support.append 預設值 : false 說明 : 指定是否可在 HDFS 原有檔案內容之後加入新資料。看 hfds-default.xml 裡對這個參數的說明是有 bug “This is currently set to false because there are bugs in the “append code" and is not supported in any prodction cluster."。但是 HBase Configuration 裡另外說明了以上的資訊是過時的,在 Cloudera 及 MapR 的版本都已經加入了這個功能。如果有使用 HBase,為了避免資料遺失,請把這個值設為 true。 dfs.namenode.handler.count 預設值 : 10 說明 : 設定 namenode server threads 的數量,這些 threads 會用 RPC 跟其他的 datanodes 溝通。當 datanodes 數量太多時會發現很容易出現 RPC timeout,解決方法是提升網路速度或調高這個值,但要注意的是 thread 數量多也表示 namenode 吃的記憶體也隨著增加。在 Hadoop Cluster Setup 這篇文章裡的提到 900 個 nodes 只需要設成 40,但是在個人經驗裡是 100 個 nodes 配 100 個 threads。 dfs.namenode.keytab.file 預設值 : N/A 說明 : 當 core-site.xml 裡的 hadoop.security.authentication 參數設為 “kerberos" 時就要指定 keytab 的位置。例如 : /etc/hadoop/conf/hdfs.keytab dfs.namenode.kerberos.principal 預設值 : N/A 說明 : 指定 kerberos principal 名稱,這在產生 keytab 檔案時會指定,一般常用的命名規則是 hdfs/_HOST@KERBEROS-REALM.COM Secondary NameNode dfs.secondary.namenode.keytab.file 預設值 : N/A 說明 : 當 core-site.xml 裡的 hadoop.security.authentication 參數設為 “kerberos" 時就要指定 keytab 的位置。例如 : /etc/hadoop/conf/hdfs.keytab dfs.secondary.namenode.kerberos.principal 預設值 : N/A 說明 : 指定 kerberos principal 名稱,這在產生 keytab 檔案時會指定,一般常用的命名規則是 hdfs/_HOST@KERBEROS-REALM.COM DataNode dfs.data.dir 預設值 : ${hadoop.tmp.dir}/dfs/data 說明 : 指定本機上放 data nodes 資料的目錄,如果要指定多個目錄(volumes) 可用 “," 分隔。在 production 環境會指定多個,並設定 dfs.datanode.failed.volumes.tolerated。一般來說,多個目錄會對應到系統上不同的 partitions,不同的硬碟。設定多個可加快存取速度,及避免硬碟壞掉需要抽換用。 dfs.datanode.address 預設值 : 0.0.0.0:50010 說明 : datanode service 聽的 port,用來傳輸資料用。除非是為了 security 的考量才會需要改 binding 的 IP/Port,不然不需要改這個值。 dfs.datanode.http.address 預設值 : 0.0.0.0:50075 說明 : Web UI 用的 port。除非是為了 security 的考量才會需要改 binding 的 IP/Port,不然不需要改這個值。 dfs.datanode.handler.count 預設值 : 3 說明 : 指定 data node 上用的 thread 數量。在 production 的環境建議調到 100。 dfs.datanode.max.xcievers 預設值 : 256 說明 : 這個值是指定 datanode 可同時處理的较大檔案數量。但是預設值很小,當多個或一個大型程式存取時會發生下面的錯誤訊息 1 10/12/08 20:10:31 INFO hdfs.DFSClient: Could not obtain block blk_XXXXXXXXXXXXXXXXXXXXXX_YYYYYYYY from any node: java.io.IOException: 2 No live nodes contain current block. Will get new block locations from namenode and retry... 以使用 HBase 為例,建議值是 4096。如果還有多個程式存取可再乘 2。 dfs.datanode.failed.volumes.tolerated 預設值 : 0 說明 : 這個值要對應 dfs.data.dir 參數設定的目錄個數,0 表示只要有任何一個 volume 壞掉 data nodes 就會被強制停掉。假設掛載 n 個 volumns,Hadoop 會確定 n – dfs.datanode.failed.volumes.tolerated 不能小於 0。設定錯誤在啟動 data node 會看到下面的訊息 01 2011-08-27 11:53:03,785 ERROR org.apache.hadoop.hdfs.server.datanode.DataNode: org.apache.hadoop.util.DiskChecker$DiskErrorException: Invalid value for validVolsRequired : -1 , Current valid volumes: 1 02 at org.apache.hadoop.hdfs.server.datanode.FSDataset.<init>(FSDataset.java:906) 03 at org.apache.hadoop.hdfs.server.datanode.DataNode.startDataNode(DataNode.java:373) 04 at org.apache.hadoop.hdfs.server.datanode.DataNode.<init>(DataNode.java:282) 05 at org.apache.hadoop.hdfs.server.datanode.DataNode.makeInstance(DataNode.java:1544) 06 at org.apache.hadoop.hdfs.server.datanode.DataNode.instantiateDataNode(DataNode.java:1484) 07 at org.apache.hadoop.hdfs.server.datanode.DataNode.createDataNode(DataNode.java:1502) 08 at org.apache.hadoop.hdfs.server.datanode.DataNode.secureMain(DataNode.java:1627) 09 at org.apache.hadoop.hdfs.server.datanode.SecureDataNodeStarter.start(SecureDataNodeStarter.java:103) 10 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 11 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 12 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 13 at java.lang.reflect.Method.invoke(Method.java:597) 14 at org.apache.commons.daemon.support.DaemonLoader.start(DaemonLoader.java:177) 如果 data volumns 有 4 個,dfs.datanode.failed.volumes.tolerated 可設為 2。表示當有 2 個硬碟壞掉時 data nodes 還是可以正常運作。這時只要換壞掉硬碟即可,並不需要停掉 data nodes。 dfs.datanode.data.dir.perm 預設值 : 700 說明 : 這個值是設定 data node 寫資料到 local disk 上的權限。使用 POSIX 表示法。在 production 上為了 security 考量,不建議改這個參數。如果是測試環境為了方便其他 users 用工具分析資料,可以改成 755。 dfs.datanode.du.reserved 預設值 : 0(byte) 說明 : 預設值表示 data nodes 會使用整個 volumns,寫滿之後會導致無法再寫入 M/R jobs 或啟動 data nodes 時的暫存檔。如果還有其他程式共用這些目錄也會受到影響。建議保留至少 1073741824(1G) 的空間。 dfs.datanode.keytab.file 預設值 : N/A 說明 : 當 core-site.xml 裡的 hadoop.security.authentication 參數設為 “kerberos" 時就要指定 keytab 的位置。例如 : /etc/hadoop/conf/hdfs.keytab dfs.datanode.kerberos.principal 預設值 : N/A 說明 : 指定 kerberos principal 名稱,這在產生 keytab 檔案時會指定,一般常用的命名規則是 hdfs/_HOST@KERBEROS-REALM.COM Etc dfs.balance.bandwidthPerSec 預設值 : 1048576(byte) 說明 : 這個值是決定 file blocks 從一個 data node 搬到另一個 data node 的速度, 預設為 1MB。主要是用在 re-balance,如果覺得執行速度太慢可以調整這個參數加快 blocks 的搬移。但是這也表示會多佔頻寬,可能會影響正常 M/R jobs 或 applications 的執行。建議值為 4194304(4MB)
1.4 查看那么node和datanode的日志信息
- clusterID:集群版本,datanode和namenode上要保持一致
- storeageID:每个datanode的唯一标识
可见datanode和namenode上的集群I版本是要一样的
问题一
当元数据(即/opt/hdpdata)被删除以后namenode找不到fsImage会闪退,解决办法
当我们重新格式化namenode以后 ,会生成新的集群版本 , 那么原来的datanode就无法完成注册 ,闪退
问题二
如果克隆了一台datanode角色的机器,会出现datanode无法注册,因为两个克隆机器的storeageID相同,namenode只能识别一个
1.5 namenode和datanode的交互过程
注意:心跳汇报时,datanode会从namenode那领取任务,如备份任务。
当一个节永远点宕机时,name是如何剔除掉这个节点的呢?
如上图,首先namenode会等10次datanode的心跳汇报时间,10次后还没收到来自此节点的心跳汇报,namenode隔5min就会向datanode发送校验信息,若没回复,再隔5min,namenode又会向datanode发送校验信息,若还是收不到回复,namenode就会将该节点剔除这个集群,不在管理这个节点。
2. HDFS的客户端操作
HDFS提供了两套供客户端使用的API(shell客户端和java客户端)
2.1 HDFS的shell客户端
知识点1处讲过如何将hdfs的客户端默认操作改成HDFS系统
当使用hdfs dfs命令时,得到如下结果
如:创建文件夹: hdfs dfs -mkdir /aa
2.2 HDFS的java客户端
2.2.1 获取java客户端对象
FileSystem fs = FileSystem.get(URI uri, Configuration conf, String user);
参数一: 指定操作的HDFS系统的位置 就是namenode的位置
参数二: 配置对象 用户可以使用这个对象定制化配置 , 比如设置副本的个数 数据切块存储的大小
参数三 : 执行命令的用户的用户名 默认是自己的主机名
public class GetHdfsClient { public static void main(String[] args) throws Exception { // 指定操作HDFS系统的位置,就是namenode的位置 URI uri = new URI("hdfs://feng01:9000/"); // 配置对象,用户可以使用这个对象定制化配置,比如设置副本的个数 Configuration conf = new Configuration(); // java操作hdfs的客户端对象 FileSystem fs = FileSystem.get(uri, conf, "root"); // 操作fs,在hdfs中的所有的文件或者文件夹抽象对象是path 即IO File Path path = new Path("/jj"); boolean b = fs.mkdirs(path); if(b) { System.out.println("创建文件夹成功"); } fs.close(); } }
2.2.2 Configuration详解
设置对象可以设置副本的个数,block的大小,以及系统读取配置文件的顺序
(1)若不进行conf配置,自动的设置默认的配置信息=====>副本个数为3,block大小为128
如hdfs-default.xml文件中
(2)自动读取类路径下的默认配置文件 hdfs-site.xml core-site.xml mapred-site.xml
(3)使用conf对象在程序中设置(范围小),优先级最高
confset > 本项目的配置文件 >默认的配置信息
案例:
a. 不进行conf设置
public class ConfigurationDetail { public static void main(String[] args) throws Exception { URI uri = new URI("hdfs://feng01:9000/"); Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(uri, conf, "root"); // 上传文件,参数1 本地路径 参数2 hdfs上的路径 fs.copyFromLocalFile(new Path("E:/javafile/wc1.txt"), new Path("/x/haha.txt")); fs.close(); } }
b . 进行conf配置,即在项目中创建conf文件夹,并创建一个hdfs-site.xml,内容如下
java代码同上(hdfs中的文件改成haha1.txt),结果如下
c 使用conf对象在程序中进行设置,优先级最高,但作用范围最小
Configuration conf = new Configuration(); // 设置上传副本的个数 key value conf.set("dfs.replication", "5"); // 设置物理块的大小 conf.set("dfs.blocksize", "256M");
2.2.3 上传
参数一: delsrc, 是否删除源文件,true表示删除
参数二:是否覆盖
参数三:本地文件
参数四:HDFS系统的文件历经
2.2.4 下载
在将hdfs中的数据下载到windows中,需要在windows中安装hdp环境,将分布式流的数据写到磁盘中需要使用hdp中的转换(需要hdp环境,),但是发现第三种方法不配置hadoop也能下载成功,暂时不知道为什么
参数一:是否删除hdfs中的源文件
参数2: hdfs中的路径
参数3 :本地路径
参数4 :是否在本地生成一个hdp内部的校验文件 默认是false生成 true不生成
环境变量的配置:
(1)配置HADOOP_HOME=hadoop的路径
(2) Path中添加 %HADOOP_HOME%/bin
2.2.5 创建文件夹
2.2.6 删除
public class Delete { public static void main(String[] args) throws Exception { FileSystem fs = HdfsUtils.getFs(); Path path = new Path("/a"); if (fs.exists(path)) { /* * 参数一 hdfs的路径 参数二 递归的删除文件夹中的内容 */ boolean b = fs.delete(path, true); System.out.println(b ? "成功" : "失败"); }else { System.out.println("文件不存在"); } fs.close(); } }
2.2.7 读取文件内容
字节流
public class ReadDataFromHdfsFile { public static void main(String[] args) throws Exception { //创建java操作hdfs系统的对象 FileSystem fs = HdfsUtils.getFs(); FSDataInputStream inputStream = fs.open(new Path("/word.txt")); byte[] b = new byte[2048]; int len = 0; while((len=inputStream.read(b))!=-1) { System.out.println(new String(b,0,len)); } fs.close(); } }
包装成缓冲字符流
public class ReadDataFromHdfsFile { public static void main(String[] args) throws Exception { //创建java操作hdfs系统的对象 FileSystem fs = HdfsUtils.getFs(); // 获取文件的输入流,字节流 FSDataInputStream fis = fs.open(new Path("/word.txt")); // 读取的是文本内容,可以用字符流包装 BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String line = null; while((line=br.readLine())!=null) { System.out.println(line); } } }
读取的结果如下:
注意:此处若使用seek(i),则表示跳过i个字节(hdfs支持随机读),如代码变成如下:
这可以用于多台机器同时读取一个大文件
如下图,假设有一个30G的文件,使用3台机器去读取这个文件
2.2.8 判断文件夹是否存在
2.2.9 向文件夹中写入数据(不常用)
支持追加,不支持随机写,建议不要修改文件的内容(一般不用这功能)
原因:HDFS的定位是存储海量数据,而不是对数据进行写操作,其一般是一次存入,多次取出
- append 向一个已经存在的文件中追加内容
- create 创建文件再追加内容
public class WriteDataToHdfsFile { public static void main(String[] args) throws Exception { FileSystem fs = HdfsUtils.getFs(); // fs.append(f) // 追加内容到文件 文件存在 // fs.create(f) ; 创建文件再追加内容 /* * FSDataOutputStream fout = fs.append(new Path("/word.doc")); * fout.writeUTF("hello 熊大!"); // 刷写 fout.flush(); fout.close(); */ Path path = new Path("/word2.doc"); if(fs.exists(path)) { FSDataOutputStream fout = fs.append(new Path("/word.doc")); }else { FSDataOutputStream fout = fs.create(new Path("/word2.doc")); fout.writeUTF("hello Jerry"); fout.flush(); } fs.close(); } }
2.2.10 查看内容列表
此处查看的是HDFS分布式系统的根目录,如下
第一种形式(listFiles):只能查看文件,不能查看文件夹
代码:
public class seeList { public static void main(String[] args) throws Exception { FileSystem fs = HdfsUtils.getFs(); // 得到的是一个迭代器 RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true); while(listFiles.hasNext()) { LocatedFileStatus next = listFiles.next(); // 获取文件的路径 Path path = next.getPath(); System.out.println(path); } } }
运行结果:
除此之外,还可以查看更多的内容,如下:
public class SeeList { public static void main(String[] args) throws Exception { FileSystem fs = HdfsUtils.getFs(); // 参数一 文件夹 参数二 是否递归查看 文件 RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/aa"), true); while(listFiles.hasNext()) { // 遍历的每个文件 LocatedFileStatus next = listFiles.next(); // 获取文件的路径 Path path = next.getPath(); next.getAccessTime(); // 上次访问的时间 // 获取每个文件块的消息 每个块数据在哪个机器上 0,67,doit03,doit02,doit01 BlockLocation[] locations = next.getBlockLocations(); for (BlockLocation blockLocation : locations) { System.out.println(blockLocation); } next.getBlockSize(); // 每个文件块的大小 next.getLen() ; //文件的真正的大小 next.getModificationTime() ;// 上次修改的时间 next.getReplication() ; //副本的个数 next.getPermission() ; // 获取权限 System.out.println(path); } fs.close(); } }
第二种形式(listStatus):既可以查看文件,也可以查看文件夹
public class seeListStatus { public static void main(String[] args) throws Exception { FileSystem fs = HdfsUtils.getFs(); // 这个数组包含了文件夹的很多信息,一个数组代表一个文件夹 FileStatus[] listStatus = fs.listStatus(new Path("/")); for (FileStatus fileStatus : listStatus) { // 打印文件夹的信息 System.out.println(fileStatus); //获取文件或者文件夹的路径 Path path = fileStatus.getPath(); if(!fs.isDirectory(path)) { // 是文件夹 ==> if(fs.isFile(path)) // 获取文件的消息 System.out.println(path); } } } }
打印部分结果
数组部分(包含的信息如下):
FileStatus{path=hdfs://feng01:9000/feng; isDirectory=true; modification_time=1572809795905; access_time=0; owner=root; group=supergroup; permission=rwxr-xr-x; isSymlink=false}
3 HDFS读写数据流程
3.1 上传(写)
- block
文件上传前需要分块,这个块就是block,一般为128M,当然你可以去改,不过不推荐。因为块太小,寻址世家占比过高。块太大,map任务数太少,作业执行速度变慢。它是最大的一个单位
- packet
packet是第二大的单位,它是client端向DataNode,或DataNode的PipLine之间数据传输的基本单位,默认是64KB
- chunk
chunk是最小的单位,它是client向DataNode或者是DataNode的PipLine之间进行数据校验的基本单位,默认512Byte,因为用作校验,故每个chunk需要带有4Byte的校验位。所以实际每个chunk写入packet的大小为516Byte。由此可见真是数据与校验值数据的比值约为128:1
基本流程:
(1)请求namenode上传数据(上传到哪去,副本的个数,物理块的大小)
(2)namenode接收到请求,计算数据存储的块的个数,以及集群中各个datanode的数据存储能力
(3)划分存储的任务,记录元数据,并回复客户端准备工作做好了,可以上传数据了
(4)客户端接收到请求许可之后,开始请求上传第一块文件,namenode返回给客户端第一块数据的原信息(如:blkID:host 0 128M )
(5)接收到相应的元数据信息后,客户端请求离自己最近的一个机器(有个算法裘最近机器)建立连接通道
(6)读取上传第一块数据,存储在datanode节点上,集群中存储其他两个副本(client每向第一个DataNode写入一个packet,这个packet便会直接在pipeline里传给第二个、第三个…DataNode。 )
(注:并不是写好一个块或一整个文件后才向后分发)
(7)请求上传第二,第三。。。。。。(数据是一块一块传的,并不是分布式的)
(8)每个datanode写完一个块后,会返回确认信息
(注:并不是每写完一个packet后就返回确认信息,个人觉得因为packet中的每个chunk都携带校验信息,没必要每写一个就汇报一下,这样效率太慢。正确的做法是写完一个block块后,对校验信息进行汇总分析,就能得出是否有块写错的情况发生)
(9)写完数据,关闭输出流
(10)发送完成信号给NameNode(所有datanode写完)
(注:发送完成信号的时机取决于集群是强一致性还是最终一致性,强一致性则需要所有DataNode写完后才向NameNode汇报。最终一致性则其中任意一个DataNode写完后就能单独向NameNode汇报,HDFS一般情况下都是强调强一致性)
3.2 下载(读)
(1)客户端向namenode请求下载文件,namenode通过查询元数据,找到文件块所在的datanode地址
(2)挑选一台datanode(就近原则)服务器,请求读取数据
(3)datanode开始传输数据给客户端
(4)客户端以packet为单位接受,先在本地缓存,然后写入目标文件