HDFS入门

HDFS入门

Hadoop Distribute File System hadoop分布式文件系统

  • 分布式多台机器 解决文件存不下的问题
  • 存储元数据 解决数据查询不方便的问题
  • 分块存储 解决数据上传下载问题
  • 副本机制 解决数据丢失安全问题

在这里插入图片描述

特性
  • 主从架构 主节点从节点各司其职
  • 分块存储 hadoop2.X 默认128M hadoop1.X默认64M 可以通过配置文件修改
  • namenode 主节点 master 需要大量内存( RAM )
    • 负责存储元数据( 目录结构以及分块位置信息 datanode节点信息 )
    • 分块位置信息不会持久化存储, 这些信息在系统启动时从数据节点重建
    • namenode关闭, HDFS/Hadoop 集群就无法访问了 所以是个单点故障
  • datanode 从节点 slave 需要大量磁盘
    • 负责存储数据块(block) 默认每隔6小时向master报告自己存储的块信息
    • 心跳机制 默认每3秒向namenode发送心跳, 如果namenode长时间(3*10)没收到心跳, 会认为dn失效
  • 副本机制 每个文件都有副本 默认3个副本
  • 一次写入 多次读出 HDFS适合写一次,多次读,不支持文件修改
1.txt   100M    block-1: 1-100M
2.txt   300M    block-1: 1-128M   block-2: 128M-256M   block-3:256M-300M
shell命令行
- hadoop fs -ls hdfs://node-1:9000/   操作hdfs文件系统
- hadoop fs -ls file:///root   操作本地文件系统
- hadoop fs -ls /  如果不指定 就采用默认文件系统  
  • hadoop fs -ls [-h] [-R] / 显示文件,目录信息
  • hadoop fs -mkdir [-p] /data 创建目录
  • hadoop fs -put [-f] [-p] localsrc dst 上传linux文件到hdfs文件系统
    • hadoop fs -put /root/1.txt /data
  • hadoop fs -get [-f] [-p] src localsrc 下载hdfs文件系统中的文件到linux
    • hadoop -get /data/1.txt /root
  • hadoop fs -appendToFile localsrc dst 追加一个文件到已经存在的文件末尾
    • hadoop fs -appendToFile /root/2.txt /data/1.txt
  • hadoop fs -cat path 显示文件内容
  • hadoop fs -tail path 动态输出文件内容
  • hadoop fs -chgrp|-chmod|-chown 更新文件的组/权限/拥有者
  • hadoop fs -mv|-cp|-rm 剪切/复制/删除 文件
  • hadoop fs -getmerge /data/*.txt ./log.txt 合并下载多个文件
  • hadoop fs -setrep -w num path 改变文件的副本系数 ( 不推荐使用 )
    • hadoop fs -setrep -w 5 /data/1.txt 速度太慢,影响集群效率
默认三副本策略

在这里插入图片描述

理想状态:

机架(rack)与机架之间通过交换机连接, 机架中的服务器通过交换机连接, 网络拓扑图的远近是指两个服务器之间通信 , 要经过几个交换机

  • 第一个副本, 如果客户端所在机器有datanode, 且datanode状态很好, 磁盘够,则第一个副本存储在这个datanode, 如果不满足, 则找离客户端最近( datanode在网络拓扑图的远近 )的一台机器作为第一个副本所在地.( 一般为同一机架 )
  • 第二个副本, 在不同于第一个副本所在机架的其他机架上 , 随机找一个datanode存储
  • 第三个副本, 在第二个副本所在机架上随机找一个datanode存储( 排除第二个副本所在datanode )

非理想状态: 副本数不为3 或者没有机架…

  • 第一个副本存储和理想转态一样
  • 其他副本随机datanode存储

在这里插入图片描述

文件上传过程

HDFS上传机制:

启动一个线程, 将文件的第一块(block-1) 进行上传到dn1, 由dn1复制到dn2,dn2复制到dn3( 默认3副本 )

第一块上传完成后, 开始第二块(block-2)的上传

  • 客户端RPC请求服务端( namenode )上传文件
  • 服务端根据请求信息, 查询元数据判断是否可以上传
  • 服务端返回信息给客户端
  • 客户端请求上传1.txt block-1( 文件1.txt的第一个分块)
  • 服务端根据请求, 结合dn ( datanode )信息返回3台可用的dn信息( ip )
  • 服务端将上传的dn信息传给客户端
  • 建立传输数据的管道( pipeline )
  • 管道建立完毕, 开始上传, dn将接受到的包( packet 64k) 保存到指定目录, 并且把包往后面发( 备份 )
  • 反方向逐个ACK应答,保证packet安全稳定
  • block-1上传完毕后, 客户端重新发送请求, 上传1.txt block-2( 第二个分块 , 如果有的话)
  • 所有块上传完毕后, 客户端通知namenode, namenode把信息同步到元数据中

hadoop认为在一起请求中(块上传请求), 只要有一个副本上传成功, 则本次上传就算成功, 不足的副本在上传完毕后 指定其他dn进行副本的复制, 最终副本数=设定的副本数

文件下载过程

todo

  • 客户端向namenode发起RPC请求, 请求文件下载

  • 服务端接收请求, 确定客户端可以下载这个文件, 视情况返回部分block或全部block, 对于每个block, namenode都会返回含有该副本的datanode地址

    • 1.txt 300M 2 {block-1,block-2,block-3}

      {block-1:node-1,node2;block-2:node-2,node-3;block-3:node-1,node-3}

    • 返回的dn地址 是经过排序的

      • 网络拓扑图中距离Client近的靠前
      • 心跳机制最后那个超时汇报的靠后
  • 客户端选排名靠前的dn读取block, 如果客户端本身就是dn, 那么直接从本地获取

    • 数据传输的底层是IO流
    • 读完一个block,都会进行checksum验证, 如果出现错误, 客户端会通知namenode, 然后从下一个拥有该block的dn继续读
    • read方法是并行读取block信息 例如 namenode返回三个block 则开启三个线程,并行读取
  • 读取完这一批的block后, 如果还没有结束, 客户端继续向namenode获取下一批block列表

  • 最终读取的block会合并成完整的最终文件

java api

windows平台做hadoop开发, 需要设置hadoop环境, 在window平台编译hadoop

不然会报 winutils.exe null的异常, 这个文件缺失

Failed to locate the winutils binary in the hadoop binary path java.io.IOException: Could not
locate executable null\bin\winutils.exe in the Hadoop binaries.
<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>
package com.hrh;

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.net.URI;

/**
 * @Title: Test
 * @ProjectName BigData
 * @Description: TODO
 */
