Hadoop分布式文件系统——HDFS概念以及hdfs读写数据

HDFS一流式数据访问模式来存储超大文件,运行于商用硬件集群上。
一、HDFS 的概念
1、数据块:
(1) 每个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位
(2) HDFS有数据块的概念,默认是64M,hadoop2中是128M。对分布式文件系统中的块进行抽象会带来很多好处。
* 最明显的好处就是,一个文件的大小可以大于网络中任意一个磁盘的容量。文件的所有快并不要存储在其他任意一个磁盘中,可以利用集群存储在任意一个磁盘中。
* 使用抽象块而非整个文件作为存储单元,大大简化了存储系统的设计。
2、namenode和datanode
HDFS集群上有两类节点以管理者-工作者模式运行,即一个namenode(管理者)和多个datanode(工作者)
(1)namenode管理文件系统的命名空间。他维护这文件系统树及整个树内所有的文件和目录这些信息一两个文件形式永久保存在本地磁盘上:命名空间镜像文件和编辑日志文件。namenode也记录着每个文件中各个块所在的数据节点信息,但它并不永久的保存块的位置信息。
(2)客户端(client)代表用户通过与namenode和datanode交互来访问整个文件系统。
(3)datanode是文件系统的工作节点。根据需要存储检索数据块,并且定期向namenode发送他们所存储的块的块的列表。
(4) 对namenode实现容错非常重要,Hadoop为此要两种机制:
* 第一种机制是备份那些组成文件系统元数据持久状态的文件。Hadoop通过配置使namenode在多个文件系统上实时同步的保存元数据的持久状态(原子操作)。一般配置是将持久状态写入一个远程挂载的网络文件系统(NFS)。
* 另一种可行的方法就是运行一个辅助的namenode,但他不能作为namenode使用。辅助namenode的作用是定期通过编辑日志合并命名空间镜像,以防止编辑日志过大。辅助namenode的保存状态滞后主namenode,当主namenode失效时,把存储在NFS上的namenode元数据复制到辅助namenode上,并作为主namenode。
(5)HDFS高可用性,namenode是唯一存储元数据与文件到数据块映射的的地方。
如何恢复一个失效的namenode?
* 系统管理员要启动一个拥有文件系统元数据副本的新的namenode,并配置datanode和客户端以便使用这个新的namenode。
* 新的namenode响应服务的条件:
一是将命名空间的映像导入内存中;
二是重做编辑日志;三是接受足够多的来自datanode的数据块报告并退出安全模式。
* 故障切换与规避:一个称为故障转移控制器的系统中有一个新实体管理这将活动namenode转移为备用namenode的转换过程。每个namenode运行一个轻量级的故障转移控制器,其工作就是监视宿主namenode是否失效并在namenode失效时进行故障切换,可以组织两个namenode进行有序切换。

二、HDFS常用命令:
hadoop fs
注:path 为路径 src为文件路径 dist 为文件夹
1、-help[cmd] 显示命令的帮助信息
2、-ls(r) 显示当前目录下的所有文件 -R层层循出文件夹
3、-du(s) 显示目录中所有文件大小
4、-count[-q] 显示当前目录下的所有文件大小
5、-mv 移动多个文件目录到目标目录
6、-cp 复制多个文件到目标目录
7、-rm(r) 删除文件(夹)
8、-put 本地文件复制到hdfs
9、-copyFromLocal 本地文件复制到hdfs
10、-moveFromLocal 本地文件移动到hdfs
11、-get[-ignoreCrc] 复制文件到本地,可以忽略crc校验
12、-getmerge 将源目录中的所有文件排序合并到一个文件中
13、-cat 在终端显示文件内容
14、-text 在终端显示文件内容
15、-copyToLocal[-ignoreCrc] 复制文件到本地
16、-moveToLocal 移动文件到本地
17、-mkdir 创建文件夹 后跟-p 可以创建不存在的父路径
例:bin/hdfs dfs -mkdir -p /dir1/dir11/dir111
18、-touchz 创建一个空文件

三、Hadoop文件系统
1、从Hadoop URL读取数据

public class HdfsTest {
             static{
         URL. setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());//每个Java虚拟机只能调用一次这个方法,因此在静态方法中调用。
            }
  @Test
public void testURL(String path){
      path= "hdfs://hadoop:8020/words.txt";
      InputStream input= null;
      try {
          input= new URL( path).openStream();
          IOUtils. copyBytes(input, System. out, 4096,false );
                } catch (MalformedURLException e) {
                          e.printStackTrace();
              } catch (IOException e) {
                          e.printStackTrace();
              }finally {
            IOUtils. closeStream(input);
          }
      }
}

