1、
第一步是要部署java环境和安装ssh,首先在根目录建两个文件夹software和app分别存放安装包和解压后的文件:
把安装好的jdk-8u161-linux-x64.tar和hadoop-2.6.0-cdh5.14.0.tar上传到software文件夹。然后把jdk解压到app目录下:
tar -zxvf jdk-8u161-linux-x64.tar.gz -C ~/app/
接着进入jdk的目录输入pwd得到jdk目录所在路径,并手动把这个路径复制到粘贴板(即/root/app/jdk1.8.0_161):
下一步是要配置jdk的环境变量,由于Linux下有多种方式配置环境变量,这里只是选择其中的一种方式:
该配置文件的原始设置如下:
加入jdk的环境变量设置后如下所示:
使用source命令使刚刚的配置文件立即生效:
然后就能测试一下jdk是否配置成功:
弄完JDK之后,接下来就是要安装ssh:
sudo yum install ssh
然后设置ssh免密登录:
ssh-keygen -t rsa
输入完之后一直按回车即可:
用ls -a可以看到隐藏的.ssh文件夹,然后复制一下id_rsa.pub文件,如下所示:
可以看一下拷贝后的文件的内容:
这时ssh已经配置成功,可以测试一下,输入
ssh hadoop1(这个是我的用户名),第一次还需要输入yes,然后输入exit退出一下,再输入一次ssh hadoop1,这时就已经能直接登录进去而不需要输入密码了:
2、
第二步就是安装hadoop的CDH版本(http://archive.cloudera.com/cdh5/cdh/5/),这里我安装的是hadoop-2.6.0-cdh5.14.0.tar.gz,注意要看一下CDH的版本和JDK的版本以及Linux系统的版本有什么对应的要求(https://www.cloudera.com/documentation/enterprise/release-notes/topics/rn_consolidated_pcm.html#pcm_jdk)
解压包放在了software文件夹,同样是解压到app文件夹下:
tar -zxvf hadoop-2.6.0-cdh5.14.0.tar.gz -C ~/app/
解压完成后,要配置app/hadoop-2.6.0-cdh5.14.0/etc/hadoop/hadoop-env.sh:
修改该配置文件的JAVA_HOME即可,其它地方不用动:
然后配置另外两个配置文件:
其中core-site.xml里的fs.defaultFS配置了HDFS默认的文件系统的地址,其中9000端口是Hadoop1.x用的,在2.x中一般是8020,如下(hadoop000是计算机名):
特别注意:由于HDFS文件存储在临时文件夹中,而每次虚拟机重启后都会清除掉,所以要在core-site.xml中加一个设置项,value指定的文件夹可以自己新建,将会用来存储HDFS的文件:
hdfs-site.xml中的副本节点数调整为1(原本默认为3),因为这里是用单机的伪分布式:
我创建的用于存储临时文件的文件夹和两个配置文件具体如下所示:
①文件夹:
②core-site.xml(/root/app/hadoop-2.6.0-cdh5.14.0/etc/hadoop/core-site.xml):
③hdfs-site.xml(/root/app/hadoop-2.6.0-cdh5.14.0/etc/hadoop/hdfs-site.xml):
最后,同一路径下还有一个配置文件slaves用于设置datanode(现在是单机伪分布式,暂时用不到)
3、启动hdfs
配置好上述步骤后,可以开始启动执行。(参考:http://archive.cloudera.com/cdh5/cdh/5/hadoop/里面有一个single node setup:http://archive.cloudera.com/cdh5/cdh/5/hadoop/hadoop-project-dist/hadoop-common/SingleCluster.html)
①格式化文件系统(仅第一次执行即可,不要重复执行,因为数据会清空):
bin/hdfs namenode -format
如下所示:
②开启 NameNode 进程和 DataNode 进程:
sbin/start-dfs.sh
如下所示:
输入完成后可以输入jps命令查看有哪些java进程从而检查节点是否启动成功:
同时有对应生成的日志:
除了用jps命令检查是否启动成功之外,还可以在本地浏览器上输入http://Hadoop1:50070(事先要修改hostname:http://blog.csdn.net/u014034934/article/details/72874927)或者http://192.168.197.129:50070查看:
若要停止则输入如下命令:
4、HDFS常用命令(hdfs shell)
参考:http://archive.cloudera.com/cdh5/cdh/5/hadoop/hadoop-project-dist/hadoop-hdfs/HDFSCommands.html
注意:为了更方便地使用Hadoop的bin目录下的命令,建议把它配置到环境变量中,然后就能直接在命令行输入bin目录里的命令,例如输入hdfs,就会显示所有hdfs命令的选择:
选择第一个dfs,即输入hdfs dfs会有进一步的提示:
为了使用这些指令,创建一个data文件夹用于存放被测试的文件:
在data文件夹新建一个hello.txt,里面输入一些内容然后保存退出。
查看当前路径是否有hdfs的文件:
一开始没有,这时把当前路径的hello.txt上传到hdfs(实际也是选择了当前路径)里,重新查看就会发现hdfs中有了刚刚的文件(hdfs dfs -put hello.txt / 从本地上传到hdfs中,也可以使用copyFromLocal)(hdfs dfs -ls / 查看hdfs的文件):
查看HDFS里文件的内容(hdfs dfs -text /hello.txt):
在HDFS中新建文件夹:
hdfs dfs -mkdir /test
在HDFS中递归新建文件夹:
hdfs dfs -mkdir -p /test/a/b
查看递归的文件和文件夹目录:
hdfs dfs -ls -R /
使用-copyFromLocal命令(即上传,上面另一种方式是用-put):
hdfs dfs -copyFromLocal hello.txt /test/a/b/h.txt
使用-cat查看内容(上面另一种方式是用-text查看内容):
hdfs dfs -cat /test/a/b/h.txt
从HDFS中拿取文件到本地(即下载):
hdfs dfs -get /test/a/b/h.txt
从HDFS删除文件:
hdfs dfs -rm /hello.txt
从HDFS删除文件夹,要用递归的方式:
hdfs dfs -rm -R /test
现在要测试一下HDFS的分块(Block)功能,先把一个大文件(400多M)上传到HDFS中:
然后浏览器输入http://192.168.197.129:50070,点击导航栏最右边的浏览文件系统:
显示的是HDFS中所有的文件和文件夹:
点击刚刚上传的那个400多M的文件,会发现文件被分成了多个Block,因为该文件超过了128M:
5、Java API操作HDFS文件
包含内容:
(1)首先在IDEA中新建一个Maven Project(勾选Create from …,并选择quick start)
项目初始结构如下(pom.xml中的JUnit版本可以改成4.10):
(2)在pom.xml中添加Hadoop的依赖
由于与CDH以及Hadoop的版本都有关系,先在centos系统中输入echo $HADOOP_HOME得到版本为hadoop-2.6.0-cdh5.14.0,然后为了在pom.xml中能统一管理这些版本,在properties中添加一个节点记录版本:
<hadoop.version>2.6.0-cdh5.14.0</hadoop.version>
然后添加如下依赖:
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>${hadoop.version}</version>
</dependency>
第一次导入包会报错:Dependency ‘org.apache.hadoop:hadoop-client:2.6.0-cdh5.14.0’ not found,说明找不到这些包(因为默认的仓库是没有cdh的仓库的),我们要在pom.xml中定义一个新的仓库(id可以随便写,但url要正确):
<repositories>
<repository>
<id>cloudera</id>
<url>https://repository.cloudera.com/artifactory/cloudera-repos</url>
</repository>
</repositories>
重新导入包(第一次下载需要耗费较长的时间),这时就能解决上面的问题了。
(3)hadoop-client的包下载完后,进入下列选项选中Maven Projects视图
就能看到依赖的包等信息:
(4)下面开始编写单元测试来使用HDFS的Java API(可以删掉test/java下的原有的AppTest,新建测试类)
这里先列出几个编写测试过程中遇到的问题:
①使用FileSystem的copyFromLocalFile方法时,报错:rg.apache.hadoop.ipc.RemoteException(org.apache.hadoop.hdfs.server.namenode.SafeModeException): Cannot create file/hdfsapi/test/img.png. Name node is in safe mode.The reported blocks 7 has reached the threshold 0.9990 of total blocks 7. The number of live datanodes 1 has reached the minimum number 0. In safe mode extension. Safe mode will be turned off automatically in 13 seconds.
涉及到的知识点是:Hadoop安全模式的理解(参考:http://blog.csdn.net/michael_zhu_2004/article/details/8268728),即若要让hadoop强制退出安全模式(不建议):
$ hdfs dfsadmin -safemode leave
上述操作不建议,最好是等待(一般默认的延迟时间是30s,由dfs.safemode.extension指定)
②使用FileSystem的copyToLocalFile方法时,报错:java.lang.NullPointerException
解决方法(参考:http://blog.csdn.net/u014307117/article/details/44787639):调用方法改为
fileSystem.copyToLocalFile(false,hdfsPath, localPath,true);
对应的API是:
public void copyToLocalFile(boolean delSrc, Path src, Path dst, boolean useRawLocalFileSystem) throws IOException {
Configuration conf = this.getConf();
FileSystem local = null;
if (useRawLocalFileSystem) {
local = getLocal(conf).getRawFileSystem();
} else {
local = getLocal(conf);
}
FileUtil.copy(this, src, (FileSystem)local, dst, delSrc, conf);
}
③
short replication = fileStatus.getReplication();
问题:我们已经在hdfs-site.xml中设置了副本系数为1,为什么上述代码得到的文件副本系数为3
原因:
如果是通过hdfs shell的方式put上去的,那么采用默认的副本系数为1;
而如果是java api上传上去的,在本地我们并没有手工设置副本系数,所以默认采用的是hadoop自己的副本系数。
下面是hdfs完整的Java API测试代码:
package com.jaxhin.hadoop.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
/**
* Hadoop HDFS Java API操作
*/
public class HDFSApp {
private static final String HDFS_PATH = "hdfs://192.168.197.129:8020";
FileSystem fileSystem = null;
Configuration configuration = null;
/**
* 创建HDFS目录
* @throws Exception
*/
@Test
public void mkdir() throws Exception{
fileSystem.mkdirs(new Path("/hdfsapi/test"));
}
/**
* 创建文件
* @throws Exception
*/
@Test
public void create() throws Exception{
FSDataOutputStream output = fileSystem.create(new Path("/hdfsapi/test/a.txt"));
output.write("Hello Hadoop".getBytes());
output.flush();
output.close();
}
/**
* 查看HDFS中文件的内容
* @throws Exception
*/
@Test
public void cat() throws Exception{
FSDataInputStream in = fileSystem.open(new Path("/hdfsapi/test/a.txt"));
IOUtils.copyBytes(in, System.out, 1024);
in.close();
}
/**
* 重命名
* @throws Exception
*/
@Test
public void rename() throws Exception{
Path oldPath = new Path("/hdfsapi/test/a.txt");
Path newPath = new Path("/hdfsapi/test/b.txt");
fileSystem.rename(oldPath, newPath);
}
/**
* (从本地系统,现在是win10)上传文件到HDFS
* @throws Exception
*/
@Test
public void copyFromLocalFile() throws Exception{
Path localPath = new Path("D:/code/img.png");
Path hdfsPath = new Path("/hdfsapi/test");
fileSystem.copyFromLocalFile(localPath, hdfsPath);
}
/**
* (从本地系统,现在是win10)上传文件到HDFS,并带有进度条(适用于大文件上传)
* @throws Exception
*/
@Test
public void copyFromLocalFileWithProgress() throws Exception{
InputStream in = new BufferedInputStream(new FileInputStream(new File("D:/code/img.png")));
FSDataOutputStream out = fileSystem.create(new Path("/hdfsapi/test/t.png"), new Progressable() {
public void progress() {
System.out.print(".");
}
});
IOUtils.copyBytes(in, out, 32);
}
/**
* 下载HDFS文件
* @throws Exception
*/
@Test
public void copyToLocalFile() throws Exception{
Path localPath = new Path("D:/code/hdfs-test.png");
Path hdfsPath = new Path("/hdfsapi/test/img.png");
fileSystem.copyToLocalFile(false, hdfsPath, localPath, true);
}
/**
* 查看某个目录下的所有文件
* @throws Exception
*/
@Test
public void listFiles() throws Exception{
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/hdfsapi/test"));
for (FileStatus fileStatus : fileStatuses){
String isDir = fileStatus.isDirectory() ? "文件夹" : "文件";
short replication = fileStatus.getReplication();
long len = fileStatus.getLen();
String path = fileStatus.getPath().toString();
System.out.println(isDir+"\t"+replication+"\t"+len+"\t"+path);
}
}
/**
* 删除HDFS文件
* @throws Exception
*/
@Test
public void delete() throws Exception{
//fileSystem.delete(new Path("/hdfsapi/test/"));//方法过时
fileSystem.delete(new Path("/hdfsapi/test/"), true);//后面的参数表示是否递归删除
}
@Before
public void setUp() throws Exception{
System.out.println("HDFSApp.setUp ...");
configuration = new Configuration();
fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, "root");
}
@After
public void tearDown() throws Exception{
configuration = null;
fileSystem = null;
System.out.println("HDFSApp.tearDown ...");
}
}
6、HDFS文件读写流程详解
参考(有很多类似的漫画可以搜索):通过漫画轻松掌握HDFS工作原理:http://blog.chinaunix.net/uid-27105712-id-3274395.html