public class Test {

    public static void main(String[] args) throws Exception {
        //加载配置文件
        // new Configuration();的时候,它就会去加载jar包中的hdfs-default.xml 
        //默认是file:/// 本地文件系统, 而不是hdfs
		// 然后再加载classpath下的hdfs-site.xml
        //参数优先级: 
        //1、客户端代码中设置的值 2、classpath下的用户自定义配置文件 3、然后是服务器的默认配置
        Configuration conf=new Configuration();
        //获得一个hdfs的访问客户端
        FileSystem fs = FileSystem.get(new URI("hdfs://node-1:9000"),conf,"root");
        //本地上传文件
        fs.copyFromLocalFile(new Path("E:\\hr.txt"),new Path("/"));
        //删除文件
        boolean flag = fs.delete(new Path("/hr.txt"), false);
        // 创建目录
        fs.mkdirs(new Path("/a1/b1/c1"));
        // 删除文件夹 ,如果是非空文件夹,参数2必须给值true
        fs.delete(new Path("/aaa"), true);
        // 重命名文件或文件夹
        fs.rename(new Path("/a1"), new Path("/a2"));
        //获取dn信息
        DatanodeInfo[] dataNodeStats = ((DistributedFileSystem) fs).getDataNodeStats();
        for (DatanodeInfo dataNodeStat : dataNodeStats) {
            System.out.println(dataNodeStat.getHostName());
            System.out.println(dataNodeStat.getName());
            System.out.println(dataNodeStat.getLevel());
        }
		//获得指定目录下的文件 分块信息
        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.getBlockSize());
            System.out.println(fileStatus.getPermission());
            System.out.println(fileStatus.getLen());
            BlockLocation[] blockLocations = fileStatus.getBlockLocations();
            for (BlockLocation blockLocation : blockLocations) {
                System.out.println(blockLocation.getLength());
                System.out.println(blockLocation.getOffset());
                String[] hosts = blockLocation.getHosts();
                for (String host : hosts) {
                    System.out.println(host);
                }
            }
        }
      
        //关闭连接
        fs.close();

    }

}

