Hdaoop的SequenceFile使用(学校实验)

感觉最后这个实验还是有点难度的,前前后后花了整整一个下午的时间,只给出实验思路和关键代码,仅供学习参考,千万不要直接抄袭啊。。。

实验题目

  • 本实验要求学生通过SequenceFile实现对多个小文件的封装。
    要求如下:
    • 使用随机数生成以(整数,字符串)为(key,Value)的文本文件,文件的大小内容任意,文件数量不少于100个;
    • 如果需要,可以选择以下代码生成随机文件。
  • 使用SequenceFile对以上文件进行封装,生成一个独立文件,压缩格式任意;
  • 实现以下的三种方式的查询:
    • 给出文件名,可以从序列文件整体读取文件并存储到指定的位置;
    • 给出某个整数的key,可以读取所有该key的数据,并给出所在文件的名称(可以输出到控制台)
    • 给出文件名和整数的key,可以读取该文件中的对应key的数据(可以输出到控制台)。

具体思路

  • 我们将这次的实验任务进行拆解,大概分为三个小模块。
  • (1)生成待处理的文件
    • 设计一段代码,要求可以生成至少100个小文件,同时,每个小文件里面需要有一点点内容。
    • 这个直接利用Java的相关文件IO函数即可。
package org.example;

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 生成100个文件,每个文件里面生成随机的数字
 */
public class RandomTxtFileCreator {
	public static void main(String[] args) {
		long start=System.currentTimeMillis();
		// 生成文件的个数
		int numFiles = 3;
		// 文件里面的数据的条数
		int numRecorders = 10;
		// 指定文件的生成路径
		String uri = "/home/lyp/backend-projects/demo/src/main/java/static";
		Random random = new Random();
		try {
			// 循环生成numFiles个文件
			for (int i = 1; i <= numFiles; i++) {
				System.out.println("writing file#"+i);
				// 指定当前生成的文件的路径
				FileOutputStream fileOutputStream = new FileOutputStream(uri + "/file" + i);
				// 将每个文件的生成的内容暂时存储到一个列表里面
				List<String> list = new ArrayList<>();
				for (int j = 0; j < numRecorders; j++){
					list.add(random.nextInt(numRecorders) + 1 + "\t" + "the recorder #" + j+ " in file#" + i);
				}
				PrintStream pStream = new PrintStream(new BufferedOutputStream(fileOutputStream));
				// 将这个list里面的所有的内容写到当前的这个文件里面
				for (String str : list) {
					pStream.println(str);
				}
				pStream.close();
				fileOutputStream.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			long end=System.currentTimeMillis();
			System.out.println("write "+numFiles+" files successfully in "+ (end-start)+"ms");
		}
	}
}

  • (2)使用SequenceFile对生成的文件进行处理
    • 这里我们需要先知道SequenceFile怎么使用。SequenceFile 使用参考以下博客
      • https://blog.csdn.net/bitcarmanlee/article/details/78111289
    • 通过这些博客我们知道了这个大概就是一个容器,可以用来高效地处理小文件。提供了一种哈希映射关系,注意这里的key是允许重复了。我们需要将我们生成的小文件压缩合并成一个文件。其实这个就和之前做的那些实验是类似的了。大概的过程就是我们一个一个读取某一个目录下面的文件,然后将这些文件都追加到hdfs的某一个文件里。
package org.example;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.ID;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;

public class MergeFile {
    public static void main(String[] args) throws IOException, URISyntaxException {
        Configuration conf = new Configuration();
        String uri = "hdfs://localhost:9000/user/lyp/gzfile";
        Path hdfsDir = new Path(uri);
        FileSystem hdfs = FileSystem.get(new URI(uri), conf);

        String localDir = "/home/lyp/backend-projects/demo/src/main/java/static/";
        File gzFileDir = new File(localDir);

        String[] gzFiles = gzFileDir.list();

        Text value = new Text();
        Text key = new Text();

        if (gzFiles == null) {
            return;
        }

        int fileLength = gzFiles.length;
        String line;
        SequenceFile.Writer writer = SequenceFile.createWriter(hdfs, conf, hdfsDir, Text.class, value.getClass());
        while(fileLength>0) {
            File gzFile = new File(localDir + gzFiles[fileLength - 1]);
            BufferedReader reader = new BufferedReader(new FileReader(gzFile.getPath()));


            StringBuilder content = new StringBuilder();

            while((line=reader.readLine())!= null) {
                // 对于第二种查询,key是文件里面前面的数字,value对应行的后面的内容
                String[] group = line.split("\t");
                key.set(group[0]);
                value.set(group[1]);
                writer.append(key, value);
                content.append(line).append("\n");

                // 对于第三种查询,key就是文件名+数字,value就是对应的后面的内容
                key.set(gzFile.getName() + group[0]);
                value.set(group[1]);
                writer.append(key,value);
            }

            // 对于第一种查询,key是文件的名字,value是文件的内容
            key.set(gzFile.getName());
            value.set(content.toString());
            writer.append(key, value);
            fileLength--;
        }
    }
}


