HDFS应用开发
文章目录
- HDFS应用开发
- 1、HDFS的JAVA API操作
- 1.1 搭建开发环境
- 1.2 构造客户端对象
- 1.3 示例代码
- 2.案例:shell定时采集数据至HDFS
- 2.1技术分析
- 2.2实现流程
- 2.3代码实现
1、HDFS的JAVA API操作
HDFS在生产应用中主要是客户端的开发,其核心步骤是从HDFS提供的API中构造一个HDFS的访问客户端对象,然后通过该客户端对象操作(增删改查)HDFS上的文件。
1.1 搭建开发环境
创建Maven工程,引入pom依赖
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.itcast</groupId>
<artifactId>example-hdfs</artifactId>
<version>1.0-SNAPSHOT</version>
<repositories>
<repository>
<id>maven-net-cn</id>
<name>Maven China Mirror</name>
<url>https://maven.google.com/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven-net-cn</id>
<name>Maven China Mirror</name>
<url>https://maven.google.com/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.4</version>
</dependency>
</dependencies>
</project>
1.2 构造客户端对象
在java中操作HDPS,主要涉及以下Class:Configuration
:该类的对象封转到客户端或者服务器的配置:FileSystem
:该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过FileSystem的静态方法get获得该对象。
FileSystem fs = FileSystem. get (conf)
get方法从conf中的一个参数fs. defaultFS的配置值判断具体是什么类型的文件系统。如果我们的代码中没有指定fs. defaultFs,并且工程classpath下也没有给定相应的配置, conf中的默认值就来自于hadoop的jar包中的core-default.xml,默认值为:file:///,则获取的将不是一个DistributedFi FileSystem的实例,而是一个本地文件系统的客户端对象。
1.3 示例代码
package cn.itcast.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class TestHDFSClient {
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
// 设定使用的hdfs文件系统
conf.set("fs.defaultFS","hdfs://192.168.10.128:9000");
// 进行客户端身份的设定
System.setProperty("HADOOP_USER_NAME","root");
FileSystem fs = FileSystem.get(conf);//静态方法,类名点调用
// FileSystem fs = FileSystem.get(new URI("hdfs://192.168.10.128:9000"),conf,"root");
// fs.create(new Path("/create"));//创建目录
//从hdfs下载到本地
//fs.copyToLocalFile(new Path("/Mllib/test.tsv"),new Path("e://"));
//上传文件到hdfs
fs.copyFromLocalFile(new Path("E:/get/密码链接说明.txt"),new Path("/"));
fs.close();
}
}
stream流形式操作
public void UploadToHdfs(){
//本地上传HDFS
FSDataOutputStream OutputStream = fs.create(new Path("/1.txt"),true);
FileInputStream inputStream = new FileInputStream("e:\\2.txt");
IOUtils.copy(inputStream,OutputStream);
}
文件操作
package cn.itcast.hdfs;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.junit.Before;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
public class HdfsClient {
FileSystem fs = null;
@Before
public void init() throws Exception{
Configuration conf = new Configuration();
//conf.set("fs.defaultFS","hdfs://192.168.10.128:9000");
/**
* 参数优先级:1.客户端代码中设置的值2.classPath下用户自定义配置的值
* */
//获取一个hdfs的访问客户端
fs = FileSystem.get(new URI("hdfs://192.168.10.128:9000"),conf,"root");
}
/*
* 往hdfs上传文件
* */
public void testAddFileToHdfs() throws Exception{
//要上传的文件的本地路径
//要上传到hdfs的目标路径
Path src = new Path("E:/get/密码链接说明.txt");
Path dst = new Path("/");
fs.copyFromLocalFile(src,dst);
fs.close();
}
/*
* 从hdfs中复制文件到本地文件系统
* @throws IOException
* @throws IllegalArgumentException
* */
public void testDownloadFileToLocal() throws IOException,IllegalArgumentException{
// fs.copyToLocalFile(new Path("/"),new Path("d:/"));
fs.copyToLocalFile(false,new Path("/1.txt"),new Path("e:/"),true);
fs.close();
}
/*
* 目录操作
*
* */
public void testMkdirAndDeleteAndRename() throws IOException,IllegalArgumentException{
//创建目录
fs.mkdirs(new Path("/zhangyaning"));
//删除目录,如果非空,参数2必须给true
fs.delete(new Path("/zhangyaning"),true);
//重命名文件夹
fs.rename(new Path("/zhangyaning"),new Path("/zhang"));
}
/*
* 查看目录信息,只显示文件
* */
@Test
public void testListFiles() throws IOException,IllegalArgumentException, FileNotFoundException {
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"),true);
while(listFiles.hasNext()){
LocatedFileStatus fileStatus = listFiles.next();
System.out.println(fileStatus.getPath().getName());
System.out.println(fileStatus.getBlockLocations());
System.out.println(fileStatus.getPermission());
System.out.println(fileStatus.getLen());
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
for(BlockLocation bl:blockLocations){
System.out.println("block_length:"+bl.getLength()+"__"+"block-offset:"+bl.getOffset());
String[] hosts = bl.getHosts();
for (String host :hosts){
System.out.println(host);
}
}
System.out.println("----------------------------------");
}
}
@Test
public void testListAll() throws FileNotFoundException,IllegalArgumentException,IOException{
FileStatus[] listStatus = fs.listStatus(new Path("/"));
String flag = "";
for (FileStatus fstatus : listStatus){
if(fstatus.isFile()){
flag = "f--";
}else {
flag = "d--";
}
System.out.println(flag + fstatus.getPath().getName());
System.out.println(fstatus.getPermission());
}
}
}
2.案例:shell定时采集数据至HDFS
上线的网站每天都会产生日志数据。假如有这样的需求L要求在凌晨24点开始操作前一天产生的日志文件,准时上传至HDFS集群。
2.1技术分析
HDFS Shell:
hadoop fs -put //满足上传文件,不满足定时、周期性传入。
Linux crontab:
crontab -e
0 0 * * * /shell/uploadFile2HDFS.sh //每天凌晨12:00 执行一次
2.2实现流程
一般日志文件生成的逻辑由业务系统决定,比如每小时滚动一次,或者一定大小滚动一次,避免单个日志文件过大不方便操作。
比如滚动后的文件重命名为access.log.x,其中x为数字。正在进行写的日志文件叫做access.log。这样的话,如果日志文件后缀是1/2/3等数字,则该文件满足需求可以上传,就把该文件移动到准备上传的工作区间目录。工作区间有文件之后,就可以使用hadoop put 命令将文件上传。
2.3代码实现
#!/bin/bash
#set java env
export JAVA_HOME = /....
export JRE_HOME = /...
export CLASSPATH = /...
export PATH = ${JAVA_HOME}/bin:$PATH
#set hadoop env
export HADOOP_HOME = /...
export PATH = ${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH
#日志文件存放的目录
log_src_dir = /root/logs/log/
#待上传文件存放的目录
log_toupload_dir =/root/logs/toupload/
#日志文件上传到hdfs的根目录
date1 = `date -d last-day + %Y_%M_%d`
hdfs_root_dir = /data/clickLog/$date1/
#打印环境变量信息
echo "env:hadoop_home:$HADOOP_HOME"
#读取日志文件的目录,判断是否有需要上传的文件
echo "log_src_dir:"$log_src_dir
ls $log_src_dir | while read fileName
do
if[["$fileName" == access.log.*]];then
#if[access.log == "$fileName"];then
date = `date + %Y_%m_%d_%H_%M_%S`
#将文件移动到带上传目录并重命名
#打印信息
echo "moving $log_src_dir$fileName to $log_toupload_dir"xxxxx_click_log_$fileName"date"
mv $log_src_dir$fileName $log_toupload_dir"xxxxx_click_log$fileName"$date
#将待上传的文件path写入一个列表文件willDoing
echo $log_toupload_dir"xxxxx_click_log_$fileName"$date >> $log_toupload_dir"willDoing."$date
fi
done
#找到列表文件willDoing
ls $log_toupload_dir | grep will | grep -v "_COPY_" | grep -v "_DONE_"| while read line
do
#打印信息
echo "toupload is in file:"$line
#将待上传文件列表willDoing改名为willDoing_COPY_
mv $log_toload_dir$line $log_toupload_dir$line"_COPY_"
#读列表文件willDoing_COPY_的内容(一个一个的待上传文件名),此处的line就是列表中的一个待上传文件的path
cat $log_toload_dir$line"_COPY_" | while read line
do
#打印信息
echo "puting .... $line to hdfs path .... $hdsf_root_dir"
hadoop fs -mkdir -p $hdsf_root_dir
hadoop fs -put $line $hdsf_root_dir
done
mv $log_toload_dir$line"_COPY_" $log_toload_dir$line"_DONE_"
done