하둡과 몽고DB 연결하고 맵리듀스 결과 몽고DB에 적재하기

~일간~ 두더지 탈출기 (부제. 삽질 멈춰!…)
Seoul Emergency 프로젝트를 진행하며 발생한 에러를 정리합니다.

의존성 설정(이 99%)

아래 블로그 설명 따라 2가지 라이브러리를 추가한다.

[Hadoop] 하둡과 MongoDB 연동하는 코드

hadoop 연결에 대한 mongoDB 공식 문서

MapReduce Usage · mongodb/mongo-hadoop Wiki

문제의 시작..(벌써?)

일반적으로 maven 이나 gradle로 빌드하면 위에서 요구하는 2가지 라이브러리를 쉽게 추가할 수 있는데,,,(pom.xml에 태그로 설정하거나 build.gradle에서 추가하면 됨)

현재 갖고있는 map reduce 예제 코드는 ant 방식으로 build 한다. 참고로 ant 방식의 빌드는 현재 많이 사용되지 않아 정보를 찾기 힘들다…

또한 build.xml 파일에서 클래스 패스를 설정하여 dependency를 추가하는 태그가 없다. build.xml에서 dependency 태그를 추가하려면 ivy라는 프레임워크를 사용해야 한다?!!

How to add dependency to Ant project

How to add dependency to Ant project - Stack Overflow

jar추가를 위해 또 다른 jar를 추가하는 과정이 너무 번거로우며…(~귀찮아서가 아니라 6년전 글이라는 경고가 너무 무섭잖아요…~) 향후에는 애플리케이션 백엔드 프로젝트를 Spring Boot에서 생성한 Gradle 빌드 방식으로 변경할 것이므로 ivy 프레임워크는 사용하지 않았다.

대신 임시 방편으로 의존성 추가를 위해 jar파일을 직접 다운받아 클래스 패스에 넣어주는 방법을 선택했다.

Jar 파일 다운로드

클래스 경로에 Jar 파일 넣어주기

build.xml을 참고하여 다음 디렉토리에 jar 파일을 옮긴다.

/usr/local/hadoop/hadoop-3.2.2/share/hadoop/common/lib

build.xml

추가적으로 아래 디렉토리에도 jar 파일이 존재해야 빌드 및 실행 오류가 발생하지 않음!

/usr/local/hadoop/share/hadoop/common/lib

추측컨데 하둡을 위한 클래스 파일들이 모아져있는 경로와 프로젝트 클래스 파일들이 모아져 있는 경로 2가지 모두에 넣어줘야 한다고 이해했는데… 확실하지 않아 더 찾아볼 예정.

class path

진짜 찐 최종 Jar 파일 하나만 더 추가하기

지독하게 뭔가 또 추가해달라고 에러를 뱉네요..

jar error

StringUtils 를 설치해주러 갑시다.

commons-lang-2.6.jar

commons-lang-2.6.jar

마찬가지로

/usr/local/hadoop/hadoop-3.2.2/share/hadoop/common/lib

/usr/local/hadoop/share/hadoop/common/lib

두개의 클래스 경로에 넣어준다.

commons-lang jar

맵 리듀스 결과 몽고DB에 적재하기

맨 위에서 참고한 블로그는 input 자체를 mongoDB에서 받아오지만 우리 프로젝트의 경우 파이썬으로 형태소 분석한 파일을 hdfs에 넣은 뒤 input은 hdfs로, output만 mongoDB로 설정해야 한다. 따라서 코드를 다음과 같이 수정해야 한다.

main() 함수 설정 변경하기

public static void main(String[] args) throws Exception {
		Configuration conf = new Configuration();
		
		MongoConfigUtil.setOutputURI(conf, "mongodb://<mongoDB IP address>/<database name>.<collection name>"); 
		
		Job job = new Job(conf,"word count");
		job.setJarByClass(Wordcountsort.class);

		// Custom Partition
		job.setPartitionerClass(MyPartitioner.class);

		// let hadoop know my map and reduce classes
		job.setMapperClass(TokenizerMapper.class);
		job.setReducerClass(IntSumReducer.class);
		
		job.setMapOutputKeyClass(Text.class);
		job.setMapOutputValueClass(IntWritable.class);

		job.setOutputKeyClass(Text.class);
		job.setOutputValueClass(BSONWritable.class);

		// set number of reduces
		job.setNumReduceTasks(2);

		// set input and output directories
		FileInputFormat.addInputPath(job,new Path("test_news_1_nouns"));
		job.setOutputFormatClass(MongoOutputFormat.class);
		System.exit(job.waitForCompletion(true) ? 0 : 1 );
}
  • MongoConfigUtil.setOutputURI(conf, "mongodb://<mongoDB IP address>/<database name>.<collection name>"); : 결과물을 저장할 mongoDB를 설정
  • job.setOutputValueClass(BSONWritable.class); : 결과물 value의 타입 BSONWritable 로 설정
  • FileInputFormat.addInputPath(job,new Path("test_news_1_nouns")); : 실행 시 main() 함수 매개변수로 input 파일 경로 설정하지 않고 코드에 직접 설정
  • job.setOutputFormatClass(MongoOutputFormat.class); : 결과물은 MongoOutputFormat 에 저장

reduce() 함수 반환형 수정하기

public static class IntSumReducer
			extends Reducer<Text,IntWritable,Integer,BSONWritable> {

			// variables
			private IntWritable result = new IntWritable();
			private int k = 1;
	
			// key : a disticnt word
			// values :  Iterable type (data list)
			public void reduce(Text key, Iterable<IntWritable> values, Context context) 
					throws IOException, InterruptedException {
					
					BasicBSONObject output = new BasicBSONObject();
					BSONWritable reduceResult = new BSONWritable();
		
					int sum = 0;
					for ( IntWritable val : values ) {
							sum += val.get();
					}
					output.put(key.toString(), Integer.toString(sum));
					reduceResult.setDoc(output);
					context.write(k++, reduceResult);
			}
}
  • extends Reducer<Text,IntWritable,Integer,BSONWritable> : 반환의 value 타입을 BSONWritable 로 설정
  • BasicBSONObject output = new BasicBSONObject(); : 결과물은 mongoDB에 저장될 key-value 형태의 BSON 타입 객체
  • output.put(key.toString(), Integer.toString(sum)); : BSON 타입의 key 로 word를, value로 count를 설정
  • BSONWritable reduceResult = new BSONWritable(); : mongoDB에 BSON 타입 객체로 document를 추가해 나가기 위한 설정
  • reduceResult.setDoc(output); : mongoDB에 결과물 document로 추가해 나감
  • context.write(k++, reduceResult); : key 는 pk 역할을 하는 id, value는 BSONWritable 객체

실행

실행 시 입력 매개변수 받지 않고 직접 main() 함수 내부에 input 파일 설정해주었으므로 다음과 같은 명령어로 실행한다.

hadoop jar ssafy.jar <Driver.java 에 선언한 클래스 alias>

MongoDB에서 적재 결과물 확인하기

MongoDB Compass

야호~~~!!!!!

I'm fine...