201707041940复习-hadoop篇1:文件系统的读写

一、hadoop文件系统

  1、集群中所有机器的磁盘作为整体,以每个block作为最小存储单元,每个block128M, 大小可以在hdfs-site.xml文件中调整

  2、block块大小为何设置为128M? 答:为了减少寻址时间,寻址时间要在数据传输时间的1%以下,寻址速度快,那么把block块大小也可增加

  3、读数据:

    3.1:使用命令行读取,hdfs fs -cat hdfs://localhost:9000/user/data

             要想使用hdfs命令,这台机器必须安装hadoop,但是安装后只作为客户端,并不作为集群中的某个节点来使用。

    3.2:使用java代码读取:

import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

/**
 * 通过api访问hdfs文件系统1:不适用hadoop提供的FileSystem,使用协议工厂方式访问
 * Created by Administrator on 2017/4/26.
 */
public class TestHDFS {

    static {
        // 注册协议处理器工厂,让java程序能够识别HDFS协议
        URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
    }


    public static void main(String[] args) throws IOException {
        // 定义url地址
        String url = "hdfs://s128:8020/user/ubuntu/data/hello.txt";

        // URL对象
        URL u = new URL(url);

        // URL链接
        URLConnection conn = u.openConnection();

        // 打开输入流
        InputStream is = conn.getInputStream();

        // 定义一个输出流
        FileOutputStream fos = new FileOutputStream("d:/hello.txt");

        byte[] buf = new byte[1024];
        int len = -1;
        while ((len = is.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        is.close();
        fos.close();
        System.out.println("over");
    }
}
View Code

      3.2.1:读数据解析:

          a:首先获取configuration,默认读取hadoop的jar包里的core-site.xml ,但是可以在classpath下增加自己的core-site.xml,或者在代码中手动配置

          b:通过配置获取到文件系统,文件系统中包含以下内容:

          c:通过文件系统对象,连接远程机器,open()方法中通过rpc调用,得到一个FSDataInputStream对象,

             FSDataInputStream是对DFSInputStream类的一个包装,而DFSInputStream类包含了namenode和datanode 的io,如图:

             FSDataInputStream此时已经获得了文件的初始block的所有的位置。

             d:FSDataInputStream对象的read()方法,去找对应该打datanode,读取数据,然后读取到的内容保存到客户端的一个内存空间即byte[]数组里,然后讲数组内容写到本地。

      3.2.2:读取数据的细节:

          以上读取数据对于客户端是透明的,客户端只知道调用了read()方法,然后就能一直得到数据,然后保存到byre[]数组,然后写到本地。

         细节1:读取一个block完毕,重新找namenode获取下一个block的位置,

           细节2:读取如果出错,FSDataInputStream已经保存了block各个datanode的地址,它会先关闭当前这个出错的连接,并报告给namenode,然后从下一个最近的datanode上读取。

           细节3:一次读取多少?怎样指定读取第2个block块?

        细节4:数据的校验细节:略    

  4、写数据

/**
 * 使用hadoop提供的FileSystem来往hdfs上写数据
 * Created by Administrator on 2017/4/26.
 */
public class TestFileSystem {
    @Test
    public void writeFile() throws IOException {
        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);
        Path path = new Path("hdfs://s128:8020/user/world.txt");
        // 数据输出流
        FSDataOutputStream fsDataOutputStream = fs.create(path);
        fsDataOutputStream.write("helloworld".getBytes());
        fsDataOutputStream.close();
        System.out.println("over");
    }
}
View Code

  4.1、首先得到一个FileSystem对象,这里是DistributedFileSystem对象,

  4.2、调用DistributedFileSystem的create()方法,这里是rpc调用,向namenode发出一个写文件的请求,此时namenode会进行一系列的检查:确保该文件还不存在,或者当前用户有权限写文件。检查失败抛出异常。否则返回一个FSDataOutputStream对象,同FSDataInputStream对象,FSDataOutputStream对象封装了一个DFSOutputstream对象,这个对象负责和namenode和datanode的通信

  4.3、DFSOutputstream将数据分成刚刚一个个的数据包,并写入内部队列,成为数据队列,

  4.4、DataStreamer负责处理数据队列,根据datanode列表来要求namenode分配合适的新块来存储。这一组datanode构成一个管线,DataStreamer将数据包流式传输到管线中的第一个datanode,然后该datanode存储数据包,并将它发送到管线的第2个datanode,同样,第二个datanode存储数据包并发给第三个datanode。

  4.5、DFSOutputStream也维护这一个内部数据包队列来等待datanode的收到确认回执,称为确认队列,收到管道中所有datanode的确认消息后,该数据包才会从确认队列删除。

  4.6、写入过程出错:第一个datanode写成功,第二个datanode失败:

      先关闭管线,将数据包添加到数据队列的最前端,然后将存储过了数据的datanode的当前数据块增加一个标识,并将该标识发给namenode,以便故障那么node恢复后可以删除数据

  4.7、数据写入并不是立即可见的,即使是客户端已经flush了,在服务端还是在缓存中,只有当调用FSDataOutputStream的sync()方法,数据才会真正写入到磁盘中,别的用户才可以读取数据。

二、namenode和secondaryNamenode的工作原理

  接受客户端的请求,并响应请求

  保存了edits和fsimage的持久化文件,接收到写请求时,先在edits文件中记录一条,然后在内存中记录一条,当edits达到64M时,或者过了一个小时时,edits文件会滚动,此时secondaryNamenode会获取edits文件和fsimage文件,加载到内存合并,然后将新的fsimage文件发回给namenode,此时namenode用心得fsimage文件替换旧的fsimage文件,并,删除旧的edits文件,

三、遇到大量小文件如何处理:

  大量小文件,会增加namenode的压力,每记录一个小文件,在namenode的内存中大概占用150kb的内存来存储相应的元数据。

  方法1:SequenceFile是一个小文件的很好的容器,多个小文件合成一个文件,namenode内存中只记录这么一个文件。压力减小。而在sequenceFile文件中,有相应的同步点,来区分不同的文件,这样,各个文件既能同时独立存在,又减小了namenode的压力。sequenceFile是以一种kv对的文件,可以写一个mr程序,这个程序中自定义inputformat,输出SequenceFileOutputFormat,以文件名作为key。

  方法2:mr程序使用combineInputFormat来读取大量小文件,而不会产生大量的map进程。这只能减少在mr作业时的map任务数量,并不能减少namenode 的压力

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2017-09-04 17:11  IT豪哥  阅读(124)  评论(0)    收藏  举报