前言
上一篇文章,我們利用3台雲伺服器搭建了一個Hadoop叢集,并通過hadoop -jar指令運作了Hadoop自帶的一個wordcount例子,那本片文章就通過實作一個wordcount程式,并在本地模式下運作這個程式,了解一下mapreduce編碼規範,最後 再将這個jar包送出到真正的叢集上運作。
Hadoop maven依賴
本想使用springboot內建的hadoop,但是發現其版本最高隻內建到Hadoop2.7.3,于是乎,不适用spring boot,直接使用hadoop2.9.2的包。maven依賴如下:
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
Hadoop本地(windows環境)運作依賴
由于是本地模式運作,是以需要導入windows下的hadoop.dll和winutils.exe,否則将會抱錯
java.lang.UnsatisfiedLinkError
:
各個版本的hadoop.dll以及winutils.exe下載下傳位址:
https://download.csdn.net/download/wxgxgp/11072195
(CSDN資源預設需要積分,若沒有積分,請在我的部落格-留言闆留下郵箱,将郵件發送。)
當然你也可以去github下載下傳(這裡并不包含2.9.2版本):
https://github.com/4ttty/winutils
編寫MapReduce程式
由于之前對storm有所研究,是以,這次了解mapreduce還是較為容易的,關于mapreduce的原理以及解釋本人不在重複造輪子,詳情請看下方連結:
MapReduce工作流程最詳細解釋
以下内容隻是從代碼層面說明mapreduce注意的一些問題。
Map階段
public class MapperTest extends Mapper<Object, Text, Text, IntWritable> {
@Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
String[] words = value.toString().split(" ");
for(String word : words){
context.write(new Text(word),new IntWritable(1));
}
}
}
繼承Mapper類即可。注意泛型參數格式為<輸入key類型,輸入value類型,輸出key類型,輸出value類型>
注意輸入類型一般是檔案中每一行首的偏移量。
這個程式隻能統計英文單詞個數,但無法統計中文詞語個數,因為英文單詞之間有空格分割,而中文則沒有,是以若需要統計中文詞語,則需要先進行分詞處理,而這就屬于nlp的範圍了。
Reduce階段
public class ReduceTest extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
context.write(key, new IntWritable(sum));
}
}
同樣的隻需要繼承Reducer類即可,泛型參數格式為<輸入key類型,輸入value類型,輸出key類型,輸出value類型>,注意這裡的輸入key和value的類型要和map階段輸出的key和value的類型一緻。
整個map-reduce從代碼層面看起來很簡單,就是把單詞根據空格分割,然後寫入到contex,然後在根據相同的單詞進行一個累加計數的彙總,其實更多的是Hadoop架構為我們做了大量的事情。
編寫主類
public class WordCount {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
if (args == null || args.length == 0) {
args = new String[2];
args[0] = "E:/hadoop/input/test.txt";
args[1] = "E:/hadoop/output";
}
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
job.setJarByClass(WordCount.class);
job.setMapperClass(MapperTest.class);
job.setReducerClass(ReduceTest.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
Path inputPath = new Path(args[0]);
Path outputPath = new Path(args[1]);
//自動删除已存在的目錄
outputPath.getFileSystem(configuration).delete(outputPath, true);
FileInputFormat.setInputPaths(job,inputPath);
FileOutputFormat.setOutputPath(job,outputPath);
boolean wait = job.waitForCompletion(true);
System.exit(wait?0:1);
}
}
運作一下:
送出到叢集運作
打包jar
因為使用的是maven,是以就用maven打包jar了。
需要注意的是:
要在pom.xml中添加打包類型:
<packaging>jar</packaging>
添加主類:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>test.WordCount</mainClass>
<addClasspath>true</addClasspath>
</manifest>
</archive>
</configuration>
</plugin>
不添加主類則送出到叢集後會出現找不到主類的錯誤。
送出到叢集
将jar包上傳到伺服器上,執行指令:
hadoop jar hadooptest-1.0-SNAPSHOT.jar /test/input /test/output
(注意這裡的路徑修改為自己的,hadooptest-1.0-SNAPSHOT.jar為打包後的jar包名稱,若沒有在程式裡設定job名稱,則會預設一jar包名稱作為job名稱)
其中/test/input下有一個檔案text.txt,其内容如下:
哈哈哈哈
a
adad
adads
asdddqwd
asda
adds
13
132
132
a
我們也可以在web界面上看到執行情況:
最後我們在web界面檢視一下結果:
表明叢集上運作成功。