这里采用的方法是通过FsUrlStreamHandlerFactory实例去调用java.net.URL对象的setURLStreamHandlerFactory方法。然后调用org.apache.hadoop.io.IOUtils类,并在finally自剧中关闭数据流,同时也可以在输入流和输出流之间复制数据copyBytes方法的最后两个参数第一个设置用于复制缓冲区大小,第二个设置用于复制结束后是否关闭数据流。
2、通过org.apache.hadoop.fs.FileSystem API读取数据
FileSystem是一个通用的文件系统API,获取FileSystem实例有以下几个静态工厂方法。

Configuration对象封装了客户端或服务器的配置,通过设置配置文件读取类路径来实现(如conf/core-site.xml)
 public staticFileSystem get(Configurationconf)throwsException{}//返回的是默认文件系统在(conf/core-site.xml中指定的,如果没有指定,则使用默认的本地文件系统)
public static FileSystem get(URI uri,Configuration conf ) throws Exception{}//第二个方法通过给定的URI方案和权限来确定要使用的文件系统,如过给定的URI没有指定方案,则返回默认    的文件系统
public static FileSystem get(URI uri,Configuration conf ,String user) throws Exception{}//作为给定用户访问文件系统,对安全来说至关重要。

获取本地系统的运行实例,可以通过getLocal()方法获取

public static LocalFileSystem getLocal(Configuration conf) throws Exception{}

获取FileSystem实例后,可以调用open()函数来获取文件的输入流:

  public FSDataInputStream open(Path path ) throws Exception{}
  public abstract FSDataInputStream open(Path path, int buffSize) throws Exception{}

实例:

public void testFileSystem() throws Exception{
                        String uri= "hdfs://hadoop:8020/words.txt";
                        Configuration conf= new Configuration();
                        FileSystem fs=FileSystem. get(URI.create (uri ), conf );
                        InputStream in= null;
                         try {
                                     in= fs.open( new Path( uri));
                                    IOUtils. copyBytes(in, System.out, 4096, false);
                        } finally{
                                    IOUtils. closeStream(in);
                        }
            }

FSDataInputStream对象
FileSystem对象中的open()方法返回的是FSDataInputStream对象,此对象是继承了java.io.DataInputStream接口,同时实现了org.apache.hadoop.fs.Seekable接口以及org.apache.hadoop.fs.PositionedReadable接口的一个特殊类

  public class FSDataInputStream extends DataInputStream implements Seekable,PositionedReadable{}

Seekable接口支持文件中找到指定位置,并提供了一个查询当前位置相对与文件起始位置偏移量(getPos())的查询方法。而其中的seek()方法可以移动到文件中任意一个绝对位置,与java.io.InputStram中的skip()方法不同,skip()方法只能相对于当前位置定位到另一个新位置。

public interface Seekable{
             void seek( long pos) throws Exception;
             void getPos() throws Exception;
             boolean seekToNewSource(long targetPos) throws Exception;
}

PositionedReadable接口从一个指定偏移量处读取文件的一部分。
其中的read()方法从指定position处读取至多为length字节的数据并存入缓冲区buffer的指定偏移量offset处。返回值是实际读到的字节数。
readFully()方法将指定length长度的字节数数据读取到buffer中,除非读取到文件的末尾,这种情况将抛出EOFException异常。

public interface PositionReadable{
             public static read(long postion, byte[] buffer,int offset,int length) throws Exception;
             public void readFully(long postion,byte[] buffer, int offset, int length) throws Exception;
             public void readFully(long postion,byte[] buffer) throws Exception;
}

所有的这些方法都会保留文件的当前偏移量,并且是线程安全的。
最后,seek()方法是一个相对于高开销的操作,需要谨慎使用。建议使用流数据来创建应用的访问模式(如使用MapReduce),而非之行大量seek()方法。
3、 写入数据
(1)最简单的方法就是给准备见的文件指定一个Path对象,然后返回一个用于写入数据的输出流。

public FSDataOutputStream create(Path path ) throws Exception{}

此方法的有多个重载版本,指定是否需要强制覆盖现有的文件,文件备份数量,写入文件时所用的缓冲区大小、文件块大小以及文件权限。create()方法能够为需要写入且当前不存在的文件创建父目录。如果希望父目录不存在就导致文件写入失败,则可以先调用exists()方法检查父目录是否存在。

@Test
public void uploadFilt2Hdfs() throws Exception{
   String localFile= "D://log_network.txt";
   String hdfsFile= "hdfs://hadoop:8020/upload/log.txt" ;//把本地文件写入到输入流
   InputStream input= new BufferedInputStream( new FileInputStream( localFile));
   FileSystem fs=FileSystem. get(new URI( hdfsFile), new Configuration());
   OutputStream out= fs.create( new Path( hdfsFile), new Progressable() {
   @Override
  public void progress() {
     System. err.println("" );
     }
 });
  IOUtils. copyBytes(input, out, 4096,true);
}

(2)另一种新建文件的方法是使用append()方法在一个已有文件末尾追加数据。

public FSDataOutputStream append(Path path ) throws Exception{}

