• 大数据学习day10-----zookeeper--------1.小文件合并,2 输入和输出 3 多路径输出 4.zookeeper(选举机制,安装,zk的shell客户端、java客户端)


    1. 小文件合并

    HDFS中不适合存储大量的小文件,原因如下;

    • 无论文件大小,namenode记录的元数据大小几乎是一致的(1KB的文件与120M的文件在namenode中的元数据都是一样的)
    • namenode的内存有限,记录的元数据条数有限,集群的存储容量受限,所以HDFS不能无限添加datanode扩容
    • 增加namenode管理元数据的压力
    • MR程序默认的是使用TextInputFormat类,计算任务的时候是以文件数量为基准的,大量的小文件会启动大量的maptask,而maptask内部处理数据是比较复杂的,这会降低处理数据的效率

    所以

      尽量别在hdfs上存储小文件,如果有大量的小文件产生,最好将小文件合并以后再上传

      假如小文件真存储在了HDFS中,这是需要避免处理数据时大量maptask的产生

    案例:

     将E:wcfilemergeinput中的所有文件中的内容读取到一个文件中,文件中内容的格式为文件名:内容

    A. 默认设置(产生多个map任务(文件的个数)处理的情况),代码如下:

    public class Merge1 {
        static class MergeMapper extends Mapper<LongWritable, Text, Text,Text >{
            // 获取文件名
            String fileName = null;
            @Override
            protected void setup(Mapper<LongWritable, Text, Text, Text>.Context context)
                    throws IOException, InterruptedException {
                FileSplit fs = (FileSplit)context.getInputSplit();
                fileName = fs.getPath().getName();
            }
            // 处理每行数据
            StringBuilder sb = new StringBuilder();
            @Override
            protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, Text>.Context context)
                    throws IOException, InterruptedException {
                String line = value.toString();
                sb.append(line+" ");
            }
            // 将数据以filename为key,文件内容为value写出
            @Override
            protected void cleanup(Mapper<LongWritable, Text, Text, Text>.Context context)
                    throws IOException, InterruptedException {
                context.write(new Text(fileName), new Text(sb.toString().trim()));
            }
        }
        static class MergeReducer extends Reducer<Text, Text, Text, NullWritable>{
            @Override
            protected void reduce(Text key, Iterable<Text> iters, Reducer<Text, Text, Text, NullWritable>.Context context)
                    throws IOException, InterruptedException {
                String fileName = key.toString();
                Text next = iters.iterator().next(); //  迭代器中只有一条数据
                String content = next.toString();
                context.write(new Text(fileName+":"+content), NullWritable.get());
            }
        }
        public static void main(String[] args) throws Exception {
            Configuration conf = new Configuration();
            Job job = Job.getInstance(conf);
    
            job.setMapperClass(MergeMapper.class);
            job.setReducerClass(MergeReducer.class);
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(Text.class);
    
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(NullWritable.class);
    
            // 输出好输入的数据路径
    
            FileInputFormat.setInputPaths(job, new Path("E:/wc/filemerge/input/"));
            FileOutputFormat.setOutputPath(job, new Path("E:/wc/filemerge/output1/"));
            // true 执行成功
            boolean b = job.waitForCompletion(true);
            // 退出程序的状态码 404 200 500
            System.exit(b ? 0 : -1);
        }
    }
    View Code

    此处的代码需要注意的点:

    (1)StringBuilder的使用(节省资源,若直接使用String会在常量池创建多个空间)

     (2)迭代器中只有一个值时,直接获取值,不需要遍历

    运行部分结果如下:

    B.  比较少mapTask个数的情况(此种情况不能得到文件名)

     代码如下

    public class Merge2 {
        static class MergeMapper2 extends Mapper<LongWritable, Text, Text, NullWritable>{
            StringBuilder sb = new StringBuilder();
            @Override
            protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context)
                    throws IOException, InterruptedException {
                try {
                    String line = value.toString();
                    sb.append(line+" ");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            @Override
            protected void cleanup(Mapper<LongWritable, Text, Text, NullWritable>.Context context)
                    throws IOException, InterruptedException {
                context.write(new Text(sb.toString().trim()), NullWritable.get());
            }
        }
        // 可以不需要reduce
        static class MergeReducer2 extends Reducer<Text, NullWritable, Text, NullWritable>{
            @Override
            protected void reduce(Text key, Iterable<NullWritable> iters,
                    Reducer<Text, NullWritable, Text, NullWritable>.Context context) throws IOException, InterruptedException {
                context.write(key, NullWritable.get());
            }
        }
    public static void main(String[] args) throws Exception {
            
            // 获取mr程序运行时的初始化配置
            Configuration conf = new Configuration();
            //一个maptask处理的最小数据大小
            // 参数一  name  参数二  处理数据量的最小值 单位字节  
            conf.setLong("mapreduce.input.fileinputformat.split.minsize", 1024*2);//2M
            Job job = Job.getInstance(conf);
            // 设置map和reduce类  调用类中自定义的map reduce方法的业务逻辑
            job.setMapperClass(MergeMapper2.class);
            job.setReducerClass(MergeReducer2.class);
            // 设置map端输出的key-value的类型
            job.setMapOutputKeyClass(Text.class);
            job.setMapOutputValueClass(NullWritable.class);
            // 设置reduce的key-value的类型   结果的最终输出
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(NullWritable.class);
            //设置reducetask的个数  默认1个
            //job.setNumReduceTasks(3);
            // 修改默认的输入对象
            job.setInputFormatClass(CombineTextInputFormat.class);
            // 处理的文件的路径
            FileInputFormat.setInputPaths(job, new Path("E:/wc/filemerge/input"));
            // 结果输出路径
            FileOutputFormat.setOutputPath(job, new Path("E:/wc/filemerge/output3/"));
            // 提交任务   参数等待执行
            job.waitForCompletion(true) ;
        }
    }
    View Code

    知识点

     2. 输入和输出

      

       day07索引案例中利用MR程序进行了两次的计算,第二次的计算是以第一次的计算结果为输入文件的(由于默认是TextInputFormat,第一次MR计算结果输出是文本文件),此处可以将MR第一次计算得到的文本文件改为sequence文件(kvkvkvkv形式,比如MR程序中map阶段处理完交给reduce阶段的数据就是sequence)  

     

     若让A输出文件类型变为sequence,这样当写第二个MR程序对第一次结果作为输入文件进行计算时,map阶段就不需要进行文件的处理了(如得到offset,line等,直接就能获取到kv值,效率比较高)

     

     代码如下

    Index1:在原先的代码中改数据输出类型为:SequenceFileOutputFormat,如下图

     Index2:改变处

    • 声明以SequenceFile文件的格式输入数据处理,如下图

    •  接收的数据是kv的形式,不需要再像以前那样写成LongWritable,Text

     3. 多路径输出

     可以将计算结果输出到不同的目录中(具体可见教案5.1.2.2

     

     4. zookeeper

     4.1 简介:

    •  是什么

       是一个分布式协调工具,一个集群(不会存在单点故障:多台zk,并且记录相同的数据

    •  用处是什么

      (1)记录数据(重要状态数据,大小不要超过64k) 

      (2)为客户提供读取数据的功能

      (3)当客户端读取的数据发生变化时,zk会以时间的形式告知客户端(事件监听)  

    •  运用场景

      

     4.2 选举机制

    4.2.1 知识补充

     leader  领导(只有一个)

     flower  小弟(有多个)

     Zookeeper会在集群中安装多个,在每个zk中都有一个唯一的不重复的ID,数据在存储的时候有一个数据VERSION,版本越新就越大

     常见的几个端口

    2181:leader和flower接收客户端请求的端口

    2888:leader和flower内部通信的端口

    3888:选举的端口

     4.2.2  选举过程

     (1)初次启动(3台机器为例)【3-5台,一般为奇数台】

    • id为1的服务器启动,它向局域网发送广播寻找leader(端口2888)。发现没有leader,这台服务器就进入选举状态(3888端口),并向局域网广播投票(投自己);
    • id=2的服务器启动,它向局域网寻找leader,发现没有,进入投票状态(3888端口),收到服务器1所投的票;然后发现1<自己2,向局域网广播投票(投2);此时,服务1也会收到2的投票,发现2>自己,重新投(投2);此时,服务器2会收到两票;然后通过配置文件获知集群总共有3台机器,从而知道自己已经得多数票(当选);服务器2就切换到leader状态(2888);

    • id=3的服务器启动了,它向局域网寻找leader,发现服务器2是leader,自己主动进入follower状态

    (2)leader宕机后

       leader宕机以后集群中所有的节点进入投票状态 , (先按照数据的version 版本最新的节点当选leader , 如果多个节点的数据版本一致再按照id比较选举)  

    4.3 数据结构和类型 

     4.3.1  前言:

      zookeeper将数据存储于内存中,具体而言,Znode是存储数据的最小单元,而Znode被以层次化的结构进行组织,形容成一棵树。其对外提供的视图类似于Unix文件系统。树的根Znode节点相当于Unix文件系统根路径。正如Unix中目录下可以有子目录一样,Znode节点下也可以挂载子节点,最终形成如下所示结构:

       以文件系统进行类比的话,Znode天然具有目录和文件两重属性:即Znode既可以当做文件往里面写东西,又可以当做目录在下面挂载其他Znode。当然,由于Znode具有不同的类型,后半部分并不完全准确。

      ZK可以记录用户的一些状态信息(数据) ,不易过大,要求在64k以内,它是以一种特殊的key/value的形式存储数据的  , key类似于文件系统中的路径 ,目的是为了维护key的层级关系

     4.3.2  ZK的数据节点类型

    • 临时节点

        临时结点的生命周期和客户端会话保持一致。客户端段会话存在的话临时结点也存在,客户端会话断开则临时结点会自动被服务端删除。临时节点下不能创建子节点。

    • 临时有序节点:在具有临时节点的基本特性的基础上,会通过在结点路径后缀一串序号来区分多个子结点创建的先后顺序。这工作由Zookeeper服务端自动给我们做,只要在创建Znode时指定结点类型为该类型。
    • 持久节点:最常见的Znode类型,一旦创建将已知存在于服务端,除非客户端通过删除操作进行删除,持久节点下可以创建子节点
    • 持久顺序节点

     4.4 安装

     (1)上传------->rz

     (2)解压配置

    •  tar -xzvf zookeeper-3.4.6.tar.gz  -C /usr/apps/
    • 进入conf文件,将zoo_sample.cfg  文件名改为zoo.cfg
    • 在 zookeeper-3.4.6文件中创建一个用于存放数据的文件夹,此处自己为zk_data
    • 修改zoo.cfg中的内容 ,如下   

       dataDir=/usr/apps/zookeeper-3.4.6/zk_data 

      在末尾

    server.1=feng01:2888:3888
    server.2=feng02:2888:3888
    server.3=feng03:2888:3888

    (3)分发

        scp zookeeper-3.4.6 feng02:$PWD,同理第三台机器(此处的pwd表示的是命令行当前的路径)

    (4)分发完成后分别在各台机器中的/usr/apps/zookeeper-3.4.6/zk_data 目录中执行------->echo 1/2/3  > myid 

    (5)环境变量的配置如下图(vi /etc/profile)

     (6)启动

    • 启动ZK服务:zkServer.sh start
    • 查看节点的状态:zkServer.sh status
    • 进入shell客户端:zkCli.sh 

     4.5 zk客户端

     4.5.1 shell

     进入shell客户端:zkCli.sh 

     

    ls  /  查看根目录下所有的节点
    create  /user   lisi  在根目录下创建节点  key(/user)  value(lisi)
    get  /user   获取节点的数据值
    set /user  新值    更新节点的值

    4.5.1.1 创建节点(一定要有值)

    create  /节点名   值     默认创建永久节点
    create  -e   /节点名  值   创建临时节点  断开以后数据删除
    create -s /BIGDATA  hbase    永久的有序节点
    create -e -s   /BIGDATA hive    临时有序节点
    -e  临时节点  EPHEMERAL   ephemeral
    -s 有序节点   sort

    4.5.1.2 查看节点

     ls    ls2(相比ls,其会输出二外的信息,如下图)

     

     4.5.1.3 修改节点数据

     修改以后数据的版本号会递增

      4.5.1.4 删除节点

     

     4.5.1.5 时间监听演示

    •  数据的变化
    • 子节点个数的变化

     (1)数据变化:

     (2)子节点变化(删除子节点,添加子节点)

    4.5.1 java客户端

    (1)java客户端连接zk以及获取zk的java客户端对象

    public class ConnectionDemo {
        public static void main(String[] args) throws Exception {
            // 连接zk的地址  2181  2888  3888
            // zk所有节点的机器的位置
            String connectString = "feng01:2181,feng02:2181,feng03:2181";
            //超时时间
            int sessionTimeout = 2000;
            // 获取zk的客户端对象
            ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, null) ;
            // 操作zk节点   获取/路径下所有的子节点  ls /
            List<String> nodes = zk.getChildren("/", null);
            for (String node : nodes) {
                //[zookeeper, user0000000007, user, BIGDATA]
                System.out.println(node); 
            }
            // 释放客户端对象
            zk.close();
        }
    }
    View Code

    (2)在zk的java客户端创建节点

    public class CreateNode {
        public static void main(String[] args) throws Exception{
            // 获取zk
                    String connectString = "doit01:2181,doit02:2181,doit03:2181";
                    //超时时间
                    int sessionTimeout = 2000;
                    // 获取zk的客户端对象
                    ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, null) ;
                     // 创建节点
                    /*
                     * 参数一  key(路径)  Path must start with / character
                     * 参数二 值  字节
                     * 参数三  权限  OPEN_ACL_UNSAFE开放所有的权限
                     * 参数四  节点类型
                     *  PERSISTENT  永久
                     *  PERSISTENT_SEQUENTIAL  有序
                     *  EPHEMERAL   临时 
                     *  EPHEMERAL_SEQUENTIAL
                     */
                    String create = zk.create("/BIG2", "big".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT) ;
                    // 1 临时节点
                    // 2 永久节点
                    // 3 有序节点
                    System.out.println(create);
                    //释放zk
                    zk.close();
        }
    }
    View Code

    (3)获取节点数据

    public class GetNodeData {
    
        public static void main(String[] args) throws Exception {
    
            // 获取zk
            String connectString = "doit01:2181,doit02:2181,doit03:2181";
            // 超时时间
            int sessionTimeout = 2000;
            // 获取zk的客户端对象
            ZooKeeper zk = new ZooKeeper(connectString, sessionTimeout, null);
            // 获取/BIG节点的值数据
            byte[] data = zk.getData("/BIG", null, null);
            // 打印
            System.out.println(new String(data));
            zk.close();
    
        }
    
    }
    View Code

    (4)删除节点delete(只能删除没有子节点的节点),以下是演示代码

    /**
     * 测试Zk的其他方法
     * 创建节点
     * 删除节点
     * 获取节点数据
     * 判断节点是否存在
     * 获取所有的子节点
     * 修改节点
     * 
     * @author ThinkPad
     *
     */
    public class ClientTest {
        // zk所有节点的机器的位置
        String connectString = "doit01:2181,doit02:2181,doit03:2181";
        // 超时时间
        int sessionTimeout = 2000;
        ZooKeeper zk = null;
    
        /**
         * 获取zk对象
         * 
         * @throws Exception
         */
        @Before
        public void init() throws Exception {
            zk = new ZooKeeper(connectString, sessionTimeout, null);
        }
    
        /**
         * 单元测试的类中不要写main方法 所有的方法是公共 的 所有的方法不能有参数 不能有返回值
         * 
         * @throws InterruptedException
         * @throws KeeperException
         */
        @Test
        public void exists() throws KeeperException, InterruptedException {
    
            Stat stat = zk.exists("/BIG22", null);
            System.out.println(stat != null ? "存在" : "不存在");
            /*
             * if(stat!=null) { System.out.println("存在"); System.out.println(stat); }else {
             * System.out.println("不存在"); }
             */
        }
    
        /*
         * 删除空节点
         *  删除任意节点  /BIGDATA
         *     递归  遍历下面的所有的节点   依次删除
         *        都有出口
         */
        @Test
        public void deleteNode() throws Exception {
            /*
             * 参数1 路径 参数2 数据版本 -1 所有的版本
             */
            // 判断节点是否存在
            // 1 NoNode for /BIG
            // 2 Directory not empty for /BIGDATA
            // zk.delete("/BIGDATA", -1);
            if (zk.exists("/BIGDATA", null) != null) {
                // 是否没有子节点
                if (zk.getChildren("/BIGDATA", null).size() == 0) {
                    zk.delete("/BIGDATA", -1);
                } else {
                    System.out.println("有子节点不能删除");
                }
            } else {
                System.out.println("节点不存在");
            }
        }
    
        @After
        public void closeZk() throws Exception {
            zk.close();
    
        }
    
    }
    View Code

    (5)递归删除

    public class RmrNode {
        static ZooKeeper zk = null;
    
        static {
    
            try {
                String connectString = "doit01:2181,doit02:2181,doit03:2181";
                // 超时时间
                int sessionTimeout = 2000;
                // 获取zk的客户端对象
                zk = new ZooKeeper(connectString, sessionTimeout, null);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public static void main(String[] args) throws Exception {
            delete("/BIGDATA") ;
        }
    /**
     * 递归  函数
     *   
     * @param path
     * @throws Exception
     */
        public static  void delete(String path) throws Exception {
            // 获取路径的节点
            List<String> list = zk.getChildren(path, null);
            if(list.size()>0) {
                //  /user/user2/user3
                for (String cnode : list) {
                    // 调用自己
                    delete(path + "/" + cnode); //  递归删除子节点
                }
            }
            // 删除自己
            zk.delete(path, -1);
    
        }
    }
    View Code
  • 相关阅读:
    使用JS实现网页动态换肤
    数据库更新Sql脚本总结
    Javascript无刷新获取当前时间
    ASP.NET将网页设为桌面图标实现
    解决在IE浏览器中resize事件执行多次
    linux编译安装gcc5.3.0
    JAVA抽象类和接口
    JAVA内部类
    推测竞赛中测试集的正负比例
    Codeforces Round #742 (Div. 2) 题解
  • 原文地址:https://www.cnblogs.com/jj1106/p/11853410.html
Copyright © 2020-2023  润新知