  • (3)进行相关的查询操作
    • 首先,我们看到第一个查询,给出文件名,可以从序列文件整体读取文件并存储到指定的位置。大概的意思就是我们获取一个文件名,这个文件名是当时进行要所前的那些文件,如果有这些文件,将这些文件从压缩的文件里面单独读取到本地的某一个文件里面。
    • 对于第二种查询,就是给出一个key,我们需要找到这个key在哪些文件里面,同时输出这些key对应的value。如下图,所以这个key可能在一个文件里面,也可能在多个文件里面。
      Hdaoop的SequenceFile使用(学校实验)_第1张图片
    • 然后是第三种查询,我们发现,既需要文件名,又需要对应的整数key。
    • 所以,综上考虑,我们可以这样来构造我们的存储策略,key和value都是string类型的。这样,然后根据题目的要求构造对应的key处理即可。
package org.example;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Writable;
import org.apache.hadoop.util.ReflectionUtils;

import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import java.util.Scanner;

public class Query {
    static Scanner scanner = new Scanner(System.in);


    public static void main(String[] args) throws URISyntaxException, IOException {
        Configuration conf = new Configuration();
        String uri = "hdfs://localhost:9000/user/lyp/gzfile";
        Path hdfsDir = new Path(uri);
        FileSystem hdfs = FileSystem.get(new URI(uri), conf);
        String outputPath = "/home/lyp/backend-projects/demo/src/main/java/static/output";
        SequenceFile.Reader reader = new SequenceFile.Reader(hdfs, hdfsDir, conf);
        Writable key = (Writable) ReflectionUtils.newInstance(reader.getKeyClass(), conf);
        Writable value = (Writable) ReflectionUtils.newInstance(reader.getValueClass(), conf);
        while(true){
            System.out.println("请选择执行哪种查询操作:");
            System.out.println("1、给出文件名,可以从序列文件整体读取文件并存储到指定的位置");
            System.out.println("2、给出某个整数的key,可以读取所有该key的数据,并给出所在文件的名称(可以输出到控制台)");
            System.out.println("3、给出文件名和整数的key,可以读取该文件中的对应key的数据(可以输出到控制台)");
            System.out.println("输入exit退出整个程序");
            System.out.printf("%s", "请选择:");
            String choose = scanner.nextLine();
            switch (choose){
                case "1":
                    System.out.printf("%s: ", "请输入文件名");
                    String filename = scanner.nextLine();
                    FileOutputStream out = new FileOutputStream(outputPath);
                    PrintStream printStream = new PrintStream(new BufferedOutputStream(out));
                    while(reader.next(key,value)) {
                        String tempKey = key.toString();
                        String tempValue = value.toString();
                        if(Objects.equals(tempKey, filename)) {
                            printStream.println(tempValue);
                        }
                    }
                    printStream.close();
                    out.close();
                    IOUtils.closeStream(reader);
                    break;
                case "2":
                    System.out.printf("%s:", "请输入对应的key");
                    String queryKey = scanner.nextLine();
                    while(reader.next(key,value)){
                        String tempKey = key.toString();
                        String tempValue = value.toString();
                        if(Objects.equals(tempKey, queryKey)) {
                            System.out.println(tempValue);
                        }
                    }
                    break;
                case "3":
                    System.out.printf("%s: ", "输入查询的文件名");
                    String queryFilename = scanner.nextLine();
                    System.out.printf("%s: ", "请输入对应的整数key");
                    String queryK = scanner.nextLine();
                    String realKey = queryFilename + queryK;
                    while(reader.next(key, value)) {
                        String tempKey = key.toString();
                        String tempValue = value.toString();
                        if(Objects.equals(tempKey, realKey)){
                            System.out.println(tempValue);
                        }
                    }
                    break;
                case "exit":
                    return;
            }
        }
    }
}
  • 最后,就可以测试结果了。我在我的电脑测试基本是ok的,为了避免有人直接搬运,所以不贴截图了。

你可能感兴趣的:(学习日记,java,开发语言,hadoop,sequenceFile)