Hadoop安装以及基本操作
一,Hadoop的安裝(Mac)
下載Hadoop https://archive.apache.org/dist/hadoop/core/到自定義目錄下 ,我這邊下載的是hadoop-2.4.0版本,下載到/usr/local/目錄下。
1.1環境配置
先通過 .bash_profile添加環境變量
sudo vim ~/.bash_profile
# hadoop
export HADOOP_HOME=/usr/local/hadoop-2.4.0
export HADOOP_INSTALL=$HADOOP_HOME
export HADOOP_MAPRED_HOME=$HADOOP_HOME
export HADOOP_COMMON_HOME=$HADOOP_HOME
export HADOOP_HDFS_HOME=$HADOOP_HOME
export YARN_HOME=$HADOOP_HOME
export HADOOP_COMMON_LIB_NATIVE_DIR=$HADOOP_HOME/lib/native
export HADOOP_OPTS="-Djava.library.path=$HADOOP_HOME/lib"
export PATH=$PATH:$HADOOP_HOME/sbin:$HADOOP_HOME/bin
然後使添加的環境變量生效
source ~/.bash_profile
此時就可以通過Hadoop命令查看版本信息:
hadoop version
1.2 修改配置環境
需要修改的 Hadoop 配置文件都在/usr/local/hadoop-2.4.0/etc/hadoop/`下,包括:
- hadoop-env.sh
- core-site.xml
- hdfs-site.xml
- mapred-site.xml
- yarn-site.xml
1.2.1 hadoop-env.sh
修改hadoop-env.sh文件主要是為了配置java路徑
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/
1.2.2 core-site.xml
默认情况下,Hadoop 将数据保存在/tmp 下,当重启系统时,/tmp 中的内容将被自动清空, 所以我们需要制定自己的一个 Hadoop 的目录,用来存放数据。另外需要配置 Hadoop 所使用的默认文件系统,以及 Namenode 进程所在的主机。
修改core-site.xml,在configuration標籤內添加屬性:
<property>
<name>fs.defaultFS</name>
<value>hdfs://localhost:9000</value>
</property>
<!--用来指定hadoop运行时产生文件的存放目录 自己创建-->
<property>
<name>hadoop.tmp.dir</name>
<value>file://usr/local/hadoop-2.4.0/tmp</value>
</property>
<property>
<name>fs.trash.interval</name>
<value>1440</value>
</property>
1.2.3 hdfs-site.xml
该文件指定与 HDFS 相关的配置信息。需要修改 HDFS 默认的块的副本属性,因为 HDFS 默认 情况下每个数据块保存 3 个副本,而在伪分布式模式下运行时,由于只有一个数据节点,所 以需要将副本个数改为 1;否则 Hadoop 程序会报错。
修改hdfs-site.xml,在configuration標籤內添加屬性:
<property>
<name>dfs.replication</name>
<value>1</value>
</property>
<!--不是root用户也可以写文件到hdfs-->
<property>
<name>dfs.permissions</name>
<value>false</value> <!--关闭防火墙-->
</property>
<!-- name node 存放 name table 的目录 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/usr/local/hadoop-2.4.0/tmp/dfs/name</value>
</property>
<!-- data node 存放数据 block 的目录 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>file:/usr/local/hadoop-2.4.0/tmp/dfs/data</value>
</property>
1.2.4 mapred-site.xml
在该配置文件中指定与 MapReduce 作业相关的配置属性,需要指定 JobTracker 运行的主机 文件夹中并没有 mapred-site.xml 文件,但提供了模板 mapred-site.xml.template 将其copy另存为 mapred-site.xml 即可
cp mapred-site.xml.template mapred-site.xml
然後再修改mapred-site.xml,在configuration標籤內添加屬性:
<property>
<!--指定mapreduce运行在yarn上-->
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
1.2.5 yarn-site.xml
修改mapred-site.xml,在configuration標籤內添加屬性:
<!-- Site specific YARN configuration properties -->
<property>
<!-- mapreduce 执行 shuffle 时获取数据的方式 -->
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>localhost:18040</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>localhost:18030</value>
</property>
<property>
<name>yarn.resourcemanager.resource-tracker.address</name>
<value>localhost:18025</value>
</property>
<property>
<name>yarn.resourcemanager.admin.address</name>
<value>localhost:18141</value>
</property>
<property>
<name>yarn.resourcemanager.webapp.address</name>
<value>localhost:18088</value>
</property>
配置完畢後,在控制台執行 hdfs namenode -format
成功则会看到”successfully formatted”和”Exitting with status 0”的提示,若为 “Exitting with status 1” 则是出错。
1.3 測試
1.3.1 查看進程是否啟動
在 Hadoop 的终端执行 jps 命令,在打印结果中会看到 5 个进程,分别是 namenode、 secondarynamenode、datanode、resourcemanager、nodemanager, 如下图所示。 如果出现 了这 5 个进程表示主节点进程启动成功:
- ResourceManager
- NameNode
- RemoteMavenServer
- DataNode
- Launcher
- SecondaryNameNode
- NodeManager
Web UI 查看集群是否成功启动,浏览器中打开http://localhost:50070/,以及http://localhost:18088/;检查 namenode 和 datanode 是否正常,检查 Yarn 是否正常。
二,Hadoop案例(WordCount)
創建模塊 hadoop-test,將要統計的英文文件存放到resources目錄下,命名為input.txt。
2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lmc-user</artifactId>
<groupId>com.lmc</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hadoop-test</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-core</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
</project>
2.2 MapClass.java
package com.lmc.example.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
import java.util.StringTokenizer;
/**
* @ClassName: MapClass
* @author: Leemon
* @Description: TODO 本操作主要是进行map的数据处理,MapClass把 <k1,v1>通過map轉化為<k2,v2>
* @date: 2021/1/28 17:10
* @version: 1.0
*/
public class MapClass extends Mapper<LongWritable, Text, Text, IntWritable> {
//每一个单词最终生成的保存个数是1
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
/**
* 轉化類
* @param key k1變量
* @param value v1變量
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value,
Context context)
throws IOException, InterruptedException {
// 默认情况下是取得每行的数据,所以每行的数据里面都会存在有空格,按照空格进行拆分,每当出现一个单词就做一个统计的1
//取出每行的数据
String line = value.toString();
//进行每行数据的拆分
StringTokenizer st = new StringTokenizer(line," ");
//循环每个单词而后进行数据的生成
while(st.hasMoreTokens()){
word.set(st.nextToken());
//word:k2變量,one:v2變量
context.write(word,one);
}
}
}
2.3 ReduceClass.java
package com.lmc.example.wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.util.Iterator;
/**
* @ClassName: ReduceClass
* @author: Leemon
* @Description: TODO 本操作主要是进行reduce的数据处理,进行合并后数据的最终统计
* @date: 2021/1/28 17:12
* @version: 1.0
*/
public class ReduceClass extends Reducer<Text, IntWritable, Text, IntWritable> {
protected void reduce(Text key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
//保存每个单词出现的总次数
int sum = 0;
/**
* 对于此处的foreach循环,官网上找到的解释,此时传入Reduce的数据已经是<key, (list of values)>的结构,
* 所以Map函数循环对每个可以值进行处理,
* 对每个key值的value list进行循环加和统计。
* 官网http://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html
* In this phase the reduce(WritableComparable, Iterable<Writable>, Context)
* method is called for each <key, (list of values)> pair in the grouped inputs.
*/
for (IntWritable count: values) {
sum += count.get();
}
context.write(key, new IntWritable(sum));
}
}
2.4 WordCount.java
package com.lmc.example.wordcount;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
/**
* @ClassName: WordCount
* @author: Leemon
* @Description: TODO
* @date: 2021/1/28 17:13
* @version: 1.0
*/
public class WordCount extends Configured implements Tool{
public static void main(String[] args) throws Exception{
int exitCode = ToolRunner.run(new WordCount(), args);
System.exit(exitCode);
}
public int run(String[] args) throws Exception {
if (args.length != 2) {
System.err.printf("Usage: %s needs two arguments, input and output files\n", getClass().getSimpleName());
return -1;
}
//进行相关的配置使用
Configuration conf = new Configuration();
// 定义一个名为WordCounter的作业
Job job = new Job(conf);
job.setJobName("wordCounter1");
// 设置执行的jar文件程序类
job.setJarByClass(WordCount.class);
//设置输入输出路径
FileInputFormat.addInputPath(job, new Path("hadoop-test/src/main/resources/input.txt"));
FileOutputFormat.setOutputPath(job, new Path("hadoop-test/src/main/resources/output"));
// 指定Mapper的处理类
job.setMapperClass(MapClass.class);
// 设置Reduce的处理类
job.setReducerClass(ReduceClass.class);
// 最终输出Key信息,设置为文本
job.setOutputKeyClass(Text.class);
// 最终输出Value内容设置为一个整型数据
job.setOutputValueClass(IntWritable.class);
job.setOutputFormatClass(TextOutputFormat.class);
// if(job.isSuccessful()) {
// System.out.println("Job was successful");
// } else if(!job.isSuccessful()) {
// System.out.println("Job was not successful");
// }
// 执行完后退出
int returnValue = job.waitForCompletion(true) ? 0:1;
return returnValue;
}
}
2.4 java基本類型的Writable封裝
目前Java基本类型对应的Writable封装如下表所示。所有这些Writable类都继承自WritableComparable。也就是说,它们是可比较的。同时,它们都有get()和set()方法,用于获得和设置封装的值。
Java基本类型对应的Writable封装
Java基本类型 | Writable | 序列化后长度 |
---|---|---|
布尔型(boolean) | BooleanWritable | 1 |
字节型(byte) | ByteWritable | 1 |
整型(int) | IntWritable | 4 |
VIntWritable | 1~5 | |
浮点型(float) | FloatWritable | 4 |
长整型(long) | LongWritable | 8 |
VLongWritable | 1~9 | |
双精度浮点型(double) | DoubleWritable | 8 |
注意:在表中,对整型(int和long)进行编码的时候,有固定长度格式(IntWritable和LongWritable)和可变长度格式(VIntWritable和VLongWritable)两种选择。固定长度格式的整型,序列化后的数据是定长的,而可变长度格式则使用一种比较灵活的编码方式,对于数值比较小的整型,它们往往比较节省空间。同时,由于VIntWritable和VLongWritable的编码规则是一样的,所以VIntWritable的输出可以用VLongWritable读入。
三,Hadoop之HDFS
3.1 文件系統
fs 和 dfs区别:
(1) fs是文件系统, dfs是分布式文件系统。
(2) fs > dfs。
(3) 分布式环境情况下,fs与dfs无区别。
(4) 本地环境中,fs就是本地文件,dfs就不能用了。
(5) fs涉及到一个通用的文件系统,可以指向任何的文件系统如local,HDFS等。但是dfs仅是针对HDFS的。
命令區別:
- hadoop fs:
该命令可以用于其他文件系统,不止是hdfs文件系统内,也就是说该命令的使用范围更广 - hadoop dfs
专门针对hdfs分布式文件系统 - hdfs dfs
和上面的命令作用相同,相比于上面的命令更为推荐,并且当使用hadoop dfs时内部会被转为hdfs dfs命令
hadoop shell常用命令:
命令 | 使用方法 | 描述 |
---|---|---|
mkdir | hadoop fs -mkdir | 接受路径指定的uri作为参数,创建这些目录。其行为类似于Unix的mkdir -p,它会创建路径中的各级父目录。 |
ls | hadoop fs -ls | 如果是文件,则返回文件信息;如果是目录,则返回它直接子文件的一个列表 |
lsr | hadoop fs -lsr | ls命令的递归版本。类似于Unix中的ls -R |
mv | hadoop fs -mv URI [URI …] | 将文件从源路径移动到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。不允许在不同的文件系统间移动文件。 |
cp | hadoop fs -cp URI [URI …] | 将文件从源路径复制到目标路径。这个命令允许有多个源路径,此时目标路径必须是一个目录。 |
get | hadoop fs -get [-ignorecrc] [-crc] | 复制文件到本地文件系统。可用-ignorecrc选项复制CRC校验失败的文件。使用-crc选项复制文件以及CRC信息。 |
put | hadoop fs -put … | 从本地文件系统中复制单个或多个源路径到目标文件系统。也支持从标准输入中读取输入写入目标文件系统。 |
… | … | … |
以上的命令,執行成功返回0,失敗返回-1
3.2 在java使用HDFS
3.2.1 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>lmc-user</artifactId>
<groupId>com.lmc</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hadoop-test</artifactId>
<dependencies>
<!-- <dependency>-->
<!-- <groupId>org.apache.hadoop</groupId>-->
<!-- <artifactId>hadoop-core</artifactId>-->
<!-- <version>1.2.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
</project>
3.2.2 HDFSAction.java
package com.lmc.example.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import java.io.*;
/**
* @ClassName: HDFSAction
* @author: Leemon
* @Description: TODO
* @date: 2021/1/30 9:33
* @version: 1.0
*/
public class HDFSAction {
public static void main(String[] args) {
try {
Configuration conf = new Configuration();
conf.set("fs.defaultFS", "hdfs://10.197.29.203:9000");
FileSystem fs = FileSystem.get(conf);
//列出目录
// list(fs, "/");
//创建目录
//mkdir(fs, "/test2");
//刪除
// delete(fs, "/test2");
//寫文件
// write(fs, "/test1/write.txt");
//讀文件
read(fs, "/wordCount/input/input.txt");
//從本地拷貝文件到hdfs
// copyLocalToHDFS(fs, "hadoop-test/src/main/resources/input.txt", "/wordCount/input/input.txt");
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 讀文件
* @param fs
* @param fileName
* @throws IOException
*/
public static void read(FileSystem fs, String fileName) throws IOException {
//read
Path path = new Path(fileName);
FSDataInputStream fin = fs.open(path);
byte[] buff = new byte[128];
int len = 0 ;
while( (len = fin.read(buff,0,128)) != -1 ) {
System.out.print(new String(buff,0,len));
}
}
/**
* 寫文件
* @param fs
* @param fileName
* @throws IOException
*/
public static void write(FileSystem fs, String fileName) throws IOException {
Path path = new Path(fileName);
FSDataOutputStream fout = fs.create(path);
byte[] bWrite = "hello hadoop distribute file system \n".getBytes();
fout.write(bWrite); //写入字节数组
fout.flush(); //flush提供了一种将缓冲区的数据强制刷新到文件系统的方法
fout.close(); //关闭写出流
}
public static void copyLocalToHDFS(FileSystem fs, String source, String target) throws IOException {
Path s = new Path(source);
Path t = new Path(target);
fs.copyFromLocalFile(s, t);
}
/**
* 列出HDFS目錄
* @param fs
* @throws IOException
*/
public static void list(FileSystem fs, String path) throws IOException {
//列出目录
FileStatus[] paths = fs.listStatus(new Path(path));
for (int i = 0 ; i < paths.length ;++i) {
System.out.println(paths[i].toString());
System.out.println(paths[i].getLen());
System.out.println(paths[i].getPath().getName());
}
}
/**
* 创建目录
* @param fs
* @param dictory
* @throws IOException
*/
public static void mkdir(FileSystem fs, String dictory) throws IOException {
//创建目录
if(fs.mkdirs(new Path(dictory))) {
System.out.println("mkdir " + dictory + " success ");
}
}
/**
* 刪除
* @param fs
* @param dictory
* @throws IOException
*/
public static void delete(FileSystem fs, String dictory) throws IOException {
//删除
if(fs.delete(new Path(dictory), true)){
System.out.println("delete " + dictory + " /import ");
}
}
}
3.3 修改WordCount.java為HDFS
先將input文件上傳至HDFS上
HDFSAction.copyLocalToHDFS(fs, "hadoop-test/src/main/resources/input.txt", "/wordCount/input/input.txt");
再做好相關配置:
//进行相关的配置使用
String HDFS_PATH = "hdfs://10.197.29.203:9000";
Configuration conf = new Configuration();
conf.set("fs.defaultFS", HDFS_PATH);
然後修改輸入輸出路徑:
//设置输入输出路径
// FileInputFormat.addInputPath(job, new Path("hadoop-test/src/main/resources/input.txt"));
// FileOutputFormat.setOutputPath(job, new Path("hadoop-test/src/main/resources/output"));
FileInputFormat.addInputPath(job, new Path("/wordCount/input/input.txt"));
FileOutputFormat.setOutputPath(job, new Path("/wordCount/output"));
最後,運行測試。