본문 바로가기
대학원 공부/programming language

Java : wordcount 코드 정리 및 class들 정리

by 월곡동로봇팔 2019. 10. 31.
Class Text

Text Class는 UTF-8 encoding을 이용하여 text를 저장한다. 이는 serialize, deserialize 하는 method를 제공하고, text들을 byte level 단위에서 비교한다. 

또한 byte array를 string으로 바꿈 없이 string으로 바로 바꿔주는 method를 제공한다.

또한 encoded string의 길이를 계산한다.

 

import java.io.IOException;
// String을 Token 단위로 나눈다.
import java.util.StringTokenizer;
// configuration, path
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
// IntWritable , Text
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
// job, mapper, reducer 부여 
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
// FileOutputFormat, FileInputFormat 은 입출력 압축에 사용할 코덱이다.
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

위는 class들을 import 한 목록을 말한다.

  • StringTokenizer는 String을 입력을 받으면 이를 Token 단위로 나눈다.
  • Configuration은 우리가 hadoop 설정에서 Configuration 최적화는 설정에 따라 최적화 시킬 때 쓴다.
  • Path는 실제로 terminal에서 실행할 때 어떻게 args를 써서 명령어 배치를 할 것인지를 보여준다.
  • IntWritable은 Int type을 serializaion, deserialization을 통해 data를 byte, byte를 data로 byte streaming을 도와줌.
  • Text는 txt 파일을 Text로 가져온다.
  • Job, Mapper, Reducer, 은 hadoop에 내장되어있는 class들로 각자 기능들을 하는 것을 불러온다.
  • FileInputFormat, FileOutputFormat은 이 class 들이 가지고 있는 형태로 input, output을 내보낸다.
public static class TokenizerMapper
       extends Mapper<Object, Text, Text, IntWritable>{
    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();
    // key/value로 스플릿한다.
    public void map(Object key, Text value, Context context
                    ) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString());
      while (itr.hasMoreTokens()) {
        word.set(itr.nextToken());
        context.write(word, one);
      // hasMoreTokens : string을 받을 때 스트링이면 True, 아니먄 False 그래서 while문 조건안에
      // nextToken : string tokenizer으로 부터 다음 token을 받는다.
      }
    }
  }
  • TokenizerMapper는 extend를 통해 Mapper라는 함수를 상속받음을 알려준다.
  • IntWritable은 1 로 지정을 해두었고, private final static 은 one의 멤버변수를 바뀌지 않게 지정한다.
  • Text class를 이용하여 word 라는 멤버변수를 만들었다. -> 나중에 여기에 실제 word 를 넣는다.
  • Context은 < it's the larger surrounding part which can have any influence on the behaviour of the current unit of work. E.g. the running environment used, the environment variables, instance variables, local variables, state of other classes, state of the current environment, etcetera. > stackoverflow 에서 말햇듯이 현재 work의 단위의 행동에 영향을 끼치는 주위 부분을 의미한다고는 하는데.... 이거 잘 모르겠다. 그냥 값을 중간에 남기니 확정 짓지 않고 중간에 넘어가는 값들을 context에 저장한다고 생각하면 되려나....ㅠㅠ ----> 이거 맞는 듯
  • value.toString()은 Text를 String으로 변환하는 것을 말한다.
  • StringTokenizer(String str, String delim)는 구분자를 이용해서 str을 구분해서 token으로 넘긴다. 여기서는 value가 Text이기 때문에 Text를 string으로 변환하였고, 단순히 구분자를 주지 않으면, 이는 그냥 나눠주는 것 같다.
  • while문에서 itr.hasMoreTokens는 String이 왔을 때, String이면 True, 아니면 False. 그래서 마지막 글자가 들어오면 False를 유추하여 반복문 빠져나온다.
  • word.set(itr.nextToken()) 은 itr의 nextToken을 이용하여 StringTokenizer로 부터 다음 토큰을 이어받아서 setting 한다.
  • 그 후 work 단위의 행동에 영향을 끼치는 context에게 word와 1로 지정한 one을 write 한다.
public static class IntSumReducer
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();
    public void reduce(Text key, Iterable<IntWritable> values,
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      // values를 val에 담고 for문 반복
      // val에 숫자를 바꾸기 위해 get, sum에 위에서 추가한 value를 하나 더해준다.
      for (IntWritable val: values) {
    	  sum += val.get();
      }
      // sum을 result에 더한다.
      result.set(sum);
      // context에 keu result를 나란히 적어준다.
      context.write(key, result);
    }
  }
  • result를 serialization 해야하니 그냥 int가 아니라 Writable로써 빈 값을 넣어준다.
  • reduce는 key에 따라서 Iterable인 IntWritable 이니 values를 저렇게 선언해준다.
  • IntWritable인 val 은 sum을 += val.get()으로 누적해서 더해준다.
  • result를 sum으로 set 해준다.
  • 이를 key, result 로 context에 적어준다.
public static void main(String[] args) throws Exception {
	// 밑에 job을 생성하려면 configuration 구조를 만들어줘야한다.
    Configuration conf = new Configuration();
    // new job을 만들기 getInstance는 conf와 job의 string을 파라미터로 받는다.
    Job job = Job.getInstance(conf, "word count");
    // ()입력하는 파라미터 jar의 클래스를 불러온다.
    // job class 생성
    job.setJarByClass(WordCount.class);
    // 파라미터의 이름을 가진 클래스(이 파일을 돌리면 만들어짐), 사용할 Mapper를 job에 구현
    job.setMapperClass(TokenizerMapper.class);
    // 파라미터의 이름을 가진 클래스(이 파일을 돌리면 만들어짐), 사용할 Reducer를 job에 구현
    job.setReducerClass(IntSumReducer.class);
    // output data 잡으로 부터 key class를 파라미터로 구현
    // IntSumReducer에서 Text key 라고 해서 Text.class (이미 import해서 따로 구현x)
    job.setOutputKeyClass(Text.class);
    // output data 잡으로부터 value class를 파라미터로 구현
    // IntSumReducer에서 InWritable (이미 import해서 따로 구현x)
    job.setOutputValueClass(IntWritable.class);
    /*bin/hadoop jar wc.jar WordCount : jar 중 wc.jar 에서 class 이름 WordCount를 선택
     * /user/hadoop/wordcount/input: Path(args[0]) /user/hadoop/wordcount/output: Path(args[1]) */
    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));
//  완료되면 멈춘다.
    System.exit(job.waitForCompletion(true) ? 0 : 1);
  }
  • Configuration을 써서 conf를 멤버변수로 만들어준다. (Hadoop에서 config 한대로 !!!)
  • Job class에서 getInstance로 job을 멤버변수로 생성하면서 config 한 conf와 "word count"라고 job의 이름을 지정.
  • setJarByClass(WordCount.class) 로 job 안에 Jar 을 WordCount.class로 지정해준다.
  • setMapperClass(TokenizerMapper) 로 job 안에 Mapper를 TokenizerMapper로 넣어준다.
  • setReducerClass(IntSumReducer) 로 job 안에 Reducer를 IntSumReducer로 넣어준다.
  • setOutputKeyClass(Text.class) 로 OutputKeyClass는 Output의 key의 type인 Text로 준다.(Reducer에 존재)
  • setOutputValueClass(IntWritable) 로 OutputValueClass는 Output의 Value의 type인 Intwritable로 준다. 이는Reducer에서 result를 Intwritable이라고 선언하였기 때문이다.

댓글