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