shell定时采集数据到HDFS

编写shell脚本步骤

  • 加载环境变量
  • 定义变量 常量 方便下面的逻辑代码引用
  • 写业务逻辑时, 充分考虑各种情况, 多加判断

上传每天的日志到hdfs

#!/bin/bash

#set java env
export JAVA_HOME=/export/servers/jdk1.8.0_65
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

#set hadoop env
export HADOOP_HOME=/export/servers/hadoop-2.7.4
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 "envs: 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_toupload_dir$line $log_toupload_dir$line"_COPY_"
	#读列表文件willDoing_COPY_的内容(一个一个的待上传文件名)  ,此处的line 就是列表中的一个待上传文件的path
	cat $log_toupload_dir$line"_COPY_" |while read line
	do
		#打印信息
		echo "puting...$line to hdfs path.....$hdfs_root_dir"
		hadoop fs -mkdir -p $hdfs_root_dir
		hadoop fs -put $line $hdfs_root_dir
	done	
	mv $log_toupload_dir$line"_COPY_"  $log_toupload_dir$line"_DONE_"
done
[root@node-1 ~]# sh uploadFile2HDFS.sh 
uploadFile2HDFS.sh: line 1: et: command not found
envs: hadoop_home: /export/servers/hadoop-2.7.4
log_src_dir:/root/logs/log/
moving /root/logs/log/access.log.1 to /root/logs/toupload/xxxxx_click_log_access.log.12018_11_15_20_52_03
moving /root/logs/log/access.log.2 to /root/logs/toupload/xxxxx_click_log_access.log.22018_11_15_20_52_03
moving /root/logs/log/access.log.3 to /root/logs/toupload/xxxxx_click_log_access.log.32018_11_15_20_52_03
moving /root/logs/log/access.log.4 to /root/logs/toupload/xxxxx_click_log_access.log.42018_11_15_20_52_03
toupload is in file:willDoing.2018_11_15_20_52_03
puting.../root/logs/toupload/xxxxx_click_log_access.log.12018_11_15_20_52_03 to hdfs path...../data/clickLog/2018_11_14/
puting.../root/logs/toupload/xxxxx_click_log_access.log.22018_11_15_20_52_03 to hdfs path...../data/clickLog/2018_11_14/
puting.../root/logs/toupload/xxxxx_click_log_access.log.32018_11_15_20_52_03 to hdfs path...../data/clickLog/2018_11_14/
puting.../root/logs/toupload/xxxxx_click_log_access.log.42018_11_15_20_52_03 to hdfs path...../data/clickLog/2018_11_14/
[root@node-1 ~]# cd logs/log
You have new mail in /var/spool/mail/root
[root@node-1 log]# ll
total 4
-rw-r--r--. 1 root root 4 Nov 15 20:50 access.log
[root@node-1 log]# cd ..
[root@node-1 logs]# cd toupload/
[root@node-1 toupload]# cat willDoing.2018_11_15_20_52_03_DONE_ 
/root/logs/toupload/xxxxx_click_log_access.log.12018_11_15_20_52_03
/root/logs/toupload/xxxxx_click_log_access.log.22018_11_15_20_52_03
/root/logs/toupload/xxxxx_click_log_access.log.32018_11_15_20_52_03
/root/logs/toupload/xxxxx_click_log_access.log.42018_11_15_20_52_03
[root@node-1 toupload]# 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值