今天我們來聊一聊大數據,作為一個Hadoop的新手,我也并不敢深入探討復雜的底層原理。因此,這篇文章的重點更多是從實際操作和入門實踐的角度出發(fā),帶領大家一起了解大數據應用的基本過程。我們將通過一個經典的案例——WordCounter,來幫助大家入門。簡單來說,這個案例的目標是從一個文本文件中讀取每一
今天我們來聊一聊大數據,作為一個Hadoop的新手,我也并不敢深入探討復雜的底層原理。因此,這篇文章的重點更多是從實際操作和入門實踐的角度出發(fā),帶領大家一起了解大數據應用的基本過程。我們將通過一個經典的案例——WordCounter,來幫助大家入門。簡單來說,這個案例的目標是從一個文本文件中讀取每一行,統(tǒng)計其中單詞出現的頻率,最后生成一個統(tǒng)計結果。表面上看,這個任務似乎不難,畢竟我們在本地用Java程序就可以很輕松地實現。
然而,實際情況并非如此簡單。雖然我們能夠在一臺計算機上通過簡單的Java程序完成類似的任務,但在大數據的場景下,數據量遠遠超過一臺機器能夠處理的能力。此時,單純依賴一臺機器的計算資源就無法應對龐大的數據量,這正是分布式計算和存儲技術的重要性所在。分布式計算將任務拆分為多個子任務,并利用多臺機器協同工作,從而實現高效處理海量數據,而分布式存儲則可以將數據切分并存儲在多個節(jié)點上,解決數據存儲和訪問的瓶頸。
因此,通過今天的介紹,我希望能夠帶大家從一個簡單的例子出發(fā),逐步理解大數據處理中如何借助Hadoop這樣的分布式框架,來高效地進行數據計算和存儲。
這里我不太喜歡在本地 Windows 系統(tǒng)上進行安裝,因為本地環(huán)境中通常會積累很多不必要的文件和配置,可能會影響系統(tǒng)的干凈與流暢度。因此,演示的重點將放在以 Linux 服務器為主的環(huán)境上,通過 Docker 實現快速部署。
我們將利用寶塔面板進行一鍵式安裝,只需通過簡單的操作即可完成整個部署過程,免去手動敲命令的麻煩,讓安裝變得更加便捷和快速。
這里,系統(tǒng)本身已經對外開放了部分端口,例如 9870 用于訪問 Web UI 界面,但有一個重要的端口 8020 并沒有開放。這個端口是我們需要通過本地的 IntelliJ IDEA 進行連接和使用的,因此必須手動進行額外的配置,確保該端口能夠正常訪問。具體操作可以參考以下示意圖進行設置,以便順利完成連接。
如果你已經成功啟動并完成配置,那么此時你應該能夠順利訪問并查看 Web 頁面。如圖所示:
我們可以直接創(chuàng)建一個新的項目,并根據項目需求手動配置相關的項目信息,例如
groupId
、
artifactId
、
version
等基本配置。為了確保兼容性和穩(wěn)定性,我們選擇使用 JDK 8 作為開發(fā)環(huán)境版本。
首先,讓我們先來查看一下項目的文件目錄結構,以便對整個項目的組織形式和文件分布有一個清晰的了解。
tree /f 可以直接生成
├─input
│ test.txt
├─output
├─src
│ ├─main
│ │ ├─java
│ │ │ └─org
│ │ │ └─xiaoyu
│ │ │ InputCountMapper.java
│ │ │ Main.java
│ │ │ WordsCounterReducer.java
│ │ │
│ │ └─resources
│ │ core-site.xml
│ │ log4j.xml
接下來,我們將實現大數據中的經典示例——"Hello, World!" 程序,也就是我們通常所說的 WordCounter。為了實現這個功能,首先,我們需要編寫 MapReduce 程序。在 Map 階段,主要的任務是將輸入的文件進行解析,將數據分解并轉化成有規(guī)律的格式(例如,單詞和其出現次數的鍵值對)。接著,在 Reduce 階段,我們會對 Map 階段輸出的數據進行匯總和統(tǒng)計,最終得到我們想要的統(tǒng)計結果,比如每個單詞的出現次數。
此外,我們還需要編寫一個啟動類——Job 類,用來配置和啟動 MapReduce 任務,確保 Map 和 Reduce 階段的流程能夠順利進行。通過這整套流程的實現,我們就完成了一個基本的 WordCounter 程序,從而理解了 MapReduce 的核心思想與應用。
這里沒有什么好說的,直接添加相關依賴即可:
org.apache.hadoop
hadoop-common
3.2.0
org.apache.hadoop
hadoop-hdfs
3.2.0
log4j
log4j
1.2.17
org.apache.hadoop
hadoop-client
3.2.0
org.apache.hadoop
hadoop-mapreduce-client-core
3.2.0
org.apache.hadoop
hadoop-mapreduce-client-common
3.2.0
這里配置的我們遠程Hadoop連接配置信息:
fs.defaultFS
hdfs://你自己的ip:8020
我們此次主要以演示為主,因此并不需要處理非常大的文件。為了簡化演示過程,我在此僅提供了一部分數據。
xiaoyu xiaoyu
cuicui ntfgh
hanhan dfb
yy yy
asd dfg
123 43g
nmao awriojd
先來構建一下InputCountMapper類。代碼如下:
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
public class InputCountMapper extends Mapper {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString().trim();
for (int i = 0; i < line.split(" ").length; i++) {
word.set(line.split(" ")[i]);
context.write(word, one);
}
}
}
在Hadoop的MapReduce編程中,寫法其實是相對簡單的,關鍵在于正確理解和定義泛型。你需要集成一個
Mapper
類,并根據任務的需求為其定義四個泛型類型。在這個過程中,每兩個泛型組成一對,形成K-V(鍵值對)結構。以上面的例子來說,輸入數據的K-V類型是
LongWritable-Text
,輸出數據的K-V類型定義為
Text-IntWritable
。這里的
LongWritable
、
Text
、
IntWritable
等都是Hadoop自定義的數據類型,它們代表了不同的數據格式和類型。除了
String
在Hadoop中被替換成
Text
,其他的數據類型通常是在后面加上
Writable
后綴。
接下來,對于
Mapper
類的輸出格式,我們已經在代碼中定義了格式類型。然而,需要注意的是,我們重寫的
map
方法并沒有直接返回值。相反,
Mapper
類會通過
Context
上下文對象來傳遞最終結果。
因此,我們只需要確保在
map
方法中將格式化后的數據存入
Context
,然后交給
Reducer
處理即可。
這一步的代碼如下:
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class WordsCounterReducer extends Reducer {
@Override
protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable val : values) {
sum += val.get();
}
context.write(key, new IntWritable(sum));
}
}
在Hadoop的MapReduce編程中,
Reduce
階段的寫法也遵循固定模式。首先,我們需要集成
Reducer
類,并定義好四個泛型參數,類似于
Mapper
階段。這四個泛型包括輸入鍵值對類型、輸入值類型、輸出鍵值對類型、以及輸出值類型。
在
Reduce
階段,輸入數據的格式會有所變化,尤其是在值的部分,通常會變成
Iterable
類型的集合。這個變化的原因是,
Mapper
階段處理時,我們通常將每個單詞的出現次數(或其他統(tǒng)計信息)作為1存入
Context
。比如,假設在
Mapper
階段遇到單詞“xiaoyu”時,我們每次都會輸出一個
(xiaoyu, 1)
的鍵值對。結果,如果單詞“xiaoyu”在輸入數據中出現多次,
Context
會把這些鍵值對合并成一個
Iterable
集合,像是
(xiaoyu, [1, 1])
,表示該單詞出現了兩次。
在這個例子中,
Reduce
階段的操作非常簡單,只需要對每個
Iterable
集合中的值進行累加即可。比如,對于
xiaoyu
的輸入集合
(xiaoyu, [1, 1])
,我們只需要將其所有的
1
值累加起來,得出最終的結果2。
最后我們需要生成一個Job,代碼如下:
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 {
static {
try {
System.load("E:\\hadoop.dll");//建議采用絕對地址,bin目錄下的hadoop.dll文件路徑
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n" + e);
System.exit(1);
}
}
public static void main(String[] args) throws Exception{
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "wordCounter");
job.setJarByClass(Main.class);
job.setMapperClass(InputCountMapper.class);
job.setReducerClass(WordsCounterReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
FileInputFormat.addInputPath(job, new Path("file:///E:/hadoop/test/input"));
FileOutputFormat.setOutputPath(job, new Path("file:///E:/hadoop/test/output"));
System.exit(job.waitForCompletion(true) ? 0 : 1);
}
}
好的,這里所展示的是一種完全固定的寫法,但在實際操作過程中,需要特別注意的是,我們必須通過 Windows 環(huán)境來連接遠程的 Hadoop 集群進行相關操作。這個過程中會遇到很多潛在的問題和坑,尤其是在配置、連接、權限等方面。
接下來,我將逐一解析并解決這些常見的難題,希望能為大家提供一些實際的參考和指導,幫助大家更順利地完成操作。
如果你并不是以本地 Windows 目錄為主,而是以遠程服務器上的目錄為主進行操作,那么你可能會采用類似以下的寫法:
FileInputFormat.addInputPath(job, new Path("/input"));
FileOutputFormat.setOutputPath(job, new Path("/output"));
那么,在這種情況下,我們必須先創(chuàng)建與操作相關的輸入目錄(input),但需要特別注意的是,切勿提前創(chuàng)建輸出目錄(output),因為 Hadoop 在運行作業(yè)時會自動創(chuàng)建該目錄,如果該目錄已存在,會導致作業(yè)執(zhí)行失敗。因此,只需要進入 Docker 環(huán)境并直接執(zhí)行以下命令即可順利開始操作。
hdfs dfs -mkdir /input
當然,還有一種更簡單的方式,就是直接通過圖形界面在頁面上創(chuàng)建相關目錄或資源。具體操作可以參考以下步驟,如圖所示:
接下來,當你在運行 Job 任務時,系統(tǒng)會在最后一步嘗試創(chuàng)建輸出目錄(output)。然而,由于當前用戶并沒有足夠的權限來進行此操作,因此會出現類似于以下的權限錯誤提示:
Permission denied: user=yu, access=WRITE, inode="/":root:supergroup:drwxr-xr-x
。該錯誤意味著當前用戶(yu)試圖在根目錄下創(chuàng)建目錄或文件,但由于該目錄的權限設置為只有管理員(root)才能寫入,普通用戶無法進行寫操作,從而導致作業(yè)執(zhí)行失敗。
所以你仍需要進入docker容器,執(zhí)行以下命令:
hadoop fs -chmod 777 /
這樣基本上就可以順利完成任務了。接下來,你可以直接點擊進入查看 output 目錄下的文件內容。不過需要注意的是,由于我們沒有配置具體的 IP 地址,因此在進行文件下載時,你需要手動將文件中的 IP 地址替換為你自己真實的 IP 地址,才能確保下載過程能夠順利進行并成功獲取所需的文件。
這種問題通常是由于缺少
hadoop.dll
文件導致的。在 Windows 系統(tǒng)上運行 Hadoop 時,
hadoop.dll
或者
winutils.exe
是必需的依賴文件,因為它們提供了 Hadoop 在 Windows 上所需的本地代碼支持和執(zhí)行環(huán)境。
為了確保順利運行,你需要下載對應版本的
hadoop.dll
或者
winutils.exe
文件。已經為你準備好了多個 Hadoop 版本對應的這些文件,所有的文件都可以從以下鏈接下載:
https://github.com/cdarlint/winutils
我們這里只下載一個hadoop.dll,為了不重啟電腦,直接在代碼里面寫死:
static {
try {
System.load("E:\\hadoop.dll");//建議采用絕對地址,bin目錄下的hadoop.dll文件路徑
} catch (UnsatisfiedLinkError e) {
System.err.println("Native code library failed to load.\n" + e);
System.exit(1);
}
}
如果仍然有問題,那就配置下windows下的wsl子系統(tǒng):
使用Windows + R快捷鍵打開「運行」對話框,執(zhí)行OptionalFeatures打開「Windows 功能」。
勾選「適用于 Linux 的 Windows 子系統(tǒng)」和「虛擬機平臺」,然后點擊「確定」。
終于成功跑出結果了!在這個過程中,輸出的結果是按照默認的順序進行排序的,當然這個排序方式是可以根據需要進行自定義的。如果你對如何控制排序有興趣,實際上可以深入了解并調整排序機制。
通過今天的分享,我們簡單地了解了大數據處理中一個經典的應用——WordCounter,并通過Hadoop框架的實踐,展示了如何使用MapReduce進行分布式計算。雖然表面上看,WordCounter是一個相對簡單的程序,但它卻揭示了大數據處理中的核心思想。
從安裝配置到編寫代碼,我們一步步走過了Hadoop集群的搭建過程,希望通過這篇文章,你能對大數據應用開發(fā),特別是Hadoop框架下的MapReduce編程,獲得一些啟發(fā)和幫助。大數據的世界龐大而復雜,但每一次小小的實踐,都會帶你離真正掌握這門技術更近一步。
我是努力的小雨,一名 Java 服務端碼農,潛心研究著 AI 技術的奧秘。我熱愛技術交流與分享,對開源社區(qū)充滿熱情。同時也是一位騰訊云創(chuàng)作之星、阿里云專家博主、華為云云享專家、掘金優(yōu)秀作者。
? 我將不吝分享我在技術道路上的個人探索與經驗,希望能為你的學習與成長帶來一些啟發(fā)與幫助。
? 歡迎關注努力的小雨!?