一.下载hadoop依赖项
下载地址
https://github.com/cdarlint/winutils
直接下载zip文件,之后保留自己hadoop版本的或者相近版本的就可以,其他都删掉。
这里我保留的3.3.5 因为我的是3.3.1
ok下载完成.
二.配置环境变量
配置系统变量:
新建一个环境变量,并写入自己的依赖项路径.
然后再Path中配置:
记得点确定!! 这里可能总共有三个确定,必须全点,否则没用.作者试错过,真的好难崩..希望读者别..
三.创建maven项目
files中点击新建项目,选择maven项目,新版idea只有maven archetype 这是maven项目的子项,所以我们点击右上边的Java蓝字。
编辑自己的项目名称和路径,选择自己的jdk,并create。
创建好后打开对应目录下的pom.xml并配置相关依赖项:
添加以下代码:<dependencys>
注意:
版本version要和自己的hadoop相一致!!!比如我的hadoop是3.3.1,不要无脑cp,否则后续可能有bug,junit是java测试项,log4j是日志管理项。
<dependencies>
<!-- hadoop相关依赖-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.3.1</version>
</dependency>
<!-- 单元测试依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- log4j依赖 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
ok 配置好之后点击最右边的maven,生成依赖项:
生成后会有dependencies 并且内容如下:
依赖项装好之后,添加我们的log4j:在resources中添加log4j.properties文件:
内容如下:这个可以直接无脑cp:
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
也不知道这是什么语言哈哈哈...
ok可以创建我们的包和java文件远程访问啦:我的目录是这样的:
四.远程访问
创建好自己的文件夹就可以访问hdfs,注意要启动我们的hadoop集群别忘记
上传文件:
package ztt.example;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
public class Java_API {
/**
* 测试上传文件
*/
// 可操作HDFS文件系统的对象
FileSystem hdfs = null;
// 测试方法执行前执行,用于初始化操作,避免频繁初始化
@Before
public void init() throws IOException {
// 构造一个配置参数对象,设置一个参数:要访问的HDFS的URI
Configuration conf = new Configuration();
// 指定使用HDFS访问
conf.set("fs.defaultFS","hdfs://hadoop01:8020");
// conf.set("fs.hdfs.impl", "org.apache.hadoop.hdfs.DistributedFileSystem");
// 进行客户端身份的设置(root为虚拟机的用户名,hadoop集群节点的其中一个都可以)
System.setProperty("HADOOP_USER_NAME","root");
// 通过FileSystem的静态get()方法获取HDFS文件系统客户端对象
hdfs = FileSystem.get(conf);
}
// 测试方法执行后执行,用于处理结尾的操作,关闭对象
@After
public void close() throws IOException {
// 关闭文件操作对象
hdfs.close();
}
@Test
public void testUploadFileToHDFS() throws IOException {
// 待上传的文件路径(windows)
Path src = new Path("E:/myStage/the3rd/Big_Data/HDFS-API/test/upload.txt");
// 上传之后存放的路径(HDFS)
Path dst = new Path("/wcinput/upload_fr_win.txt");
// 上传
hdfs.copyFromLocalFile(src,dst);
System.out.println("上传成功");
}
@Test
public void testDownFileToLocal() throws IOException {
// 待下载的路径(HDFS)
Path src = new Path("/wcinput/word.txt");
// 下载成功之后存放的路径(windows)
Path dst = new Path("E:/myStage/the3rd/Big_Data/HDFS-API/test/word.txt");
// 下载
hdfs.copyToLocalFile(false,src,dst,true);
System.out.println("下载成功");
}
}
这里如果直接cp ,编译器可能会报错,因为有重名类,读者需要自己重新根据提示选择导入模块的类或包。
结果成功
内容和hdfs中的一致:
内容就别看啦,这个是我在shell端写的本地文件,上传到hdfs中,现在又被我拉到本地windows下哈哈。
查看文件所在区块:
如果我想访问一下我的文件在哪个区块,有哪些节点存储了我的文件副本,代码如下:
package ztt.example;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import java.net.URI;
import java.util.Arrays;
public class file_loc {
public static void main(String[] args) {
String uri = "hdfs://hadoop01:8020/sanguo/shuguo.txt";
Configuration conf = new Configuration();
try {
FileSystem fs = FileSystem.get(new URI(uri),conf,"root");//URI和配置获取HDFS文件系统的实例
Path path = new Path(uri);
FileStatus fileStatus = fs.getFileStatus(path);//获取文件状态
//hdfs文件大小小于一个块的大小,默认128M,所以只有1块block0
BlockLocation blkLocation[] = fs.getFileBlockLocations(fileStatus,0,fileStatus.getLen());
System.out.println(Arrays.toString(blkLocation));
for (int i = 0; i < blkLocation.length; i++) {
String[] hosts = blkLocation[i].getHosts();//找到主机
// System.out.println("block_" + i + "_Location:" + hosts[0]);//打印块位置的第一个副本
System.out.println("block_" + i + "_Location: " + Arrays.toString(hosts));
}
}catch (Exception e){
e.printStackTrace();
}
}
}
注意一下,这是放自己的文件路径,不过端口都是8020,具体要看你的hadoop配置文件`core-site.xml`
结果如下:
五.实现wordcount
首先,在自己项目下新建一个模块吧用于实现自己编写的mapreduce的wordcount:
里面同样有pom.xml的配置项,直接复制粘贴整个项目的就行,这里不多做赘述. (可以看第三点)
还有log4j.properties记得cp过去
创建自己创建模块和定义的三个类:
各个类的代码如下:
WordCountMapper.java:
package ztt.mapreduce;
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;
/*
* KEYIN:输入类型LongWritable
* KEYOUT:输出key类型Text
* */
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private Text outK = new Text();
private IntWritable outV = new IntWritable(1);
@Override
protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException {
// super.map(key, value, context);
//获取一行
String line=value.toString();
//切割
String[] words=line.split(" ");
//循环写出
for(String word:words){
//封装
outK.set(word);
//写出
context.write(outK, outV);
}
}
}
WordCountReducer:
package ztt.mapreduce;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
// super.reduce(key, values, context);
int sum = 0;
//apple,(1,1)
//累加
for (IntWritable value : values) {
sum += value.get();
}
context.write(key, new IntWritable(sum));
}
}
JobMain.java:
package ztt.mapreduce;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordCountReducer extends Reducer<Text, IntWritable,Text,IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException {
// super.reduce(key, values, context);
int sum = 0;
//apple,(1,1)
//累加
for (IntWritable value : values) {
sum += value.get();
}
context.write(key, new IntWritable(sum));
}
}
以上代码需要根据情况选择修改,包括输入输出路径等等。
最终run JobMain.java文件即可.
成功写入:
- r表示文件来自reduce阶段
- 00000表示输出文件的编号
文件内容:
完美啦!