From 9c82a00b1984a60111527a8f25d035f3f22b8cf4 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:02:44 +0000 Subject: [PATCH 01/16] Setting up GitHub Classroom Feedback From 402d33fef86e8fc890b80d10e4f052ba1dbb8fcf Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:02:47 +0000 Subject: [PATCH 02/16] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 948dc85..b8e38ee 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/-hH64FG6) ## Лабораторная работа: Реализация MapReduce для анализа данных о продажах с ипользованием HADOOP!!! # Цель работы From 22845f4f446a5c72926828decef41f62dd3dbae3 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Tue, 17 Dec 2024 19:53:07 +0300 Subject: [PATCH 03/16] init --- .gitignore | 2 + 0.csv | 3 - 1.csv | 3 - 2.csv | 3 - 3.csv | 3 - 4.csv | 3 - 5.csv | 3 - 6.csv | 3 - 7.csv | 3 - build.gradle.kts | 26 ++ docker-compose.yml | 59 +++++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++++++ gradlew.bat | 92 +++++++ settings.gradle.kts | 1 + .../lab4/CustomCompositeKeyComparator.java | 11 + src/main/java/org/itmo/lab4/CustomMapper.java | 22 ++ .../CustomNaturalKeyGroupingComparator.java | 11 + .../lab4/CustomNaturalKeyPartitioner.java | 13 + .../java/org/itmo/lab4/CustomReducer.java | 22 ++ src/main/java/org/itmo/lab4/Main.java | 33 +++ 22 files changed, 548 insertions(+), 24 deletions(-) create mode 100644 .gitignore delete mode 100644 0.csv delete mode 100644 1.csv delete mode 100644 2.csv delete mode 100644 3.csv delete mode 100644 4.csv delete mode 100644 5.csv delete mode 100644 6.csv delete mode 100644 7.csv create mode 100644 build.gradle.kts create mode 100644 docker-compose.yml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts create mode 100644 src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java create mode 100644 src/main/java/org/itmo/lab4/CustomMapper.java create mode 100644 src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java create mode 100644 src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java create mode 100644 src/main/java/org/itmo/lab4/CustomReducer.java create mode 100644 src/main/java/org/itmo/lab4/Main.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bffa164 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.gradle +.idea diff --git a/0.csv b/0.csv deleted file mode 100644 index 2f7dfb7..0000000 --- a/0.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:eae61feac01cb36de9d6dd148a5b3fb76ad506fd8b283b20306df16f246e511e -size 3406784 diff --git a/1.csv b/1.csv deleted file mode 100644 index 3a258b0..0000000 --- a/1.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7b2681409003d07d3ca063eb34339a0cdea9b688d16723b0e284091afd6bf806 -size 7078520 diff --git a/2.csv b/2.csv deleted file mode 100644 index 4cdd100..0000000 --- a/2.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea9a50aa300dfe7e766eb419b2a963d31cfe68644acb23f654df7abe852d3a76 -size 10737171 diff --git a/3.csv b/3.csv deleted file mode 100644 index afbe78d..0000000 --- a/3.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6268fae7b50879a151a6f6de4852e4f39d2e22a8315290e87dcda71f4e10b866 -size 14530705 diff --git a/4.csv b/4.csv deleted file mode 100644 index 9ff08df..0000000 --- a/4.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b4adf0d42097f1b2c9e68731460c5a7c52cb7ac7238addab7a796817cee9d00b -size 18299520 diff --git a/5.csv b/5.csv deleted file mode 100644 index 3980291..0000000 --- a/5.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:212b6d6b07197921eaedaa271d781431b8bb034c5328622d63231a2967df1702 -size 22053240 diff --git a/6.csv b/6.csv deleted file mode 100644 index 5906dc6..0000000 --- a/6.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81f65085c6ce29a8f766244dc4b21d41d565ea3d6231b3b1c0b6739d67cd1d53 -size 25790880 diff --git a/7.csv b/7.csv deleted file mode 100644 index df43af3..0000000 --- a/7.csv +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4e73ff71a37438ec216fc522d77e3902c5670e24a917d0be047ed101dbeea914 -size 29524261 diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..787cf25 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + java +} + +group = "org.itmo" +version = "0.0.1-SNAPSHOT" + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(8) + } +} + + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.apache.hadoop:hadoop-common:3.2.1") + implementation("org.apache.hadoop:hadoop-client:3.2.1") +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..77c43fe --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +volumes: + hadoop_namenode: + hadoop_datanode: + hadoop_historyserver: + +services: + namenode: + image: bde2020/hadoop-namenode:2.0.0-hadoop3.2.1-java8 + container_name: namenode + restart: always + ports: + - "9870:9870" + - "9000:9000" + volumes: + - hadoop_namenode:/hadoop/dfs/name + environment: + - CLUSTER_NAME=test + env_file: + - ./hadoop.env + + datanode: + image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1-java8 + container_name: datanode + restart: always + volumes: + - hadoop_datanode:/hadoop/dfs/data + environment: + SERVICE_PRECONDITION: "namenode:9870" + env_file: + - ./hadoop.env + + resourcemanager: + image: bde2020/hadoop-resourcemanager:2.0.0-hadoop3.2.1-java8 + container_name: resourcemanager + restart: always + environment: + SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864" + env_file: + - ./hadoop.env + + nodemanager1: + image: bde2020/hadoop-nodemanager:2.0.0-hadoop3.2.1-java8 + container_name: nodemanager + restart: always + environment: + SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088" + env_file: + - ./hadoop.env + + historyserver: + image: bde2020/hadoop-historyserver:2.0.0-hadoop3.2.1-java8 + container_name: historyserver + restart: always + environment: + SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088" + volumes: + - hadoop_historyserver:/hadoop/yarn/timeline + env_file: + - ./hadoop.env diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..d64cd4917707c1f8861d8cb53dd15194d4248596 GIT binary patch literal 43462 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1af9e09 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..1aa94a4 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..93e3f59 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..b882dba --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "lab4-parcalc-bromles" diff --git a/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java b/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java new file mode 100644 index 0000000..7339e91 --- /dev/null +++ b/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java @@ -0,0 +1,11 @@ +package org.itmo.lab4; + +import org.apache.hadoop.io.WritableComparable; +import org.apache.hadoop.io.WritableComparator; + +public class CustomCompositeKeyComparator extends WritableComparator { + @Override + public int compare(WritableComparable w1, WritableComparable w2) { + return w1.compareTo(w2); + } +} diff --git a/src/main/java/org/itmo/lab4/CustomMapper.java b/src/main/java/org/itmo/lab4/CustomMapper.java new file mode 100644 index 0000000..c8c8cac --- /dev/null +++ b/src/main/java/org/itmo/lab4/CustomMapper.java @@ -0,0 +1,22 @@ +package org.itmo.lab4; + +import org.apache.hadoop.io.IntWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Mapper; + +import java.io.IOException; +import java.util.StringTokenizer; + +public class CustomMapper extends Mapper { + private final static IntWritable one = new IntWritable(1); + private final Text word = new Text(); + + 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); + } + } +} diff --git a/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java b/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java new file mode 100644 index 0000000..23c9a60 --- /dev/null +++ b/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java @@ -0,0 +1,11 @@ +package org.itmo.lab4; + +import org.apache.hadoop.io.WritableComparable; +import org.apache.hadoop.io.WritableComparator; + +public class CustomNaturalKeyGroupingComparator extends WritableComparator { + @Override + public int compare(WritableComparable w1, WritableComparable w2) { + return w1.compareTo(w2); + } +} diff --git a/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java b/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java new file mode 100644 index 0000000..c098aa3 --- /dev/null +++ b/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java @@ -0,0 +1,13 @@ +package org.itmo.lab4; + +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Partitioner; + +public class CustomNaturalKeyPartitioner extends Partitioner { + @Override + public int getPartition(Text key, DoubleWritable val, int numPartitions) { + int hash = key.hashCode(); + return hash % numPartitions; + } +} diff --git a/src/main/java/org/itmo/lab4/CustomReducer.java b/src/main/java/org/itmo/lab4/CustomReducer.java new file mode 100644 index 0000000..d47e9f8 --- /dev/null +++ b/src/main/java/org/itmo/lab4/CustomReducer.java @@ -0,0 +1,22 @@ +package org.itmo.lab4; + +import org.apache.hadoop.io.IntWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Reducer; + +import java.io.IOException; + +public class CustomReducer extends Reducer { + private final IntWritable result = new IntWritable(); + + public void reduce(Text key, Iterable values, + Context context + ) throws IOException, InterruptedException { + int sum = 0; + for (IntWritable val : values) { + sum += val.get(); + } + result.set(sum); + context.write(key, result); + } +} diff --git a/src/main/java/org/itmo/lab4/Main.java b/src/main/java/org/itmo/lab4/Main.java new file mode 100644 index 0000000..b772826 --- /dev/null +++ b/src/main/java/org/itmo/lab4/Main.java @@ -0,0 +1,33 @@ +package org.itmo.lab4; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.IntWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Job; +import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; +import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; + +public class Main { + public static void main(String[] args) throws Exception { + Configuration configuration = new Configuration(); + + Job job = Job.getInstance(configuration, "lab4-bromles"); + + job.setJarByClass(Main.class); + job.setPartitionerClass(CustomNaturalKeyPartitioner.class); + job.setGroupingComparatorClass(CustomNaturalKeyGroupingComparator.class); + job.setSortComparatorClass(CustomCompositeKeyComparator.class); + + job.setOutputKeyClass(Text.class); + job.setOutputValueClass(IntWritable.class); + + job.setMapperClass(CustomMapper.class); + job.setReducerClass(CustomReducer.class); + + FileInputFormat.addInputPath(job, new Path(args[0])); + FileOutputFormat.setOutputPath(job, new Path(args[1])); + + job.waitForCompletion(true); + } +} From c30890e2cc98fccac788e45eab4c98a009db2476 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Tue, 17 Dec 2024 19:59:36 +0300 Subject: [PATCH 04/16] add missing env file --- hadoop.env | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 hadoop.env diff --git a/hadoop.env b/hadoop.env new file mode 100644 index 0000000..95b3d10 --- /dev/null +++ b/hadoop.env @@ -0,0 +1,43 @@ +CORE_CONF_fs_defaultFS=hdfs://namenode:9000 +CORE_CONF_hadoop_http_staticuser_user=root +CORE_CONF_hadoop_proxyuser_hue_hosts=* +CORE_CONF_hadoop_proxyuser_hue_groups=* +CORE_CONF_io_compression_codecs=org.apache.hadoop.io.compress.SnappyCodec + +HDFS_CONF_dfs_webhdfs_enabled=true +HDFS_CONF_dfs_permissions_enabled=false +HDFS_CONF_dfs_namenode_datanode_registration_ip___hostname___check=false + +YARN_CONF_yarn_log___aggregation___enable=true +YARN_CONF_yarn_log_server_url=http://historyserver:8188/applicationhistory/logs/ +YARN_CONF_yarn_resourcemanager_recovery_enabled=true +YARN_CONF_yarn_resourcemanager_store_class=org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore +YARN_CONF_yarn_resourcemanager_scheduler_class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler +YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___mb=8192 +YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___vcores=4 +YARN_CONF_yarn_resourcemanager_fs_state___store_uri=/rmstate +YARN_CONF_yarn_resourcemanager_system___metrics___publisher_enabled=true +YARN_CONF_yarn_resourcemanager_hostname=resourcemanager +YARN_CONF_yarn_resourcemanager_address=resourcemanager:8032 +YARN_CONF_yarn_resourcemanager_scheduler_address=resourcemanager:8030 +YARN_CONF_yarn_resourcemanager_resource__tracker_address=resourcemanager:8031 +YARN_CONF_yarn_timeline___service_enabled=true +YARN_CONF_yarn_timeline___service_generic___application___history_enabled=true +YARN_CONF_yarn_timeline___service_hostname=historyserver +YARN_CONF_mapreduce_map_output_compress=true +YARN_CONF_mapred_map_output_compress_codec=org.apache.hadoop.io.compress.SnappyCodec +YARN_CONF_yarn_nodemanager_resource_memory___mb=16384 +YARN_CONF_yarn_nodemanager_resource_cpu___vcores=8 +YARN_CONF_yarn_nodemanager_disk___health___checker_max___disk___utilization___per___disk___percentage=98.5 +YARN_CONF_yarn_nodemanager_remote___app___log___dir=/app-logs +YARN_CONF_yarn_nodemanager_aux___services=mapreduce_shuffle + +MAPRED_CONF_mapreduce_framework_name=yarn +MAPRED_CONF_mapred_child_java_opts=-Xmx4096m +MAPRED_CONF_mapreduce_map_memory_mb=4096 +MAPRED_CONF_mapreduce_reduce_memory_mb=8192 +MAPRED_CONF_mapreduce_map_java_opts=-Xmx3072m +MAPRED_CONF_mapreduce_reduce_java_opts=-Xmx6144m +MAPRED_CONF_yarn_app_mapreduce_am_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ +MAPRED_CONF_mapreduce_map_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ +MAPRED_CONF_mapreduce_reduce_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ From f4129e61e97e0373884ab4056b64784c3ed8687d Mon Sep 17 00:00:00 2001 From: aushakovvi Date: Tue, 17 Dec 2024 20:09:27 +0300 Subject: [PATCH 05/16] update gitignore --- .gitignore | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bffa164..123859e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ -.gradle -.idea +/.gradle +/.idea +/build From 4b39cb60474506455d87f11527a68e898a6eacc7 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Tue, 17 Dec 2024 22:56:33 +0300 Subject: [PATCH 06/16] split jobs --- build.gradle.kts | 8 +++- .../lab4/CustomCompositeKeyComparator.java | 11 ----- src/main/java/org/itmo/lab4/CustomMapper.java | 22 --------- .../CustomNaturalKeyGroupingComparator.java | 11 ----- .../lab4/CustomNaturalKeyPartitioner.java | 13 ----- .../java/org/itmo/lab4/CustomReducer.java | 22 --------- src/main/java/org/itmo/lab4/Main.java | 33 ------------- .../java/org/itmo/lab4/SalesAnalyzer.java | 44 +++++++++++++++++ .../org/itmo/lab4/analyse/AnalysisJob.java | 47 ++++++++++++++++++ .../org/itmo/lab4/analyse/AnalysisMapper.java | 26 ++++++++++ .../itmo/lab4/analyse/AnalysisReducer.java | 21 ++++++++ .../java/org/itmo/lab4/analyse/SalesData.java | 29 +++++++++++ .../org/itmo/lab4/sort/CompositeData.java | 30 ++++++++++++ .../java/org/itmo/lab4/sort/SortingJob.java | 48 +++++++++++++++++++ .../org/itmo/lab4/sort/SortingMapper.java | 27 +++++++++++ .../org/itmo/lab4/sort/SortingReducer.java | 18 +++++++ 16 files changed, 297 insertions(+), 113 deletions(-) delete mode 100644 src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java delete mode 100644 src/main/java/org/itmo/lab4/CustomMapper.java delete mode 100644 src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java delete mode 100644 src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java delete mode 100644 src/main/java/org/itmo/lab4/CustomReducer.java delete mode 100644 src/main/java/org/itmo/lab4/Main.java create mode 100644 src/main/java/org/itmo/lab4/SalesAnalyzer.java create mode 100644 src/main/java/org/itmo/lab4/analyse/AnalysisJob.java create mode 100644 src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java create mode 100644 src/main/java/org/itmo/lab4/analyse/AnalysisReducer.java create mode 100644 src/main/java/org/itmo/lab4/analyse/SalesData.java create mode 100644 src/main/java/org/itmo/lab4/sort/CompositeData.java create mode 100644 src/main/java/org/itmo/lab4/sort/SortingJob.java create mode 100644 src/main/java/org/itmo/lab4/sort/SortingMapper.java create mode 100644 src/main/java/org/itmo/lab4/sort/SortingReducer.java diff --git a/build.gradle.kts b/build.gradle.kts index 787cf25..515ca23 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,7 +3,7 @@ plugins { } group = "org.itmo" -version = "0.0.1-SNAPSHOT" +version = "0.0.1" java { toolchain { @@ -19,6 +19,12 @@ repositories { dependencies { implementation("org.apache.hadoop:hadoop-common:3.2.1") implementation("org.apache.hadoop:hadoop-client:3.2.1") + + compileOnly("org.projectlombok:lombok:1.18.36") + annotationProcessor("org.projectlombok:lombok:1.18.36") + + testCompileOnly("org.projectlombok:lombok:1.18.36") + testAnnotationProcessor("org.projectlombok:lombok:1.18.36") } tasks.withType { diff --git a/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java b/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java deleted file mode 100644 index 7339e91..0000000 --- a/src/main/java/org/itmo/lab4/CustomCompositeKeyComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.io.WritableComparable; -import org.apache.hadoop.io.WritableComparator; - -public class CustomCompositeKeyComparator extends WritableComparator { - @Override - public int compare(WritableComparable w1, WritableComparable w2) { - return w1.compareTo(w2); - } -} diff --git a/src/main/java/org/itmo/lab4/CustomMapper.java b/src/main/java/org/itmo/lab4/CustomMapper.java deleted file mode 100644 index c8c8cac..0000000 --- a/src/main/java/org/itmo/lab4/CustomMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.io.IntWritable; -import org.apache.hadoop.io.Text; -import org.apache.hadoop.mapreduce.Mapper; - -import java.io.IOException; -import java.util.StringTokenizer; - -public class CustomMapper extends Mapper { - private final static IntWritable one = new IntWritable(1); - private final Text word = new Text(); - - 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); - } - } -} diff --git a/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java b/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java deleted file mode 100644 index 23c9a60..0000000 --- a/src/main/java/org/itmo/lab4/CustomNaturalKeyGroupingComparator.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.io.WritableComparable; -import org.apache.hadoop.io.WritableComparator; - -public class CustomNaturalKeyGroupingComparator extends WritableComparator { - @Override - public int compare(WritableComparable w1, WritableComparable w2) { - return w1.compareTo(w2); - } -} diff --git a/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java b/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java deleted file mode 100644 index c098aa3..0000000 --- a/src/main/java/org/itmo/lab4/CustomNaturalKeyPartitioner.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.io.DoubleWritable; -import org.apache.hadoop.io.Text; -import org.apache.hadoop.mapreduce.Partitioner; - -public class CustomNaturalKeyPartitioner extends Partitioner { - @Override - public int getPartition(Text key, DoubleWritable val, int numPartitions) { - int hash = key.hashCode(); - return hash % numPartitions; - } -} diff --git a/src/main/java/org/itmo/lab4/CustomReducer.java b/src/main/java/org/itmo/lab4/CustomReducer.java deleted file mode 100644 index d47e9f8..0000000 --- a/src/main/java/org/itmo/lab4/CustomReducer.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.io.IntWritable; -import org.apache.hadoop.io.Text; -import org.apache.hadoop.mapreduce.Reducer; - -import java.io.IOException; - -public class CustomReducer extends Reducer { - private final IntWritable result = new IntWritable(); - - public void reduce(Text key, Iterable values, - Context context - ) throws IOException, InterruptedException { - int sum = 0; - for (IntWritable val : values) { - sum += val.get(); - } - result.set(sum); - context.write(key, result); - } -} diff --git a/src/main/java/org/itmo/lab4/Main.java b/src/main/java/org/itmo/lab4/Main.java deleted file mode 100644 index b772826..0000000 --- a/src/main/java/org/itmo/lab4/Main.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.itmo.lab4; - -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.Path; -import org.apache.hadoop.io.IntWritable; -import org.apache.hadoop.io.Text; -import org.apache.hadoop.mapreduce.Job; -import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; -import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; - -public class Main { - public static void main(String[] args) throws Exception { - Configuration configuration = new Configuration(); - - Job job = Job.getInstance(configuration, "lab4-bromles"); - - job.setJarByClass(Main.class); - job.setPartitionerClass(CustomNaturalKeyPartitioner.class); - job.setGroupingComparatorClass(CustomNaturalKeyGroupingComparator.class); - job.setSortComparatorClass(CustomCompositeKeyComparator.class); - - job.setOutputKeyClass(Text.class); - job.setOutputValueClass(IntWritable.class); - - job.setMapperClass(CustomMapper.class); - job.setReducerClass(CustomReducer.class); - - FileInputFormat.addInputPath(job, new Path(args[0])); - FileOutputFormat.setOutputPath(job, new Path(args[1])); - - job.waitForCompletion(true); - } -} diff --git a/src/main/java/org/itmo/lab4/SalesAnalyzer.java b/src/main/java/org/itmo/lab4/SalesAnalyzer.java new file mode 100644 index 0000000..30438bb --- /dev/null +++ b/src/main/java/org/itmo/lab4/SalesAnalyzer.java @@ -0,0 +1,44 @@ +package org.itmo.lab4; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.util.ToolRunner; +import org.itmo.lab4.analyse.AnalysisJob; +import org.itmo.lab4.sort.SortingJob; + +public class SalesAnalyzer { + public static void main(String[] args) throws Exception { + if (args.length != 4) { + System.err.println("Usage: hadoop jar /tmp/lab4-parcalc-bromles-0.0.1.jar org.itmo.lab4.SalesAnalyzer "); + System.exit(-1); + } + + String inputDir = args[0]; + String outputDir = args[1]; + int reducerCount = Integer.parseInt(args[2]); + int datablockSizeKb = Integer.parseInt(args[3]) * ((int) Math.pow(2, 10)); // * 1 kb + String intermediateResultDir = outputDir + "-intermediate"; + + long startTime = System.currentTimeMillis(); + Configuration configuration = new Configuration(); + configuration.set("mapreduce.input.fileinputformat.split.maxsize", Integer.toString(datablockSizeKb)); + + String[] analysisArgs = new String[]{inputDir, intermediateResultDir, String.valueOf(reducerCount)}; + + // Data analysis + int exitCode = ToolRunner.run(configuration, new AnalysisJob(), analysisArgs); + + if (exitCode != 0) { + System.exit(1); + } + + String[] sortingArgs = new String[]{intermediateResultDir, outputDir, String.valueOf(reducerCount)}; + + // Data sorting + exitCode = ToolRunner.run(configuration, new SortingJob(), sortingArgs); + + long endTime = System.currentTimeMillis(); + System.out.println("Jobs completed in " + (endTime - startTime) + " milliseconds."); + + System.exit(exitCode); + } +} diff --git a/src/main/java/org/itmo/lab4/analyse/AnalysisJob.java b/src/main/java/org/itmo/lab4/analyse/AnalysisJob.java new file mode 100644 index 0000000..05bc061 --- /dev/null +++ b/src/main/java/org/itmo/lab4/analyse/AnalysisJob.java @@ -0,0 +1,47 @@ +package org.itmo.lab4.analyse; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Job; +import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; +import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; +import org.apache.hadoop.util.Tool; + +public class AnalysisJob extends Configured implements Tool { + @Override + public int run(String[] args) throws Exception { + String inputDir = args[0]; + String outputDir = args[1]; + int reducerCount = Integer.parseInt(args[2]); + + Configuration configuration = getConf(); + + Job analysisJob = Job.getInstance(configuration, "analysis"); + + analysisJob.setNumReduceTasks(reducerCount); // reducer scaling + + analysisJob.setJarByClass(AnalysisJob.class); + + analysisJob.setMapperClass(AnalysisMapper.class); + analysisJob.setReducerClass(AnalysisReducer.class); + + analysisJob.setMapOutputKeyClass(Text.class); + analysisJob.setMapOutputValueClass(SalesData.class); + + analysisJob.setOutputKeyClass(Text.class); + analysisJob.setOutputValueClass(Text.class); + + FileInputFormat.addInputPath(analysisJob, new Path(inputDir)); + FileOutputFormat.setOutputPath(analysisJob, new Path(outputDir)); + + boolean success = analysisJob.waitForCompletion(true); + + if (!success) { + return 1; + } + + return 0; + } +} diff --git a/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java b/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java new file mode 100644 index 0000000..fec97b3 --- /dev/null +++ b/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java @@ -0,0 +1,26 @@ +package org.itmo.lab4.analyse; + +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Mapper; + +import java.io.IOException; + +public class AnalysisMapper extends Mapper { + private final Text categoryKey = new Text(); + + @Override + protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { + String[] fields = value.toString().split(","); + + if (fields.length == 5 && !fields[0].equals("transaction_id")) { + String category = fields[2]; + + double price = Double.parseDouble(fields[3]); + int quantity = Integer.parseInt(fields[4]); + + categoryKey.set(category); + + context.write(categoryKey, new SalesData(price * quantity, quantity)); + } + } +} diff --git a/src/main/java/org/itmo/lab4/analyse/AnalysisReducer.java b/src/main/java/org/itmo/lab4/analyse/AnalysisReducer.java new file mode 100644 index 0000000..4a58385 --- /dev/null +++ b/src/main/java/org/itmo/lab4/analyse/AnalysisReducer.java @@ -0,0 +1,21 @@ +package org.itmo.lab4.analyse; + +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Reducer; + +import java.io.IOException; + +public class AnalysisReducer extends Reducer { + @Override + protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { + double totalRevenue = 0.0; + int totalQuantity = 0; + + for (SalesData val : values) { + totalRevenue += val.getRevenue(); + totalQuantity += val.getQuantity(); + } + + context.write(key, new Text(String.format("%.2f\t%d", totalRevenue, totalQuantity))); + } +} diff --git a/src/main/java/org/itmo/lab4/analyse/SalesData.java b/src/main/java/org/itmo/lab4/analyse/SalesData.java new file mode 100644 index 0000000..2f748f9 --- /dev/null +++ b/src/main/java/org/itmo/lab4/analyse/SalesData.java @@ -0,0 +1,29 @@ +package org.itmo.lab4.analyse; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.hadoop.io.Writable; + +import java.io.DataOutput; +import java.io.IOException; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class SalesData implements Writable { + private double revenue; + private int quantity; + + @Override + public void write(DataOutput out) throws IOException { + out.writeDouble(revenue); + out.writeInt(quantity); + } + + @Override + public void readFields(java.io.DataInput in) throws IOException { + revenue = in.readDouble(); + quantity = in.readInt(); + } +} diff --git a/src/main/java/org/itmo/lab4/sort/CompositeData.java b/src/main/java/org/itmo/lab4/sort/CompositeData.java new file mode 100644 index 0000000..4fbc891 --- /dev/null +++ b/src/main/java/org/itmo/lab4/sort/CompositeData.java @@ -0,0 +1,30 @@ +package org.itmo.lab4.sort; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.apache.hadoop.io.Writable; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CompositeData implements Writable { + private String category; + private int quantity; + + @Override + public void write(DataOutput out) throws IOException { + out.writeUTF(category); + out.writeInt(quantity); + } + + @Override + public void readFields(DataInput in) throws IOException { + category = in.readUTF(); + quantity = in.readInt(); + } +} diff --git a/src/main/java/org/itmo/lab4/sort/SortingJob.java b/src/main/java/org/itmo/lab4/sort/SortingJob.java new file mode 100644 index 0000000..b7b485a --- /dev/null +++ b/src/main/java/org/itmo/lab4/sort/SortingJob.java @@ -0,0 +1,48 @@ +package org.itmo.lab4.sort; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.conf.Configured; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Job; +import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; +import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; +import org.apache.hadoop.util.Tool; + +public class SortingJob extends Configured implements Tool { + @Override + public int run(String[] args) throws Exception { + String inputDir = args[0]; + String outputDir = args[1]; + int reducerCount = Integer.parseInt(args[2]); + + Configuration configuration = getConf(); + + Job sortingJob = Job.getInstance(configuration, "sorting"); + + sortingJob.setNumReduceTasks(reducerCount); // reducer scaling + + sortingJob.setJarByClass(SortingJob.class); + + sortingJob.setMapperClass(SortingMapper.class); + sortingJob.setReducerClass(SortingReducer.class); + + sortingJob.setMapOutputKeyClass(DoubleWritable.class); + sortingJob.setMapOutputValueClass(CompositeData.class); + + sortingJob.setOutputKeyClass(CompositeData.class); + sortingJob.setOutputValueClass(Text.class); + + FileInputFormat.addInputPath(sortingJob, new Path(inputDir)); + FileOutputFormat.setOutputPath(sortingJob, new Path(outputDir)); + + boolean success = sortingJob.waitForCompletion(true); + + if (!success) { + return 1; + } + + return 0; + } +} diff --git a/src/main/java/org/itmo/lab4/sort/SortingMapper.java b/src/main/java/org/itmo/lab4/sort/SortingMapper.java new file mode 100644 index 0000000..af97ec8 --- /dev/null +++ b/src/main/java/org/itmo/lab4/sort/SortingMapper.java @@ -0,0 +1,27 @@ +package org.itmo.lab4.sort; + +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Mapper; + +import java.io.IOException; + +public class SortingMapper extends Mapper { + private final DoubleWritable outKey = new DoubleWritable(); + + @Override + protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { + String[] fields = value.toString().split("\t"); + + if (fields.length == 3) { + String categoryKey = fields[0]; + + double val = Double.parseDouble(fields[1]); + int quantity = Integer.parseInt(fields[2]); + + outKey.set(-1 * val); + + context.write(outKey, new CompositeData(categoryKey, quantity)); + } + } +} diff --git a/src/main/java/org/itmo/lab4/sort/SortingReducer.java b/src/main/java/org/itmo/lab4/sort/SortingReducer.java new file mode 100644 index 0000000..848fc3b --- /dev/null +++ b/src/main/java/org/itmo/lab4/sort/SortingReducer.java @@ -0,0 +1,18 @@ +package org.itmo.lab4.sort; + +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapreduce.Reducer; + +import java.io.IOException; + +public class SortingReducer extends Reducer { + @Override + protected void reduce(DoubleWritable key, Iterable values, Context context) throws IOException, InterruptedException { + for (CompositeData value : values) { + Text category = new Text(value.getCategory()); + + context.write(category, new Text(String.format("%.2f\t%d", -1 * key.get(), value.getQuantity()))); + } + } +} From 630884f2827f45e2a9a9038a32a1657942c8a71e Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Tue, 17 Dec 2024 23:50:33 +0300 Subject: [PATCH 07/16] update datablock size --- src/main/java/org/itmo/lab4/SalesAnalyzer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/itmo/lab4/SalesAnalyzer.java b/src/main/java/org/itmo/lab4/SalesAnalyzer.java index 30438bb..0aee5d3 100644 --- a/src/main/java/org/itmo/lab4/SalesAnalyzer.java +++ b/src/main/java/org/itmo/lab4/SalesAnalyzer.java @@ -15,12 +15,12 @@ public static void main(String[] args) throws Exception { String inputDir = args[0]; String outputDir = args[1]; int reducerCount = Integer.parseInt(args[2]); - int datablockSizeKb = Integer.parseInt(args[3]) * ((int) Math.pow(2, 10)); // * 1 kb + int datablockSizeBytes = Integer.parseInt(args[3]) * 1024; // * 1 kb String intermediateResultDir = outputDir + "-intermediate"; long startTime = System.currentTimeMillis(); Configuration configuration = new Configuration(); - configuration.set("mapreduce.input.fileinputformat.split.maxsize", Integer.toString(datablockSizeKb)); + configuration.set("mapreduce.input.fileinputformat.split.maxsize", Integer.toString(datablockSizeBytes)); String[] analysisArgs = new String[]{inputDir, intermediateResultDir, String.valueOf(reducerCount)}; From 53c47788e0f5758e3180cb01c762da4438ac322b Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Wed, 18 Dec 2024 00:13:16 +0300 Subject: [PATCH 08/16] update hadoop deployment --- docker-compose.yml | 44 ++++++-------------------------------------- hadoop.env | 43 ------------------------------------------- 2 files changed, 6 insertions(+), 81 deletions(-) delete mode 100644 hadoop.env diff --git a/docker-compose.yml b/docker-compose.yml index 77c43fe..9ca09b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,59 +1,27 @@ volumes: hadoop_namenode: hadoop_datanode: - hadoop_historyserver: services: namenode: image: bde2020/hadoop-namenode:2.0.0-hadoop3.2.1-java8 container_name: namenode - restart: always + restart: unless-stopped ports: - "9870:9870" - "9000:9000" volumes: - hadoop_namenode:/hadoop/dfs/name environment: - - CLUSTER_NAME=test - env_file: - - ./hadoop.env + - CLUSTER_NAME=local-cluster + - CORE_CONF_fs_defaultFS=hdfs://namenode:9000 + - HDFS_CONF_dfs_replication=1 datanode: image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1-java8 container_name: datanode - restart: always + restart: unless-stopped volumes: - hadoop_datanode:/hadoop/dfs/data environment: - SERVICE_PRECONDITION: "namenode:9870" - env_file: - - ./hadoop.env - - resourcemanager: - image: bde2020/hadoop-resourcemanager:2.0.0-hadoop3.2.1-java8 - container_name: resourcemanager - restart: always - environment: - SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864" - env_file: - - ./hadoop.env - - nodemanager1: - image: bde2020/hadoop-nodemanager:2.0.0-hadoop3.2.1-java8 - container_name: nodemanager - restart: always - environment: - SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088" - env_file: - - ./hadoop.env - - historyserver: - image: bde2020/hadoop-historyserver:2.0.0-hadoop3.2.1-java8 - container_name: historyserver - restart: always - environment: - SERVICE_PRECONDITION: "namenode:9000 namenode:9870 datanode:9864 resourcemanager:8088" - volumes: - - hadoop_historyserver:/hadoop/yarn/timeline - env_file: - - ./hadoop.env + - CORE_CONF_fs_defaultFS=hdfs://namenode:9000 diff --git a/hadoop.env b/hadoop.env deleted file mode 100644 index 95b3d10..0000000 --- a/hadoop.env +++ /dev/null @@ -1,43 +0,0 @@ -CORE_CONF_fs_defaultFS=hdfs://namenode:9000 -CORE_CONF_hadoop_http_staticuser_user=root -CORE_CONF_hadoop_proxyuser_hue_hosts=* -CORE_CONF_hadoop_proxyuser_hue_groups=* -CORE_CONF_io_compression_codecs=org.apache.hadoop.io.compress.SnappyCodec - -HDFS_CONF_dfs_webhdfs_enabled=true -HDFS_CONF_dfs_permissions_enabled=false -HDFS_CONF_dfs_namenode_datanode_registration_ip___hostname___check=false - -YARN_CONF_yarn_log___aggregation___enable=true -YARN_CONF_yarn_log_server_url=http://historyserver:8188/applicationhistory/logs/ -YARN_CONF_yarn_resourcemanager_recovery_enabled=true -YARN_CONF_yarn_resourcemanager_store_class=org.apache.hadoop.yarn.server.resourcemanager.recovery.FileSystemRMStateStore -YARN_CONF_yarn_resourcemanager_scheduler_class=org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler -YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___mb=8192 -YARN_CONF_yarn_scheduler_capacity_root_default_maximum___allocation___vcores=4 -YARN_CONF_yarn_resourcemanager_fs_state___store_uri=/rmstate -YARN_CONF_yarn_resourcemanager_system___metrics___publisher_enabled=true -YARN_CONF_yarn_resourcemanager_hostname=resourcemanager -YARN_CONF_yarn_resourcemanager_address=resourcemanager:8032 -YARN_CONF_yarn_resourcemanager_scheduler_address=resourcemanager:8030 -YARN_CONF_yarn_resourcemanager_resource__tracker_address=resourcemanager:8031 -YARN_CONF_yarn_timeline___service_enabled=true -YARN_CONF_yarn_timeline___service_generic___application___history_enabled=true -YARN_CONF_yarn_timeline___service_hostname=historyserver -YARN_CONF_mapreduce_map_output_compress=true -YARN_CONF_mapred_map_output_compress_codec=org.apache.hadoop.io.compress.SnappyCodec -YARN_CONF_yarn_nodemanager_resource_memory___mb=16384 -YARN_CONF_yarn_nodemanager_resource_cpu___vcores=8 -YARN_CONF_yarn_nodemanager_disk___health___checker_max___disk___utilization___per___disk___percentage=98.5 -YARN_CONF_yarn_nodemanager_remote___app___log___dir=/app-logs -YARN_CONF_yarn_nodemanager_aux___services=mapreduce_shuffle - -MAPRED_CONF_mapreduce_framework_name=yarn -MAPRED_CONF_mapred_child_java_opts=-Xmx4096m -MAPRED_CONF_mapreduce_map_memory_mb=4096 -MAPRED_CONF_mapreduce_reduce_memory_mb=8192 -MAPRED_CONF_mapreduce_map_java_opts=-Xmx3072m -MAPRED_CONF_mapreduce_reduce_java_opts=-Xmx6144m -MAPRED_CONF_yarn_app_mapreduce_am_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ -MAPRED_CONF_mapreduce_map_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ -MAPRED_CONF_mapreduce_reduce_env=HADOOP_MAPRED_HOME=/opt/hadoop-3.2.1/ From 8ebf93700c31688b34a36db0aa1f275bb0d23078 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Wed, 18 Dec 2024 09:13:05 +0300 Subject: [PATCH 09/16] add launch scripts --- .gitignore | 3 +++ data/.gitkeep | 0 docker-compose.yml | 8 +++---- result.txt | 20 +++++++++++++++++ scripts/build_jar.ps1 | 1 + scripts/cleanup_hdfs.sh | 4 ++++ scripts/do_all.ps1 | 4 ++++ scripts/do_all_hdfs.sh | 9 ++++++++ scripts/extract_result.ps1 | 1 + scripts/extract_result_hdfs.sh | 8 +++++++ scripts/launch_job_hdfs.sh | 22 +++++++++++++++++++ scripts/load_data.ps1 | 8 +++++++ scripts/load_data_hdfs.sh | 8 +++++++ scripts/load_jar.ps1 | 1 + scripts/load_scripts.ps1 | 6 +++++ scripts/setup_hdfs.sh | 4 ++++ .../java/org/itmo/lab4/SalesAnalyzer.java | 3 ++- 17 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 data/.gitkeep create mode 100644 result.txt create mode 100644 scripts/build_jar.ps1 create mode 100644 scripts/cleanup_hdfs.sh create mode 100644 scripts/do_all.ps1 create mode 100644 scripts/do_all_hdfs.sh create mode 100644 scripts/extract_result.ps1 create mode 100644 scripts/extract_result_hdfs.sh create mode 100644 scripts/launch_job_hdfs.sh create mode 100644 scripts/load_data.ps1 create mode 100644 scripts/load_data_hdfs.sh create mode 100644 scripts/load_jar.ps1 create mode 100644 scripts/load_scripts.ps1 create mode 100644 scripts/setup_hdfs.sh diff --git a/.gitignore b/.gitignore index 123859e..2d65075 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /.gradle /.idea +/.vscode /build +/bin +/data/*.csv diff --git a/data/.gitkeep b/data/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker-compose.yml b/docker-compose.yml index 9ca09b5..3c052af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,9 +13,9 @@ services: volumes: - hadoop_namenode:/hadoop/dfs/name environment: - - CLUSTER_NAME=local-cluster - - CORE_CONF_fs_defaultFS=hdfs://namenode:9000 - - HDFS_CONF_dfs_replication=1 + CLUSTER_NAME: "local-cluster" + CORE_CONF_fs_defaultFS: "hdfs://namenode:9000" + HDFS_CONF_dfs_replication: "1" datanode: image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1-java8 @@ -24,4 +24,4 @@ services: volumes: - hadoop_datanode:/hadoop/dfs/data environment: - - CORE_CONF_fs_defaultFS=hdfs://namenode:9000 + CORE_CONF_fs_defaultFS: "hdfs://namenode:9000" diff --git a/result.txt b/result.txt new file mode 100644 index 0000000..645c483 --- /dev/null +++ b/result.txt @@ -0,0 +1,20 @@ +clothing 4560302171.99 911487 +video games 4560108307.50 913326 +baby products 4541435362.25 907186 +beauty products 4533874327.85 906417 +gardening tools 4531880837.74 905841 +automotive 4529861310.74 904962 +music instruments 4512294466.14 902389 +furniture 4503986763.16 900244 +electronics 4497526631.04 903266 +pet supplies 4488741730.38 896724 +stationery 4481794912.39 898265 +home appliances 4473888361.73 895815 +sports equipment 4469387812.34 894287 +groceries 4466915230.97 895470 +footwear 4465574983.36 894424 +jewelry 4463823670.79 893980 +office equipment 4463564947.38 892370 +toys 4462453654.12 892741 +books 4457620825.95 890948 +health & wellness 4454082892.49 890475 diff --git a/scripts/build_jar.ps1 b/scripts/build_jar.ps1 new file mode 100644 index 0000000..85fa03a --- /dev/null +++ b/scripts/build_jar.ps1 @@ -0,0 +1 @@ +pwsh -Command {$env:JAVA_HOME="C:\Users\Bromles\.gradle\jdks\temurin-8-amd64-windows\jdk8u422-b05"; .\gradlew.bat jar } diff --git a/scripts/cleanup_hdfs.sh b/scripts/cleanup_hdfs.sh new file mode 100644 index 0000000..1c7b458 --- /dev/null +++ b/scripts/cleanup_hdfs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +hdfs dfs -rm -r /user/root/output +hdfs dfs -rm -r /user/root/output-intermediate diff --git a/scripts/do_all.ps1 b/scripts/do_all.ps1 new file mode 100644 index 0000000..7d674aa --- /dev/null +++ b/scripts/do_all.ps1 @@ -0,0 +1,4 @@ +.\scripts\build_jar.ps1 +.\scripts\load_jar.ps1 +.\scripts\load_data.ps1 +.\scripts\load_scripts.ps1 diff --git a/scripts/do_all_hdfs.sh b/scripts/do_all_hdfs.sh new file mode 100644 index 0000000..2718c57 --- /dev/null +++ b/scripts/do_all_hdfs.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +cd ./tmp || exit 1 + +./setup_hdfs.sh +./load_data_hdfs.sh +./launch_job_hdfs.sh +./extract_result_hdfs.sh +./cleanup_hdfs.sh diff --git a/scripts/extract_result.ps1 b/scripts/extract_result.ps1 new file mode 100644 index 0000000..0853e60 --- /dev/null +++ b/scripts/extract_result.ps1 @@ -0,0 +1 @@ +docker cp namenode:/tmp/result.txt . diff --git a/scripts/extract_result_hdfs.sh b/scripts/extract_result_hdfs.sh new file mode 100644 index 0000000..0c48a53 --- /dev/null +++ b/scripts/extract_result_hdfs.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if ! [[ "$PWD" = tmp ]] +then + cd /tmp || exit 1 +fi + +hdfs dfs -cat /user/root/output/part-r-00000 > /tmp/result.txt diff --git a/scripts/launch_job_hdfs.sh b/scripts/launch_job_hdfs.sh new file mode 100644 index 0000000..50a28af --- /dev/null +++ b/scripts/launch_job_hdfs.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +if [[ -n $1 ]] +then + reducerCount=$1 +else + reducerCount=1 +fi + +if [[ -n $2 ]] +then + datablockSizeKb=$2 +else + datablockSizeKb=16384 +fi + +if ! [[ "$PWD" = tmp ]] +then + cd /tmp || exit 1 +fi + +hadoop jar lab4-parcalc-bromles-0.0.1.jar org.itmo.lab4.SalesAnalyzer input output "$reducerCount" "$datablockSizeKb" diff --git a/scripts/load_data.ps1 b/scripts/load_data.ps1 new file mode 100644 index 0000000..e716f20 --- /dev/null +++ b/scripts/load_data.ps1 @@ -0,0 +1,8 @@ +docker cp .\data\0.csv namenode:/tmp +docker cp .\data\1.csv namenode:/tmp +docker cp .\data\2.csv namenode:/tmp +docker cp .\data\3.csv namenode:/tmp +docker cp .\data\4.csv namenode:/tmp +docker cp .\data\5.csv namenode:/tmp +docker cp .\data\6.csv namenode:/tmp +docker cp .\data\7.csv namenode:/tmp diff --git a/scripts/load_data_hdfs.sh b/scripts/load_data_hdfs.sh new file mode 100644 index 0000000..f6a41ed --- /dev/null +++ b/scripts/load_data_hdfs.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +if ! [[ "$PWD" = "tmp" ]] +then + cd /tmp || exit 1 +fi + +hdfs dfs -put -f ./*.csv /user/root/input diff --git a/scripts/load_jar.ps1 b/scripts/load_jar.ps1 new file mode 100644 index 0000000..cf62b1d --- /dev/null +++ b/scripts/load_jar.ps1 @@ -0,0 +1 @@ +docker cp .\build\libs\lab4-parcalc-bromles-0.0.1.jar namenode:/tmp diff --git a/scripts/load_scripts.ps1 b/scripts/load_scripts.ps1 new file mode 100644 index 0000000..8002e9b --- /dev/null +++ b/scripts/load_scripts.ps1 @@ -0,0 +1,6 @@ +docker cp .\scripts\setup_hdfs.sh namenode:/tmp +docker cp .\scripts\load_data_hdfs.sh namenode:/tmp +docker cp .\scripts\launch_job_hdfs.sh namenode:/tmp +docker cp .\scripts\extract_result_hdfs.sh namenode:/tmp +docker cp .\scripts\cleanup_hdfs.sh namenode:/tmp +docker cp .\scripts\do_all_hdfs.sh namenode:/tmp diff --git a/scripts/setup_hdfs.sh b/scripts/setup_hdfs.sh new file mode 100644 index 0000000..92eb725 --- /dev/null +++ b/scripts/setup_hdfs.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +hdfs dfs -mkdir -p /user/root +hdfs dfs -mkdir /user/root/input diff --git a/src/main/java/org/itmo/lab4/SalesAnalyzer.java b/src/main/java/org/itmo/lab4/SalesAnalyzer.java index 0aee5d3..a3d2c1b 100644 --- a/src/main/java/org/itmo/lab4/SalesAnalyzer.java +++ b/src/main/java/org/itmo/lab4/SalesAnalyzer.java @@ -18,10 +18,11 @@ public static void main(String[] args) throws Exception { int datablockSizeBytes = Integer.parseInt(args[3]) * 1024; // * 1 kb String intermediateResultDir = outputDir + "-intermediate"; - long startTime = System.currentTimeMillis(); Configuration configuration = new Configuration(); configuration.set("mapreduce.input.fileinputformat.split.maxsize", Integer.toString(datablockSizeBytes)); + long startTime = System.currentTimeMillis(); + String[] analysisArgs = new String[]{inputDir, intermediateResultDir, String.valueOf(reducerCount)}; // Data analysis From e72d5f9c1376c55acd31c14065df3e1397e3e242 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Wed, 18 Dec 2024 09:26:55 +0300 Subject: [PATCH 10/16] update MR --- .gitignore | 1 + result.txt | 20 ------------------- .../org/itmo/lab4/analyse/AnalysisMapper.java | 7 +------ .../org/itmo/lab4/sort/SortingComparator.java | 16 +++++++++++++++ .../java/org/itmo/lab4/sort/SortingJob.java | 2 ++ .../org/itmo/lab4/sort/SortingMapper.java | 11 +++------- .../org/itmo/lab4/sort/SortingReducer.java | 2 +- 7 files changed, 24 insertions(+), 35 deletions(-) delete mode 100644 result.txt create mode 100644 src/main/java/org/itmo/lab4/sort/SortingComparator.java diff --git a/.gitignore b/.gitignore index 2d65075..2917b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /build /bin /data/*.csv +/result.txt diff --git a/result.txt b/result.txt deleted file mode 100644 index 645c483..0000000 --- a/result.txt +++ /dev/null @@ -1,20 +0,0 @@ -clothing 4560302171.99 911487 -video games 4560108307.50 913326 -baby products 4541435362.25 907186 -beauty products 4533874327.85 906417 -gardening tools 4531880837.74 905841 -automotive 4529861310.74 904962 -music instruments 4512294466.14 902389 -furniture 4503986763.16 900244 -electronics 4497526631.04 903266 -pet supplies 4488741730.38 896724 -stationery 4481794912.39 898265 -home appliances 4473888361.73 895815 -sports equipment 4469387812.34 894287 -groceries 4466915230.97 895470 -footwear 4465574983.36 894424 -jewelry 4463823670.79 893980 -office equipment 4463564947.38 892370 -toys 4462453654.12 892741 -books 4457620825.95 890948 -health & wellness 4454082892.49 890475 diff --git a/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java b/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java index fec97b3..48648a0 100644 --- a/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java +++ b/src/main/java/org/itmo/lab4/analyse/AnalysisMapper.java @@ -6,21 +6,16 @@ import java.io.IOException; public class AnalysisMapper extends Mapper { - private final Text categoryKey = new Text(); - @Override protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split(","); if (fields.length == 5 && !fields[0].equals("transaction_id")) { String category = fields[2]; - double price = Double.parseDouble(fields[3]); int quantity = Integer.parseInt(fields[4]); - categoryKey.set(category); - - context.write(categoryKey, new SalesData(price * quantity, quantity)); + context.write(new Text(category), new SalesData(price * quantity, quantity)); } } } diff --git a/src/main/java/org/itmo/lab4/sort/SortingComparator.java b/src/main/java/org/itmo/lab4/sort/SortingComparator.java new file mode 100644 index 0000000..eb289be --- /dev/null +++ b/src/main/java/org/itmo/lab4/sort/SortingComparator.java @@ -0,0 +1,16 @@ +package org.itmo.lab4.sort; + +import org.apache.hadoop.io.DoubleWritable; +import org.apache.hadoop.io.WritableComparable; +import org.apache.hadoop.io.WritableComparator; + +public class SortingComparator extends WritableComparator { + public SortingComparator() { + super(DoubleWritable.class, true); + } + + @Override + public int compare(WritableComparable a, WritableComparable b) { + return -1 * a.compareTo(b); + } +} diff --git a/src/main/java/org/itmo/lab4/sort/SortingJob.java b/src/main/java/org/itmo/lab4/sort/SortingJob.java index b7b485a..12c8657 100644 --- a/src/main/java/org/itmo/lab4/sort/SortingJob.java +++ b/src/main/java/org/itmo/lab4/sort/SortingJob.java @@ -25,6 +25,8 @@ public int run(String[] args) throws Exception { sortingJob.setJarByClass(SortingJob.class); + sortingJob.setSortComparatorClass(SortingComparator.class); + sortingJob.setMapperClass(SortingMapper.class); sortingJob.setReducerClass(SortingReducer.class); diff --git a/src/main/java/org/itmo/lab4/sort/SortingMapper.java b/src/main/java/org/itmo/lab4/sort/SortingMapper.java index af97ec8..adec986 100644 --- a/src/main/java/org/itmo/lab4/sort/SortingMapper.java +++ b/src/main/java/org/itmo/lab4/sort/SortingMapper.java @@ -7,21 +7,16 @@ import java.io.IOException; public class SortingMapper extends Mapper { - private final DoubleWritable outKey = new DoubleWritable(); - @Override protected void map(Object key, Text value, Context context) throws IOException, InterruptedException { String[] fields = value.toString().split("\t"); if (fields.length == 3) { - String categoryKey = fields[0]; - - double val = Double.parseDouble(fields[1]); + String category = fields[0]; + double revenue = Double.parseDouble(fields[1]); int quantity = Integer.parseInt(fields[2]); - outKey.set(-1 * val); - - context.write(outKey, new CompositeData(categoryKey, quantity)); + context.write(new DoubleWritable(revenue), new CompositeData(category, quantity)); } } } diff --git a/src/main/java/org/itmo/lab4/sort/SortingReducer.java b/src/main/java/org/itmo/lab4/sort/SortingReducer.java index 848fc3b..e09b7c5 100644 --- a/src/main/java/org/itmo/lab4/sort/SortingReducer.java +++ b/src/main/java/org/itmo/lab4/sort/SortingReducer.java @@ -12,7 +12,7 @@ protected void reduce(DoubleWritable key, Iterable values, Contex for (CompositeData value : values) { Text category = new Text(value.getCategory()); - context.write(category, new Text(String.format("%.2f\t%d", -1 * key.get(), value.getQuantity()))); + context.write(category, new Text(String.format("%.2f\t%d", key.get(), value.getQuantity()))); } } } From fd1270c4126bcdb3b0cc56bf8dfd6778b44b0c39 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Wed, 18 Dec 2024 10:12:38 +0300 Subject: [PATCH 11/16] update sorting job output type --- src/main/java/org/itmo/lab4/sort/SortingJob.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/itmo/lab4/sort/SortingJob.java b/src/main/java/org/itmo/lab4/sort/SortingJob.java index 12c8657..430c6e6 100644 --- a/src/main/java/org/itmo/lab4/sort/SortingJob.java +++ b/src/main/java/org/itmo/lab4/sort/SortingJob.java @@ -33,7 +33,7 @@ public int run(String[] args) throws Exception { sortingJob.setMapOutputKeyClass(DoubleWritable.class); sortingJob.setMapOutputValueClass(CompositeData.class); - sortingJob.setOutputKeyClass(CompositeData.class); + sortingJob.setOutputKeyClass(Text.class); sortingJob.setOutputValueClass(Text.class); FileInputFormat.addInputPath(sortingJob, new Path(inputDir)); From 464bd07747bd8eeb105c1abe671656f57cf67320 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Wed, 18 Dec 2024 15:21:39 +0300 Subject: [PATCH 12/16] update comments --- src/main/java/org/itmo/lab4/SalesAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/itmo/lab4/SalesAnalyzer.java b/src/main/java/org/itmo/lab4/SalesAnalyzer.java index a3d2c1b..a774b5f 100644 --- a/src/main/java/org/itmo/lab4/SalesAnalyzer.java +++ b/src/main/java/org/itmo/lab4/SalesAnalyzer.java @@ -8,7 +8,7 @@ public class SalesAnalyzer { public static void main(String[] args) throws Exception { if (args.length != 4) { - System.err.println("Usage: hadoop jar /tmp/lab4-parcalc-bromles-0.0.1.jar org.itmo.lab4.SalesAnalyzer "); + System.err.println("Usage: hadoop jar /tmp/lab4-parcalc-bromles-0.0.1.jar org.itmo.lab4.SalesAnalyzer "); System.exit(-1); } From 24b5f3365431f147199d29f737724b64ce73fcca Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Thu, 19 Dec 2024 12:52:21 +0300 Subject: [PATCH 13/16] update scripts --- scripts/do_all_hdfs.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/do_all_hdfs.sh b/scripts/do_all_hdfs.sh index 2718c57..bbf3175 100644 --- a/scripts/do_all_hdfs.sh +++ b/scripts/do_all_hdfs.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash -cd ./tmp || exit 1 +if ! [[ "$PWD" = tmp ]] +then + cd /tmp || exit 1 +fi ./setup_hdfs.sh ./load_data_hdfs.sh From dccecc02289eb217d74f021d1759275cfa50dd24 Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Thu, 19 Dec 2024 15:46:22 +0300 Subject: [PATCH 14/16] add extra logging --- src/main/java/org/itmo/lab4/SalesAnalyzer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/itmo/lab4/SalesAnalyzer.java b/src/main/java/org/itmo/lab4/SalesAnalyzer.java index a774b5f..7840640 100644 --- a/src/main/java/org/itmo/lab4/SalesAnalyzer.java +++ b/src/main/java/org/itmo/lab4/SalesAnalyzer.java @@ -29,6 +29,7 @@ public static void main(String[] args) throws Exception { int exitCode = ToolRunner.run(configuration, new AnalysisJob(), analysisArgs); if (exitCode != 0) { + System.out.println("Analysis job failed"); System.exit(1); } From b38774a120c056d96652623309771a958eecacff Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Thu, 19 Dec 2024 16:11:22 +0300 Subject: [PATCH 15/16] add charts --- hadoop.png | Bin 0 -> 36388 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hadoop.png diff --git a/hadoop.png b/hadoop.png new file mode 100644 index 0000000000000000000000000000000000000000..3543e7a03b774533b3ad6122b52ca25255f19d4d GIT binary patch literal 36388 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 Ibp&PSfLnoKSvm6 zLJ(_al`tUZ@~*cE_=n}~?H7xFA#NzfdpCwhMcCtVR5CC>5_~YY!D97A5woH!XU*Iy zKg05N=>UW;PArCMbHz?v4(Qo^GehmmQ3gl>piwVo=dPXCYDy37Or3ki`z@kgh4?~6 z<_tQ>#xdHaDQHX4&PWecE62!qX^Y_<3D?^ayxv50c#o6UDZEojA$w$us*uTj2tl~0 zb_?CBnKnW?i755?Sr5IzRRHJpy>ewRK(W@!%RRdqcu-h$I;n`~$;6RlbC-1wdZjw>3hsrU7xC(R~F~xU!E`6GNZwsjtjmaX8(DoIh z+O%V3Ca+L$OqcC;N-o-C>GBj~fuEjFGj5()zb@GMVc(i|Dm4jnn3y!)TZmin8 zx47%Jbuow&I9P@d6=SI=_n@*0G=-5*vvkBNS%NHOk>f12^DPj{_cN6GE8fD*^-EFL z=UzuZ(ZJoS9-+xb5*ApKM&$NW;(EN>`Sio1W^&&}K-Qwg%-szuQu^qs&@qi z5A4N{s;s2G)fL93hv?>bvq1I@0>{ZcKa1?SUOa{FIXZ1lN^rL4pJH5vZPHS7ntwlq zc*-#u;QEv1mJ_Qra6;*IWe){V8g*>8Ui<@&a6ITKjLSvjPT~XyJWYsqMYrMF+pn`r zjEkaV4MK;Nzmp_NHhLBo?Xy(z&UNONb5ydtQtP-i?cTJpcGavs6Cwph2;^s{-c{YS zDIFYnbPR8%Dx5X#%pK{~@M5b2Ciz(3y@115wZol_;CKngWD;jXRU0g)fD&j=!5*;ZFU%#ilQ@n} zP;aPs*+~s8%rih?$!P~bIKltC!c^n%Vret1nSkY?aCP|TDE8ZD^U|7A%QZv?2gKn| zAEBhJAIv!?ZpL8U9S}=C9kSDn$2+c2DN2s?Og+@B) z%jqCo?zTxTD3AbEWoeAP&Yqz%7&#nMHlN-?zJEuHedhH!%aqEXclNxNzOFPO6H5#g z6mLNYaIFw0Q#QnB``hZd__Rjop4!-`yg293^KzmOG>&6255J|)`!vduF1ZmDrF|D> zNf>pX-BXqelt_~|o!AN7O!qr#yX8!F&9RYlRe807(2(}(*t3Za+l24IWN;wb0JP!> z(5&IoR+v1*3~cviR2j4~x^2aBb?~XTbbqkoxlUJ_lK5^QJZ{GXl^O=v95O}XJC6s^ z1jLFA&o;Y-Mfx{RPpT;Iam6eg7mLu;CDXliPGC8+)^qGwb`tRhAG81~vBqNCPufHA zb!Qx7)C%V`4vdrO(Z==Oxw(<&NBz1)u1ahw6kc?_PdcAvuBsS;gGtA?X3gq^1hoE{ zqO)C%#`U83Z#f!Dbgd*S44Eet6e80_{aBb_mHxMc?l&S{&^b=L7=HzDHxIFSGPpCd|TP!DQ87Z`vC?!c8^G_kn%{f#w<> zKh|>a9Q@xvtDCYvG2+ZbWC(KPtSn={Kl*|SDlv4kw8bB1;Vfl1>D&N=kkPA~p49xS zQ0Ac*8%%Jwn4BC=SanYc=$1aJP#yfj|TId@T3UVZE zi#YqD#DyLAMol(Zy_~ESN)Y(R8{4w<^5dxvRKLmVmY2YJwGCXuX6qpWlAMTO!=^fp z2H!G}3i`Phb?;k~olS@!$fD05Z(^Jq(bc}QLy;Y%&GCqcT8zQ@hW+iQWJjG%D|*e# zJRNCevjqetvRSiFQ4=V_;wNtY5RDLxg1rZBNZT-+fg>`eiF;#cBDC0uqjlP!0NV8t z?!>|^Q*79@SIysq)Nu=Jtqs6-jPz>u=y})ry;im85>a&aUZ4;DXiuisBD^qtlB ze5G$ZdM-dqzTJMT>KcBg+ecLX;3eiM`wPM@hg+U`6KJD9h}VdFVe}t{`(32pjg!{KM_h zHR+Tiw*7X)iaV&?p>sEKETBP3xN&acJNCv~d}>wc9WkX!zxf#%Dej5-a6_1@!9n-# z@OBPw`CB-j8A^fCdR%QxUD5d_aPH33Kos&zB3`6Sm_b;7HpfC(n#wR{yJhp^9?SWh zX9Te)&%`daw1ci(ou-AuQQjuXvxpCuwi4bHE4QsrQRc|nq0|r#k%!yr0nx7jYRMfV zBc(6mX{Y0Iat18&b8gg0;gnC!D$01MdW8SN9b4EkvVgiiB|!`;Q%epK^u98qlnoFx zieT(ZzO>$H5$V8X_kjA8`!OfF&g>>_^4@jbyYjcR@wDu(K2~#fG)$jU0LV_6y)caV z@>ibNWF05=4}nOnv9OHwoBAju1aUuiE(n0}r;KFBM@Rd=mEqATK?GLD<~jCPi$Sfx zGc<3tmQHT>#dS(|tustwK=O(Mk&C94sBNz+?i_JkrQZr2v-7*IbbI!JUNE~JR%?ix z$_|o&?1KoA6)FbS6CU1-@nT!&ntR0@y)%?|#vrjMP3=6e6SLsl1N6B)^SF-+_WKMu zv;>Ad+}$rBjk0TZ%Xx$}CvBOW*U0V&l>5WV^8_@4VbTT_hQjpndp7QhbJu-H&MAB4 zzvupjVUqHqS46I#vN;6m5UQi^Ib>eAripv4wlI?;(#?HcD?BPXE{kZF&}Kl6x~1Tr z_o|+hnV6(!jbk66$lb^gl|1wzRz%i;M2{aoVXpCo!p}0`_M=!cSx6+witAKaT8j`< zPTlJTZ#3zAsXpfo%H z;bsNJF$x;bXs^;&x5LZ11hq!twclgPkuifp95h(mdY!}GM(c8^{vHV*9eOGh8Ro@0 zTw5JhgRVw_2lGZtmv^d4Pts<4?ZQ!cSXjnGMIK(e7>si~fH&rV2w&h&fj5LHX76y+ zKWms+uIR0S=|SQ@S_xdG;liJfV4^04`rkn6bc!KE){-(uhis2rnsYEiF>a~yuJe#6}AwnGL znC!RGi}Jz|m*{@(mGt=O{gW#DEq~5<@zUxI$FYXCRt%_n&d-XAqnwTI6C<=O+i6d9 z^$yM?h^h?GhNbFC6@2vFW6m13X=tyaei|t35__8dY)^uBwzZ)bZ_P>Ze0HttYE{1b zG8k8DPT#)`5lQ&$ zt2>4`#V8-WiSL&>6@(o;Q<96ut42W|9u)9dvs4^5QqE|p7N!(UBOn7IE-FTWEf=T4|$c|5EKQHr5d?3;X;Mcs$>WN zklWiy)e8DJJbYV9W@W8f!DlNw09t!@B~R2zB{gi&Y6x42|0omAem~y z)#iEC<4h@M%B5g7tkXsztU_j3z(zYRdK{mn*}iC>;lh2DmfuL;IgFAqa6&{F`?}l+ z-y{A$jc!r9);7+=kQO})O4oeUp1jV~DT1=H0_cACf!^S_8T&m0v5Olw59o(}?|#iY z_QGB1Fi?~3tS?}1^o~ywRDV8KufY>MJ3|g$vquJ2;o)6Ubfmb!S=)DA!}&U&ie7Gv za_8kaAJ=}-ZXz2(-rS~)l@{rgP$jN_OX8PZrc>c2U@mkk12oX z3;*t(7vi>ekjug87xSVFzI(PKLqq%?#h#(>nPLT{;eI1x55FEuOaF?eFU8nb4>zwrH-JbpNFNo zdd(?_inP4=Cefo3rCTW6TszBXnI{jF8w-vWP5OE8?`>%xR`Te9-$Gs0L&Z|fh2N*n zCZ>VNa{qi_e$3!O3 zoo!RPLG~4iJ3Fk%e$xq)k!bJJQdQd;%ge#Bt_JD5$B3*SPhjUyJb9DcQ^vv>VdaYp zi~{rt$j_K`Y{A!5858emRLoKE4)ZQhr+px|6H&PAn8_XZ?ZVr&Dy+lyy?un0Zq7)% zzBBfTN!;v7@lA<&=k;~c`EJi9<(Q+nKu7qU-g-W~`gJEuk$+5uIEG(tn+e-c3Gs2l zM9<~(TNs^%_IE;Vp=5%5_*X4u9hVI2BfAH6Dh-XnSo_EzCu~c*fyUF8j^w^gu@`x6 zmHr&U{)@dlW#{PYYWElkrBIH`RlOP6I`G2%}W^WmidO0?Ssn%HvYG!knz7w`{-J||9_?t$^sVCO0ggfI3g-(pbyHfqeHb#?di%d z+V87XC!$pq{S>ijtJ8Qsy9MaIZ$FrKr4>AxSwb}^=`iqhiRPz%M$el|D0+N@2avWH z80yE4?iYl3Zq0Jc4cqtAhGE*!%<4O)lSSm`c&>js%_NpC=x*R?F?}Q?m&1Fs?p^6` zw&h1}J-N<_gXp)T<^Z0T?^+;fDAH0;5HsE60w-CJo|f#+vTY<6_D0Z2j!Ib-&A)tc zp>mq$*u+du{j1oS^76sk>W{l|J`8`+^sE+6&1~h7`*-p;Ogv1y-!<_s((VtT3D@6sasok{n}`r^xJwVXXZMg_i;zXIxqO$auDxz zBtRcOxo1t&>2dnP$xWpv%LLEKKpgkrrTA-YKiS{j9Qoj*{r~mIR{eJEqju#-hxLDP z5tIDjuzk0M=~Wi$?B4X=R59P1+wnEb631Q0H{L}a*?;1?1_s<=2`OIGAwqQu4mc!5 zm|WTWkIM&)cvSa4%a=PPEBCBK?v#l@eij!~w~gfkKY8G?jrp0$O|G*!TqcG=!kIu` zV!G4FO7;nFJ<9wnBKcx$=D~=UKj251g|#IIn3H4eZRE4iq2X<$(NJp-xYZ{)%8k{J yFPuU?`+K*)AJ9^&ro`->0a4PXr|Nx+Yn`9n>ByEf=Ynsx>Q_~*LpcYI2mC*k9`w5a literal 0 HcmV?d00001 From 6085f4639f40585d5bddd6f91b137ab6dd540dce Mon Sep 17 00:00:00 2001 From: Aleksandr Ushakov Date: Thu, 19 Dec 2024 17:06:44 +0300 Subject: [PATCH 16/16] rename compose file --- docker-compose.yml => compose.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docker-compose.yml => compose.yml (100%) diff --git a/docker-compose.yml b/compose.yml similarity index 100% rename from docker-compose.yml rename to compose.yml