此追加操作允许一个writer打开一个文件后在访问该文件的最后偏移量出追加数据,可以创建无边界文件。例如在关闭日志文件之后继续追加日志。
(3)FSDataOutputStream对象
也有一个查询当前位置的方法:

public class FSDataOutpueStream extends DataOutputStream implements Syncable{
             public long getPos() throws IOException{}
}

与FSDataInputStream类不同的是,FSDataOutputStream类不允许在文件中定位。HDFS值允许对一个已打开的文件顺序写入,或在现有的文件末尾追加数据,他不支持出文件末尾外的其他位置写入数据。因此写入数据时定位没什么卵用。
(4)目录
FileSystem提供了创建目录的方法:

public boolean mkdirs(Path path) throws Exception{}

此方法可任意一次性创建所有必要但还没有的父目录。若创建成功则返回true。若不想显示创建一个目录,可调用create()方法写入文件时会自动创建文件的父目录。
4、查询文件系统
(1)文件元数据:FileStatus:FileStatus封装了文件系统中文件和目录的元数据,包括文件的长度,块大小,复本,修改时间,所有者以及权限信息。FileSystem中的getFileStatus()方法用于获取文件或目录的FileStatus对象。
检查文件或目录是否存在,可以调用exists()方法:

public boolean exists(Path path) throws Exception{}

(2)列出文件:FileSystem的listStatus()方法的功能。

public FileStatus[] listStatus(Path path) throws Exception{}
public FileStatus[] listStatus(Path path,PathFilter filter) throws Exception{}
public FileStatus listStatus(Path[] paths) throws Exception{}
public FileStatus listStatus(Path[] paths,PathFilter filter) throws Exception{}

当传入的参数是一个文件时,他会简单转变成一数组方式返回长度为1的FileStatus对象。当传入参数是一个目录时,则返回0或多个FileStatus对象,表示目录中包含的文件和目录。
而它的重载方法允许使用PathFilter来显示匹配的文件和目录。
如果指定一组路径,其执行结果相当于依次轮流传递每条路径对其调用listStatus()方法,再将FileStatus对象数组累积存入同一数组中,简便。可运用于从文件系统树的不同分支构建输入文件列表。

import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FileUtil;
import org.apache.hadoop.fs.Path;

public class ListStatus {
             public static void main(String[] args) throws Exception {
                        String uri= args[0];
                        Configuration conf= new Configuration();
                        FileSystem fs=FileSystem. get(URI.create (uri ),conf );
                        Path[] paths= new Path[ args. length];
                         for ( int i = 0; i < paths. length; i++) {
                                     paths[ i]= new Path( args[ i]);
                        }
                        FileStatus[] listStatus= fs.listStatus( paths);
                         /**
                         * 注意:FileUtil中的stat2Paths方法的使用,
                         * 他将一个FileStatus对象数组转换为一个Path对象的数组
                         */
                        Path[] listedPaths=FileUtil. stat2Paths(listStatus);
                         for (Path path : listedPaths) {
                                    System. err.println(path );
                        }
            }
}

(3)文件模式
一个处理日志的MapReduce作业可能需要分析一个月内包含大量目录中的日志文件。在一个表达式中使用通配符来匹配多个文件时比较方便的,无需列举每个文件和目录来指定输入,该操作成为“通配”(globbing)。Hadoop为之行通配提供了两个FileSystem方法:

  public FileStatus[] globStatus(Path pathPattern) throws Exception{}
 public FileStatus[] globStatus(Path pathPattern,PathFilter filter) throws Exception{}

globStatus()方法返回与其路径匹配指定模式的所有文件的FileStatus对象数组,并按路径进行排序。PathFilter命令作为可选项可以进一步对匹配结果进行限制。
通配符及其含义:
这里写图片描述
有日志文件存储在按日期分层组织的目录结构中,日期通配符为:
这里写图片描述
(4)PathFilter:匹配正则表达式的路径

import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
public class RegexExcludePathFilter implements PathFilter {
             private final String regex ;
             public RegexExcludePathFilter(String regex ) {
                         this. regex = regex;
            }
             @Override
             public boolean accept(Path path) {
                         return !path .toString().matches(regex);
            }
}

还可以过滤优化结果,如例将扩展到/2007/12/30
fs .globStatus(new Path(“/2007//“), new RegexExcludePathFilter(“^.*/2007/12/31$”));
过滤器由Path表示,只能作用于英文名。不能针对文件的属性(例如创建时间)来构建过滤器,通配符模式和正则表达式同样无法对文件属性进行匹配。例如,如果将文件存储在按照日起排列的目录结构中,则可以根据PathFilter在给定时间范围内选出文件。
(5) 删除文件
使用FileSystem的delete()方法可以永久性删除文件或目录。

public boolean delete(Path path,boolean recursice ) throws IOException{}

如果path是一个文件或空目录,那么recursice的值就会忽略。只有在recursice为true时,非空目录及其内容才会被删除,否则会抛出IOException异常。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值