文章目录
7.HDFS的javaAPI操作
由于cdh版本的所有的软件涉及版权的问题,所以并没有将所有的jar包托管到maven仓库当中去,而是托管在了CDH自己的服务器上面,所以我们默认去maven的仓库下载不到,需要自己手动的添加repository去CDH仓库进行下载,以下两个地址是官方文档说明,请仔细查阅
CDH仓库下载地址:Maven Artifacts for CDH 5.14.x
创建maven工程并导入jar包
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.Hadoop</groupId>
<artifactId>Hadoop-client</artifactId>
<version>2.6.0-mr1-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.Hadoop</groupId>
<artifactId>Hadoop-common</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.Hadoop</groupId>
<artifactId>Hadoop-hdfs</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.Hadoop</groupId>
<artifactId>Hadoop-mapreduce-client-core</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<!-- <verbal>true</verbal>-->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
<!-- <plugin>
<artifactId>maven-assembly-plugin </artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>cn.itcast.Hadoop.db.DBToHdfs2</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>-->
</plugins>
</build>
- 使用url的方式访问数据(了解)
@Test
public void demo1()throws Exception{
//第一步:注册hdfs 的url,让java代码能够识别hdfs的url形式
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
InputStream inputStream = null;
FileOutputStream outputStream =null;
//定义文件访问的url地址
String url = "hdfs://192.168.52.100:8020/test/input/install.log";
//打开文件输入流
try {
inputStream = new URL(url).openStream();
outputStream = new FileOutputStream(new File("c:\\hello.txt"));
IOUtils.copy(inputStream, outputStream);
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
如果执行出现以下错误,可以参见资料如何解决,也可以不用理会,不会影响程序的执行。记得配置完成环境变量之后重启开发工具
使用文件系统方式访问数据(掌握)
在 java 中操作 HDFS,主要涉及以下 Class:
Configuration:该类的对象封转了客户端或者服务器的配置;
FileSystem:该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过 FileSystem 的静态方法 get 获得该对象。
FileSystem fs = FileSystem.get(conf)
get 方法从 conf 中的一个参数 fs.defaultFS 的配置值判断具体是什么类型的文件系统。如果我们的代码中没有指定 fs.defaultFS,并且工程 classpath下也没有给定相应的配置,conf中的默认值就来自于Hadoop的jar包中的core-default.xml , 默 认 值 为 : file:/// , 则 获 取 的 将 不 是 一 个DistributedFileSystem 的实例,而是一个本地文件系统的客户端对象
获取FileSystem的几种方式
第一种方式获取FileSystem
@Test
public void getFileSystem() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), configuration);
System.out.println(fileSystem.toString());
}
第二种获取FileSystem类的方式
@Test
public void getFileSystem2() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS","hdfs://192.168.52.100:8020");
FileSystem fileSystem = FileSystem.get(new URI("/"), configuration);
System.out.println(fileSystem.toString());
}
第三种获取FileSystem类的方式
@Test
public void getFileSystem3() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://192.168.52.100:8020"), configuration);
System.out.println(fileSystem.toString());
}
第四种获取FileSystem类的方式
@Test
public void getFileSystem4() throws Exception{
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS","hdfs://192.168.52.100:8020");
FileSystem fileSystem = FileSystem.newInstance(configuration);
System.out.println(fileSystem.toString());
}
递归遍历文件系统当中的所有文件
通过递归遍历hdfs文件系统
@Test
public void listFile() throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isDirectory()){
Path path = fileStatus.getPath();
listAllFiles(fileSystem,path);
}else{
System.out.println("文件路径为"+fileStatus.getPath().toString());
}
}
}
public void listAllFiles(FileSystem fileSystem,Path path) throws Exception{
FileStatus[] fileStatuses = fileSystem.listStatus(path);
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isDirectory()){
listAllFiles(fileSystem,fileStatus.getPath());
}else{
Path path1 = fileStatus.getPath();
System.out.println("文件路径为"+path1);
}
}
}
官方提供的API直接遍历
/**
* 递归遍历官方提供的API版本
* @throws Exception
*/
@Test
public void listMyFiles()throws Exception{
//获取fileSystem类
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
//获取RemoteIterator 得到所有的文件或者文件夹,第一个参数指定遍历的路径,第二个参数表示是否要递归遍历
RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fileSystem.listFiles(new Path("/"), true);
while (locatedFileStatusRemoteIterator.hasNext()){
LocatedFileStatus next = locatedFileStatusRemoteIterator.next();
System.out.println(next.getPath().toString());
}
fileSystem.close();
}
下载文件到本地
/**
* 拷贝文件的到本地
* @throws Exception
*/
@Test
public void getFileToLocal()throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
FSDataInputStream open = fileSystem.open(new Path("/test/input/install.log"));
FileOutputStream fileOutputStream = new FileOutputStream(new File("c:\\install.log"));
IOUtils.copy(open,fileOutputStream );
IOUtils.closeQuietly(open);
IOUtils.closeQuietly(fileOutputStream);
fileSystem.close();
}
hdfs上创建文件夹
@Test
public void mkdirs() throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
boolean mkdirs = fileSystem.mkdirs(new Path("/hello/mydir/test"));
fileSystem.close();
}
hdfs文件上传
@Test
public void putData() throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
fileSystem.copyFromLocalFile(new Path("file:///c:\\install.log"),new Path("/hello/mydir/test"));
fileSystem.close();
}
javaAPI基本操作
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import java.net.URI;
import java.net.URISyntaxException;
public class demo {
//该类的对象封转了客户端或者服务器的配置
static Configuration conf=new Configuration();
//获取指定路径所有文件
public static void listStatus() throws Exception {
//该类的对象是一个文件系统对象
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
//获取某一目录下的所有文件
FileStatus stats[]=hdfs.listStatus(new Path("/"));
//遍历输出
for(int i = 0; i < stats.length; ++i)
System.out.println(stats[i].getPath().toString());
hdfs.close();
}
//重命名
public static void rename() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
Path frpaht=new Path("/aaa");
Path topath=new Path("/aaaaaaa");
boolean isRename=hdfs.rename(frpaht, topath);
String result=isRename?"修改成功!":"修改失败!";
System.out.println(result);
}
//获取文件日期
public static void GetTime() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
FileStatus fileStatus=hdfs.getFileStatus(new Path("/yarn-daemons.txt"));
long modiTime=fileStatus.getModificationTime();
System.out.println(modiTime);
}
//删除文件
public static void deletefile() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
boolean isDeleted=hdfs.delete(new Path("/user/new"),true);
System.out.println("Delete?"+isDeleted);
}
//创建文件夹
public static void mkdir () throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
boolean bool2=hdfs.mkdirs(new Path("/user/new"));
if (bool2)
{
System.out.println("创建成功!!");
}
else
{
System.out.println("创建失败!!");
}
}
//创建数据
public static void AddFile() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
byte[] buff="hello hadoop world!\r\n hadoop ".getBytes();
FSDataOutputStream outputStream=hdfs.create(new Path("/tmp/file.txt"));
outputStream.write(buff,0,buff.length);
outputStream.close();
}
//上传数据
public static void put() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
Path src =new Path("C:/123.py");
Path dst =new Path("/");
hdfs.copyFromLocalFile(src, dst);
}
//检查目录是否存在
public static void check() throws Exception {
FileSystem hdfs=FileSystem.get(new URI("hdfs://192.168.100.201:8020"),conf);
Path findf=new Path("/abc");
boolean isExists=hdfs.exists(findf);
System.out.println("Exist?"+isExists);
}
HDFS权限问题以及伪造用户
首先停止hdfs集群,在node01机器上执行以下命令
[root@node01 ~]# cd /export/servers/hadoop-2.6.0-cdh5.14.0
[root@node01 hadoop-2.6.0-cdh5.14.0]# sbin/stop-dfs.sh
修改node01机器上的hdfs-site.xml当中的配置文件
[root@node01 ~]# cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
[root@node01 hadoop]# vim hdfs-site.xml
在hdfs-site.xml添加如下配置 开启权限认证 (每个节点都要配置)
<property>
<name>dfs.permissions</name>
<value>true</value>
</property>
有权限,必然有管理员
linux 超级管理员是 root
HDFS 超级管理员是 hdfs(部分大数据平台)
修改完成之后配置文件发送到其他机器上面去
scp hdfs-site.xml node02:$PWD
scp hdfs-site.xml node03:$PWD
重启hdfs集群
[root@node01 ~]# cd /export/servers/hadoop-2.6.0-cdh5.14.0
[root@node01 hadoop-2.6.0-cdh5.14.0]# sbin/start-dfs.sh
随意上传一些文件到我们Hadoop集群当中准备测试使用
[root@node01 ~]# cd /export/servers/hadoop-2.6.0-cdh5.14.0/etc/hadoop
[root@node01 hadoop]# hdfs dfs -mkdir /config
[root@node01 hadoop]# hdfs dfs -put *.xml /config
[root@node01 hadoop]# hdfs dfs -chmod 600 /config/core-site.xml
- 使用代码准备下载文件
@Test
public void getConfig()throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration(),"root");
fileSystem.copyToLocalFile(new Path("/config/core-site.xml"),new Path("file:///c:/core-site.xml"));
fileSystem.close();
}
HDFS的小文件合并
由于Hadoop擅长存储大文件,因为大文件的元数据信息比较少,如果Hadoop集群当中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成大文件进行一起处理。
在我们的hdfs 的shell命令模式下,可以通过命令行将很多的hdfs文件合并成一个大文件下载到本地,命令如下
[root@node01 ~]# cd /export/servers
[root@node01 servers]# hdfs dfs -getmerge /config/*.xml ./hello.xml
既然可以在下载的时候将这些小文件合并成一个大文件一起下载,那么肯定就可以在上传的时候将小文件合并到一个大文件里面去
代码如下:
/**
* 将多个本地系统文件,上传到hdfs,并合并成一个大的文件
* @throws Exception
*/
@Test
public void mergeFile() throws Exception{
//获取分布式文件系统
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration(),"root");
FSDataOutputStream outputStream = fileSystem.create(new Path("/bigfile.xml"));
//获取本地文件系统
LocalFileSystem local = FileSystem.getLocal(new Configuration());
//通过本地文件系统获取文件列表,为一个集合
FileStatus[] fileStatuses = local.listStatus(new Path("file:///F:\\上传小文件合并"));
for (FileStatus fileStatus : fileStatuses) {
FSDataInputStream inputStream = local.open(fileStatus.getPath());
IOUtils.copy(inputStream,outputStream);
IOUtils.closeQuietly(inputStream);
}
IOUtils.closeQuietly(outputStream);
local.close();
fileSystem.close();
}
总结
HDFS -> Local 使用HDFS提供的-getmerge 方法
Local -> HDFS 遍历每个小文件追加到一个文件,在进行上传(这个文件不在HDFS)
HDFS-Web界面介绍
当我们启动HDFS集群后,然后通过http://master:50070/去访问HDFS WEB UI的时候,我们会经常使用Utilities下的Browse the file system去查看HDFS中的文件,如下:
然后就会出现HDFS中的根目录下所有的文件:
上面的方式是我们常见的访问HDFS文件的方式之一,这种使用的方式也是很方便的。
当我们启动HDFS集群后,我们可以通过http://master:50070来访问HDFS集群,其中,master是NameNode所在机器的名称。下面的就是HDFS WEB UI的七个大模块:
这篇文章,我们重点分别来详细看一下Overview、Datanodes以及Utilities三个模块
Overview
- 第1处的master:9999表示当前HDFS集群的基本路径。这个值是从配置core-site.xml中的fs.defaultFS获取到的。
- 第2处的Started表示集群启动的时间
- 第3处的Version表示我们使用的Hadoop的版本,我们使用的是2.7.5的Hadoop
- 第4处的Compiled表示Hadoop的安装包(hadoop-2.7.5.tar.gz)编译打包的时间,以及编译的作者等信息
- 第5处的Cluster ID表示当前HDFS集群的唯一ID
- 第6处的Block Pool ID表示当前HDFS的当前的NameNode的ID,我们知道通过HDFS Federation (联盟)的配置,我们可以为一个HDFS集群配置多个NameNode,每一个NameNode都会分配一个Block Pool ID
Summary
- 第1处的Security is off表示当前的HDFS集群没有启动安全机制
- 第2处的Safemode is off表示当前的HDFS集群不在安全模式,如果显示的是Safemode is on的话,则表示集群处于安全模式,那么这个时候的HDFS集群是不能用的
- 第3处表示当前HDFS集群包含了3846个文件或者目录,以及1452个数据块,那么在NameNode的内存中肯定有3846 + 1452 = 5298个文件系统的对象存在
- 第4处表示NameNode的堆内存(Heap Memory)是312MB,已经使用了287.3MB,堆内存最大为889MB,对
- 第5处表示NameNode的非堆内存的使用情况,有效的非堆内存是61.44MB,已经使用了60.36MB。没有限制最大的非堆内存,但是非堆内存加上堆内存不能大于虚拟机申请的最大内存(默认是1000M)
- 第6处的Configured Capacity表示当前HDFS集群的磁盘总容量。这个值是通过:Total Disk Space - Reserved Space计算出来的。Total Disk Space表示所在机器所在磁盘的总大小,而Reserved Space表示一个预留给操作系统层面操作的空间。Reserved space空间可以通过dfs.datanode.du.reserved(默认值是0)在hdfs-site.xml文件中进行配置。
我们这边的总容量为什么是:33.97GB呢,我们可以通过du -h看一下两个slave的磁盘使用情况,如下:
上面 17GB + 17GB = 34GB,而且我们没有配置Reserved Space,所以HDFS总容量就是33.97GB(有一点点的误差可以忽略)
- 第7处DFS Used表示HDFS已经使用的磁盘容量,说白了就是HDFS文件系统上文件的总大小(包含了每一个数据块的副本的大小)
- 第8处Non DFS Used表示在任何DataNodes节点上,不在配置的dfs.datanode.data.dir里面的数据所占的磁盘容量。其实就是非HDFS文件占用的磁盘容量
配置dfs.datanode.data.dir就是DataNode数据存储的文件目录
- 第9处DFS Remaining = Configured Capacity - DFS Used - Non DFS Used。这是HDFS上实际可以使用的总容量
- 第10处Block Pool Used表示当前的Block Pool使用的磁盘容量
- 第11处DataNodes usages%表示所有的DataNode的磁盘使用情况(最小/平均/最大/方差)
- 第12处Live Nodes表示存活的DataNode的数量。Decommissioned表示已经下线的DataNode
- 第13处Dead Nodes表示已经死了的DataNode的数量。Decommissioned表示已经下线的DataNode
- 第14处Decommissioning Nodes表示正在下线的DataNode的数量。
- 第15处Total Datanode Volume Failures表示DataNode上数据块的损坏大小
- 第16处Number of Under-Replicated Blocks表示没有达到备份数要求的数据块的数量
- 第17处Number of Blocks Pending Deletion表示正要被删除的数据块
- 第18处Block Deletion Start Time表示可以删除数据块的时间。这个值等于集群启动的时间加上配置dfs.namenode.startup.delay.block.deletion.sec的时间,其中配置dfs.namenode.startup.delay.block.deletion.sec默认是0秒
Datanodes
上面有一个Admin State我们有必要说明下,Admin State可以取如下的值:
- 1.In Service,表示这个DataNode正常
- 2.Decommission In Progress,表示这个DataNode正在下线
- 3.Decommissioned,表示这个DataNode已经下线
- 4.Entering Maintenance,表示这个DataNode正进入维护状态
- 5.In Maintenance,表示这个DataNode已经在维护状态
我们这里详细总结下Browse the file system,对于Logs我们在HDFS日志的查看总结中讲解
当我们点击Browse the file system时,我么会进入到如下的界面:
上图每一个字段的解释如下: - Permission:表示该文件或者目录的权限,和Linux的文件权限规则是一样的
- Owner:表示该文件或者目录的所有者
- Group:表示该文件或者目录的所有者属于的组
- Size:表示该文件或者目录的大小,如果是目录的话则一直显示0B
- Last Modified:表示该文件或者目录的最后修改时间
- Replication:表示该文件或者目录的备份数,如果是目录的话则一直显示0
- Block Size:表示该文件的数据块的大小,如果是目录的话则一直显示0B
- Name:表示文件或者目录的名字
我们可以通过鼠标点击Name来访问对应的文件目录或者文件:
当我们访问的是目录的时候,则是去查看该目录下有哪些子文件或者子目录。
当我们访问的是文件的时候,我们查看的是文件的详细信息,比如,我们访问文件/user/omneo.csv文件:
Journal Manager:Journal Node 存储EditLog数据的路径
State: Journal Node 存储EditLog数据的文件名
NameNode 存储数据的路径
NameNode存储edits的路径
/export/servers/hadoop-2.6.0-cdh5.14.0/hadoopDatas/dfs/nn/edits
NameNode存储fsimage的路径
/export/servers/hadoop-2.6.0-cdh5.14.0/hadoopDatas/namenodeDatas
Storage Type : 集群存储类型 DISK(磁盘)
Configured Capacity: 配置容量 135.01 GB
Capacity Used: 使用的容量 355.01 MB (0.26%)
Capacity Remaining:剩余容量 117.49 GB (87.02%)
Block Pool Used: 使用的块池 355.01 MB
Nodes In Service:服务中的节点 3
Datanode usage histogram: 数据节点使用率柱状图
Disk usage of each DataNode (%):每个数据节点的磁盘使用率(%)
In operation:运行中的节点
Entering Maintenance: 进入维护的节点列表
Decommissioning: 退役的节点列表
Snapshot Summary:快照摘要
Snapshottable directories : 快照目录列表:2
Snapshotted directories: 已创建的快照目录:4
Startup Progress:集群启动时加载的fsimage和edits
启动时加载的fsimage: fsimage_0000000000000000537
启动时加载的edits:edits_0000000000000000538-0000000000000000538