hadoop--大數據生態圈中最基礎、最重要的組件

hadoop是什麼?

hadoop是一個由Apache基金會所開發的分佈式系統基礎架構,hdfs分佈式文件存儲、MapReduce並行計算。主要是用來解決海量數據的存儲和海量數據的分析計算問題,這是狹義上的hadoop。廣義上來說,hadoop一般指的是一個更普遍的概念--hadoop生態圈html

hadoop三大發行版本

hadoop三大發型版本:Apache、Cloudera、Hortonworksjava

  • Apache版本,也成爲社區版,是最原始的版本,對入門學習較好
  • Cloudera版本在大型互聯網企業中用的較多,是對社區版進行了封裝,主要是解決了和其它大數據組件(好比:hive)的兼容性問題。可是出了問題幫你解決要收費
  • Hortonworks版本文檔較好

公司通常更經常使用的是Cloudera版本的hadoop,另外Cloudera版本的hadoop也簡稱爲CDH。node

hadoop優點

1)高可靠性:hadoop底層維護多個數據副本,因此即便某個機器出現故障,也不會致使數據的丟失。python

好比我如今有1G的數據,要存在5臺機器上,hadoop默認會先將整個數據進行切割,默認是128M/塊,固然這個數值也能夠本身改。而後是以三副本進行存儲,也就是說每一個128M的塊,都會被存儲三份在不一樣的機器上。這樣即便一臺服務器宕機了,數據也不會丟失。mysql

2)高擴展性:在集羣間分配任務數據,可方便的擴展數以千計的節點。很好理解,若是容量不夠了,直接橫向擴展,加機器就行。linux

3)高效性:在MapReduce的思想下,hadoop是並行工做的,以加快任務處理速度。實際上若是學了spark,會發現hadoop本身所描述的易用性、高效性實在是不敢恭維哈。可是hadoop做爲大數據生態圈中很是重要的組件,咱們是有必要學好的,並且學了hadoop以後,再學spark會輕鬆不少。並且學習了hadoop,再學spark也會明白爲何spark會比hadoop在效率上高出幾十倍、甚至上百倍。git

4)高容錯性:可以自動將失敗的任務從新分配,若是某臺機器掛掉了,那麼會自動將任務分配到其餘的機器上執行github

hadoop 1.x和hadoop 2.x的區別

hadoop組成

hadoop的組成上面已經說了,主要由四部分組成,可是哪一個common,咱們通常不用管。所以從下往上只須要關注,hdfsyarnMapReduce便可。golang

hdfsweb

hdfs:hadoop distributed file system,hadoop分佈式文件系統,它由哪幾部分組成呢?

1.NameNode(nn):存儲文件的元數據,如文件名,文件目錄結構,文件屬性(生成時間、副本數、文件權限),以及每一個文件的塊列表和塊所在的DataNode等等。

2.DataNode(dn):真正用來存儲文件塊數據以及塊數據的校驗和,以前說了大文件是要分紅多塊的,每一塊存在不一樣的DataNode節點(說白了就是服務器、或者電腦)上,每個節點存儲了哪些文件、以及文件被切分了幾份都存在哪些DataNode上、文件名、屬性等等,這些都叫作元數據,統一交給NameNode管理,而DataNode只負責真正的存儲數據。

3. Secondary NameNode(2nn):用來監控hdfs狀態的後臺輔助程序,每一個一段時間獲取hdfs元數據的快照。

yarn

圖中有一個Resource Manager和三個Node Manager,至關於有四個節點。

Resource Manager

1.處理客戶端請求。客戶端想訪問集羣,好比提交一個做業,要通過Resource Manager,它是整個資源的管理者,管理整個集羣的CPU、內存、磁盤

2.監控Node Manager

3.啓動或監控Application Master

4.資源的分配和調度

Node Manager

1.管理單個節點上的資源,Node Manager是當前節點資源的管理者,固然須要跟Resource Manager彙報

2.處理來自Resource Manager的命令

3.處理來自Application Master的命令

Application Master

1.某個任務的管理者。當任務在Node Manager上運行的時候,就是由Application Master負責管理

2.負責數據的切分

3.爲應用程序申請資源並分配給內部的任務

4.任務的監控與容錯

Container

Container是yarn中資源的抽象,它封裝了節點上的多維度資源,如內存、CPU、磁盤、網絡等等。

其實Container是爲Application Master服務的,由於任務在運行的時候,是否是須要內存、cpu,這些資源都被虛擬化到Container裏面了。

MapReduce

1. map階段並行處理輸入數據

2. reduce階段對map結果進行彙總

幾張圖讓你理解hdfs工做原理(細節內容後面介紹,先看幾張漫畫感覺一下)

大數據技術生態體系

安裝

下面咱們來配置一下環境,個人全部大數據組件都安裝在/opt目錄下面

hadoop是java語言編寫的,所以須要安裝jdk,我這裏已經安裝了,安裝jdk很是簡單,能夠自行查找方法,若是還不會的話,那麼hadoop也能夠不用學了。

下面安裝hadoop,這個安裝java同樣簡單。這裏咱們使用社區版,直接去hadoop.apache.org網站下載便可,而後拷貝到個人阿里雲、解壓、配置環境變量便可。

而後在家目錄中輸入hdfs dfs,若是出現以下內容,證實安裝而且環境變量配置都沒問題

hadoop目錄結構

hadoop目錄結構以下,咱們依次來看。

bin目錄

bin目錄主要放一些有關服務的文件,好比hadoop、hdfs、yarn等等

etc目錄

裏面是有一個hadoop目錄,可是hadoop目錄裏面有不少配置文件,將來咱們會改大概七八個左右,固然不用怕

include目錄

這是與C語言有關的一些頭文件,咱們不用管

lib目錄

一些本地庫,.so文件,至關於windows的.dll文件,這個不須要關注

libexec目錄

和lib目錄相似

sbin目錄

很是重要的一個目錄,存放了大量的啓動文件,好比啓動、關閉集羣,啓動、關閉yarn等等

share目錄

存放了一些手冊、案例等等

僞分佈式環境搭建以及配置、啓動hdfs

hadoop的運行模式有三種,單機模式、僞分佈式、徹底分佈式。

  • 單機模式:基本不用,不用管
  • 僞分佈式:按照徹底分佈式來進行搭建、配置,可是機器只有一臺
  • 徹底分佈式:真正意義上的多臺機器

下面就來搭建僞分佈式環境,首先要修改配置文件,以前咱們說要修改七八個,可是目前先配三個就行,慢慢來,全部的配置文件都在hadoop安裝目錄/etc/hadoop下面

hadoop-env.xml

export JAVA_HOME=/opt/java/jdk1.8.0_221(默認是${JAVA_HOME},須要手動改爲java的安裝路徑)

core-site.xml

<!--指定hdfs中NameNode的地址-->
<!--這裏之因此是localhost,是由於咱們只有一臺機器,多臺機器就要換成相應的主機名-->
<property>
    <name>fs.defaultFS</name>
    <value>hdfs://localhost:9000</value>
</property>

<!--指定hadoop運行時產生文件的存儲目錄,若是不指定,那麼重啓以後就丟失了-->
<!--data目錄會自動建立-->
<property>
    <name>hadoop.tmp.dir</name>
    <value>/opt/hadoop/hadoop-2.7.7/data</value>
</property>

全部更改完的配置,都放在configuration標籤裏面

hdfs-site.xml

<!--指定hdfs副本的數量,默認是3,咱們是僞分佈式,因此改爲1-->
<property>
    <name>dfs.replication</name>
    <value>1</value>
</property>

配置完畢,下面啓動集羣(僞)

格式化namenode(第一次啓動時格式化,之後不須要總格式化)

hdfs namenode -format

查看以前的data目錄,自動幫咱們建立了,並且裏面也有東西了

啓動namenode

若是把sbin目錄也配置了環境變量,那麼sbin/也不須要加
sbin/hadoop-daemon.sh start namenode

啓動datanode

sbin/hadoop-daemon.sh start datanode

查看集羣是否啓動成功

輸入jps

注意jps是java的一個命令,必須安裝jdk以後纔可以使用,不然會提示命令未找到,出現以下內容表示安裝成功

輸入ip:50070

若是能訪問,也表示安裝成功

NameNode格式化注意事項

思考:爲何不能一直格式化NameNode,格式化NameNode須要注意什麼?

格式化NameNode會產生新的集羣id,致使NameNode和DataNode的集羣id不一致,集羣找不到以往的數據。因此格式化NameNode的時候,務必要先刪除data數據和log日誌,而後再格式化NameNode。由於二者須要有一個共同的id,這樣才能交互。

配置、啓動yarn

老規矩,先修改配置文件

yarn-env.sh

和hadoop-env.sh同樣,配置java的路徑
加上export JAVA_HOME=/opt/java/jdk1.8.0_221

yarn-site.xml

<!--reducer獲取數據的方式-->
<property>
    <name>yarn.nodemanager.aux-services</name>
    <value>mapreduce_shuffle</value>
</property>

<!--指定yarn的ResourceManager的地址-->
<property>
    <name>yarn.resourcemanager.hostname</name>
    <value>主機名</value>
</property>

mapred-env.sh

老規矩,遇到env.sh都是配JAVA_HOME

mapred-site.xml

可是會發現沒有這個文件,不過有一個mapred-site.xml.template
能夠cp mapred-site.xml.template mapred-site.xml

<!--指定MR運行在yarn上-->
<property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
</property>

修改完畢,下面啓動集羣

yarn-daemon.sh start resourcemanager
yarn-daemon.sh start nodemanager

經過網頁查看一下,輸入ip:8088,咱們以前好像輸入過50070,那個是查看hdfs的,8088是查看運行MapReduce程序的進程的。

配置運行歷史服務

有時候咱們想看一下程序的歷史運行狀況,那麼咱們能夠配置一下。

插一句,目前一直在搭建、配置環境,固然後面會詳細介紹hdfs、MapReduce的相關操做,目前先把環境打好。

固然若是對運行歷史不感興趣的話就跳過,固然最好仍是關注一下。

mapred-site.xml

<!--仍是這個配置文件,指定歷史服務端地址-->
<property>
    <name>mapreduce.jobhistory.address</name>
    <value>主機名:10020</value>
</property>

<!--歷史web端地址-->
<property>
    <name>mapreduce.jobhistory.webapp.address</name>
    <value>主機名:19888</value>
</property>

啓動歷史服務器:sbin/mr-jobhistory-daemon.sh start historyserver

jps查看歷史服務器,或者http://ip:19888/jobhistory

hdfs介紹

下面就開始着重講解hadoop組件之一的hdfs,以前的幾張漫畫只是提早感覺一下。還有目前咱們搭建的都是僞分佈式,至於徹底分佈式,因爲我阿里雲機器只有一臺因此就不演示怎麼搭建了。最主要的是筆者是python和golang系的,集羣的搭建什麼的應該用不到,環境什麼的會有其餘人負責,我只負責鏈接、計算什麼的。後面也會介紹如何使用python和golang鏈接hdfs,因此徹底分佈式如何搭建,這裏就不介紹了。

咱們下面的演示都是基於僞分佈式的

hdfs產生背景

隨着數據量愈來愈大,在一臺機器上沒法存下全部的數據,那麼就分配到更多的機器上,可是不方便管理和維護,所以迫切須要一種系統來管理多臺機器上的文件,這就是分佈式文件管理系統。hdfs只是分佈式文件管理系統中的一種

hdfs定義

hdfs(hadoop distributed file system),它是一個文件系統,用於存儲文件,經過目錄樹來定位文件;其次它是分佈式的,由不少服務器聯合起來實現其功能,集羣中的服務器有各自的角色

hdfs的使用場景

適合一次寫入,屢次讀出的場景,且不支持文件的修改。適合用來作數據分析,並不適合作網盤應用。

hdfs的優缺點

優勢:

  • 高容錯性
    • 數據自動保存多個副本,它經過增長副本的形式,提升容錯性
    • 某一個副本丟失後,它能夠自動恢復
  • 適合處理大數據
    • 數據規模:可以處理TB、甚至PB級別的數據
    • 文件規模:可以處理百萬規模以上的文件數量,數量至關之大
  • 可構建在廉價的機器之上,經過多副本機制,提升可靠性

缺點:

  • 不適合低延時數據訪問,若是你想作到毫秒級存儲,別想了,作不到的

  • 沒法高效地對大量小文件進行存儲,存一個1G的數據比存10個100MB加上一個24MB的數據要高效不少

    至於爲何,由於NameNode是否是惟一的啊,這就意味着空間是有限的,不可能像DataNode同樣,容量不夠了就加機器。而NameNode要記錄文件的元數據,無論你是1KB,仍是1GB,都須要150字節的空間進行記錄,若是全是小文件的話,是否是很耗費NameNode所在機器的空間呢?

    並且小文件存儲的尋址時間會超過讀取時間,它違反了hdfs的設計目標。

  • 不支持併發寫入、文件隨機修改

    一個文件只能有一個寫,不容許多個線程同時寫

    僅支持數據的append,不支持文件的隨機修改

hdfs的架構

先看官網給的一張圖

NameNode(nn):就是master,它是一個主管、管理者

  • 管理hdfs的名稱空間
  • 配置副本策略
  • 管理數據塊(block)映射信息
  • 處理客戶端讀寫請求

DataNode(nn):就是slave,NameNode下達命令,DataNode執行實際的操做

  • 存儲實際的數據塊
  • 執行數據塊的讀/寫操做

client:就是客戶端

  • 文件切分,文件上傳到hdfs的時候,client將文件切分紅一個個的block,而後上傳
  • 與NameNode交互,獲取文件的位置信息
  • 與DataNode交互,讀取或者寫入數據
  • 客戶端提供一些命令來管理hdfs,好比NameNode的格式化
  • 客戶端能夠經過一些命令來訪問hdfs,好比對hdfs的增刪改查操做

secondary NameNode:它不是NameNode的替補,當NameNode掛掉時,並不能立刻替換NameNode並提供服務。

  • 輔助NameNode,分擔其工做量,好比按期合併Fsimage和Edits,並推送給NameNode
  • 緊急狀況下,可輔助恢復NameNode(能夠恢復一部分)

強烈建議結合以前的漫畫來理解,會有更好的體驗

hdfs塊的大小設置

hdfs中的文件在物理上是分塊存儲(block),塊的大小能夠經過配置參數(df.blocksize)指定,默認大小在hadoop 2.x版本中是128M,老版本中是64M

思考:爲何塊不能設置過小,也不能設置太大?

1.hdfs塊設置過小,會增長尋址時間,程序一直在找塊的開始位置

2.hdfs塊設置太大,從磁盤傳輸的時間會明顯大於定位這個塊的開始位置所須要的時間。致使程序在處理這塊數據時,會很是慢。

總結:hdfs塊的大小設置主要取決於磁盤的傳輸速率

hdfs shell命令(重點)

基本語法:hdfs dfs 命令 參數

這個hdfs的shell命令和linux是很是相似的,好比查看某個目錄下的文件,linux中是ls,那麼hdfs shell中就是hdfs dfs -ls,查看文件內容,hdfs dfs -cat filename,能夠看到是很是類似的,只不過在hdfs shell中須要加上一個橫槓。另外hdfs dfs還能夠寫成hadoop fs,對於shell操做來講二者區別不大

啓動集羣

sbin/start-dfs.sh sbin/start-yarn.sh

以前咱們說過hadoop-daemon.sh和yarn-daemon.sh,那是對於僞分佈式也就是單節點來講的,若是是啓動集羣須要使用sbin/start-dfs.sh sbin/start-yarn.sh

hdfs dfs -help 命令

從linux命令也能看出來,這是一個查看命令使用方法的命令

hdfs dfs -ls 目錄路徑

查看某個目錄有哪些文件,加上-R表示遞歸顯示

hdfs dfs -mkdir 目錄

在hdfs上面建立目錄,加上-p表示遞歸建立,和linux是同樣的

hdfs dfs -moveFromLocal 本地路徑 hdfs路徑

將本地文件或目錄移動到hdfs上面,注意是移動,移完以後本地就沒了

hdfs dfs -cat 文件

查看一個文件的內容

hdfs dfs -appendToFile 追加的文件 追加到哪一個文件

將一個文件的內容追加到另外一個文件裏面去,好比本地有一個file.txt,那麼hdfs dfs -appendToFile file.txt /a.txt表示將本地的file.txt文件裏面的內容追加到hdfs上的/a.txt文件裏面去

-chgrp、-chmod、-chown

更改組、更改權限、更改全部者,這個和linux中用法同樣

hdfs dfs -copyFromLocal 本地路徑 hdfs路徑

將文件從本地拷貝到hdfs上面去,這個和剛纔moveFromLocal就相似於linux中cp和mv

hdfs dfs -copyToLocal hdfs路徑 本地路徑

將hdfs上的文件拷貝到本地,這個路徑是hdfs路徑在前、本地路徑在後。

hdfs dfs -cp hdfs路徑 hdfs路徑

copyFromLocal是針對本地和hdfs來講了,cp是hdfs路徑和hdfs路徑之間的拷貝

hdfs dfs -mv hdfs路徑 hdfs路徑

不用說也能明白

hdfs dfs -get hdfs路徑 本地路徑

等同於copyToLocal

hdfs dfs -put 本地路徑 hdfs路徑

等同於copyFromLocal

hdfs dfs -getmerge hdfs路徑(通配符) 本地路徑

將hdfs上面的多個文件合併下載到本地

hdfs dfs -tail 文件名

顯示文件的結尾

hdfs dfs -rm 文件

刪除文件,若是是文件夾須要加上-r

hdfs dfs -rmdir 空目錄

刪除一個空目錄,不經常使用,通常使用-rm

hdfs dfs -du 目錄

統計目錄的大小信息

hdfs dfs -du -h /:加上-h人性化顯示

hdfs dfs -du -h -s / :查看當前目錄的總大小

hdfs dfs -setrep 數值 文件

設置文件的副本數量,hdfs dfs -setrep 5 /file.txt:表示將file.txt的副本設置成5

python鏈接hdfs進行相關操做

下面咱們來介紹如何使用python操做hdfs,首先python若想操做hdfs,須要下載一個第三方庫,也叫hdfs,直接pip install hdfs便可。

import hdfs
from pprint import pprint

# 導入相關模塊,輸入http://ip:50070,建立客戶端
client = hdfs.Client("http://ip:50070")

client.list:查看當前目錄的內容

print(client.list("/"))  # ['黑色相簿.txt', 'a.txt', 'b.txt', 'test']

# status默認爲False,表示是否顯示文件的相關屬性
# 返回數據的格式爲:[("", {}), ("", {}), ("", {}), ...]
pprint(client.list("/", status=True))
"""
[('黑色相簿.txt',
  {'accessTime': 1570347361399,
   'blockSize': 134217728,
   'childrenNum': 0,
   'fileId': 16393,
   'group': 'supergroup',
   'length': 0,
   'modificationTime': 1570343722271,
   'owner': 'root',
   'pathSuffix': '黑色相簿.txt',
   'permission': '644',
   'replication': 1,
   'storagePolicy': 0,
   'type': 'FILE'}),
 ('a.txt',
  {'accessTime': 1570347222071,
   'blockSize': 134217728,
   'childrenNum': 0,
   'fileId': 16386,
   'group': 'supergroup',
   'length': 26,
   'modificationTime': 1570344172155,
   'owner': 'root',
   'pathSuffix': 'a.txt',
   'permission': '755',
   'replication': 1,
   'storagePolicy': 0,
   'type': 'FILE'}),
 ('b.txt',
  {'accessTime': 1570347263315,
   'blockSize': 134217728,
   'childrenNum': 0,
   'fileId': 16396,
   'group': 'supergroup',
   'length': 10,
   'modificationTime': 1570347263628,
   'owner': 'root',
   'pathSuffix': 'b.txt',
   'permission': '644',
   'replication': 1,
   'storagePolicy': 0,
   'type': 'FILE'}),
 ('test',
  {'accessTime': 0,
   'blockSize': 0,
   'childrenNum': 2,
   'fileId': 16387,
   'group': 'supergroup',
   'length': 0,
   'modificationTime': 1570346913737,
   'owner': 'root',
   'pathSuffix': 'test',
   'permission': '755',
   'replication': 0,
   'storagePolicy': 0,
   'type': 'DIRECTORY'})]
"""

client.status:獲取指定路徑的狀態信息

pprint(client.status("/"))
"""
{'accessTime': 0,
 'blockSize': 0,
 'childrenNum': 4,
 'fileId': 16385,
 'group': 'supergroup',
 'length': 0,
 'modificationTime': 1570347630036,
 'owner': 'root',
 'pathSuffix': '',
 'permission': '755',
 'replication': 0,
 'storagePolicy': 0,
 'type': 'DIRECTORY'}
"""

# 裏面還有一個strict=True,表示嚴格模式
# 若是改成False,那麼若是輸入的路徑不存在就返回None
# 爲True的話,路徑不存在,報錯

client.makedirs:建立目錄

print(client.list("/"))  # ['黑色相簿.txt', 'a.txt', 'b.txt', 'test']
# 會自動遞歸建立,若是想建立的時候給目錄賦予權限,可使用permission參數,默認爲None
client.makedirs("/a/b/c", permission=777)
print(client.list("/"))  # ['黑色相簿.txt', 'a', 'a.txt', 'b.txt', 'test']
print(client.list("/a"))  # ['b']
print(client.list("/a/b"))  # ['c']

client.rename:重命名

print(client.list("/"))  # ['黑色相簿.txt', 'a', 'a.txt', 'b.txt', 'test']
client.rename("/黑色相簿.txt", "/白色相簿")
print(client.list("/"))  # ['白色相簿', 'a', 'a.txt', 'b.txt', 'test']

client.write:往文件裏面寫內容

client.read:往文件裏面讀內容

關於寫、讀、上傳、下載,若是報錯,出現了requests.exceptions.ConnectionError:xxxxx,那麼解決辦法就是在你當前使用python的Windows機器上的hosts文件中增長以下內容:部署hadoop的服務器ip 部署hadoop的服務器主機名

若是出現了hdfs.util.HdfsError: Permission denied: user=dr.who, access=WRITE,······異常,那麼須要在hdfs-site.xml中加入以下內容

<property>
  <name>dfs.permissions</name>
  <value>false</value>
</property>

下面就開始寫數據、讀數據

with client.write("/這是一個不存在的文件.txt") as writer:
    # 須要傳入字節
    writer.write(bytes("this file not exists", encoding="utf-8"))


with client.read("/這是一個不存在的文件.txt") as reader:
    # 讀取出來也是字節類型
    print(reader.read())  # b'this file not exists'

write方法,若是不指定額外的參數,那麼須要文件不能存在,不然會報錯,提示文件已經存在。若是要對已存在的文件進行操做,那麼須要顯式的指定參數:overwrite(重寫)或者append(追加)

with client.write("/白色相簿", append=True) as writer:
    writer.write(bytes("使人討厭的冬天又來了,", encoding="utf-8"))


with client.read("/白色相簿") as reader:
    print(str(reader.read(), encoding="utf-8"))  # 使人討厭的冬天又來了,
with client.write("/白色相簿", append=True) as writer:
    writer.write(bytes("冬天的街道,戀人們的微笑,讓人想一把火全燒了", encoding="utf-8"))


with client.read("/白色相簿") as reader:
    # 因爲是追加,以前的內容也讀取出來了
    print(str(reader.read(), encoding="utf-8"))  # 使人討厭的冬天又來了,冬天的街道,戀人們的微笑,讓人想一把火全燒了
with client.write("/白色相簿", overwrite=True) as writer:
    writer.write(bytes("暖かい日差しが降り注いできて、眩しすぎ、目が見えない", encoding="utf-8"))


with client.read("/白色相簿") as reader:
    # 若是是overwrite,那麼以前的內容就全沒了
    print(str(reader.read(), encoding="utf-8"))  # 暖かい日差しが降り注いできて、眩しすぎ、目が見えない

注意:overwrite和append不能同時出現,不然報錯

# 若是write裏面傳入了encoding參數,那麼writer.write則須要寫入str,由於會自動按照傳入的encoding進行編碼
with client.write("/白色相簿", overwrite=True, encoding="utf-8") as writer:
    girls = ["古明地覺", "古明地戀", "八重櫻"]
    writer.write(str(girls))


# 同理若是傳入了encoding參數,reader.read會讀出str,由於會自動按照傳入的encoding進行解碼
with client.read("/白色相簿", encoding="utf-8") as reader:
    print(reader.read())  # ['古明地覺', '古明地戀', '八重櫻']

client.content:查看目錄的彙總狀況

好比:當前目錄下有多少個子目錄、多少文件等等

print(client.content("/", strict=True))
# {'directoryCount': 6, 'fileCount': 6, 'length': 95, 'quota': 9223372036854775807, 'spaceConsumed': 95, 'spaceQuota': -1}

client.set_owner:設置全部者

client.set_permission:設置權限

client.set_replication:設置副本系數

client.set_times:設置時間

"""
def set_owner(self, hdfs_path, owner=None, group=None):
def set_permission(self, hdfs_path, permission):
def set_replication(self, hdfs_path, replication):
def set_times(self, hdfs_path, access_time=None, modification_time=None):
"""

client.resolve: 將帶有符號的路徑,轉換成絕對、規範化路徑

# 固然並不要求路徑真實存在
print(client.resolve("/白色相簿/白色相簿/.."))  # /白色相簿

client.walk:遞歸遍歷目錄

# 遞歸遍歷文件,相似於os.walk,會返回一個生成器,能夠進行迭代
# 每一步迭代的內容是一個三元組,("路徑", ["目錄1", "目錄2"], ["文件1", "文件2", "文件3"])
for file in client.walk("/"):
    print(file)
"""
('/', ['a', 'test'], ['白色相簿', '這是一個不存在的文件.txt', 'a.txt', 'b.txt'])
('/a', ['b'], [])
('/a/b', ['c'], [])
('/a/b/c', [], [])
('/test', ['test1'], ['黑色相簿.txt'])
('/test/test1', [], ['b.txt'])
"""

client.upload:上傳文件

print("2.py" in client.list("/"))  # False
client.upload(hdfs_path="/", local_path="2.py")
print("2.py" in client.list("/"))  # True

client.download:下載文件

client.download(hdfs_path="/白色相簿", local_path="白色相簿")
print(open("白色相簿", "r", encoding="utf-8").read())  # ['古明地覺', '古明地戀', '八重櫻']

client.checksum:獲取文件的校驗和

# 獲取文件的校驗和
print(client.checksum("/白色相簿"))
# {'algorithm': 'MD5-of-0MD5-of-512CRC32C', 'bytes': '00000200000000000000000095b1c9929656ce2b779093c67c95b76000000000', 'length': 28}

client.delete:刪除文件或目錄

# recursive表示是否遞歸刪除,默認爲False
try:
    client.delete("/test")
except Exception as e:
    print(e)  # `/test is non empty': Directory is not empty

print("test" in client.list("/"))  # True
client.delete("/test", recursive=True)
print("test" in client.list("/"))  # False

golang操做hdfs

golang鏈接hdfs一樣須要一個第三方驅動,golang鏈接hdfs的驅動推薦兩個,一個也叫hdfs,另外一個叫gowfs。先來看看怎麼安裝。

安裝hdfs稍微有點費勁,首先能夠經過go get github.com/colinmarc/hdfs下載,由於總所周知的緣由,不出意外會失敗,報出以下錯誤

那麼咱們須要先在gopath(第三方包安裝的位置,通常是進入C盤,點擊用戶,再點擊你的用戶名對應的目錄,而後就會看到一個go目錄,我這裏是C:\Users\satori\go)的src目錄下新建golang.org/x目錄,而後進入到golang.org/x下,在此處打開命令窗口,而後執行git clone https://github.com/golang/crypto

而後再執行go get github.com/colinmarc/hdfs就沒問題了

至於安裝gowfs就簡單多了,直接go get github.com/vladimirvivien/gowfs就沒問題了

可是咱們使用哪個呢?我的推薦使用gowfs,下面咱們就來看看怎麼用。

讀取文件

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
    "io/ioutil"
)

func main() {
    //這是配置,傳入Addr: "ip: 50070", User: "隨便寫一個英文名就行"
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    //返回一個客戶端(這裏面叫文件系統)和error
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:",err))
    }
    
    //這裏不能直接傳入文件名,而是須要做爲gowfs.Path結構體的Name參數的值
    //而後將Path傳進去,咱們後面的api都是這樣作的
    path := gowfs.Path{Name:"/whitealbum.txt"}
    
    //接收以下參數:gowfs.Path,offset(偏移量),長度(顯然是字節的長度), 容量(本身的cap)
    //返回一個io.ReadCloser,這是須要實現io.Reader和io.Closer的接口
    reader, _ := client.Open(path, 0, 512, 2048)
    
    //可使用reader.Read(buf)的方式循環讀取,也能夠丟給ioutil。ReadAll,一次性所有讀取
    data, _ := ioutil.ReadAll(reader)
    fmt.Println(string(data))
    /*
    白色相簿什麼的,已經無所謂了。
    由於已經再也不有歌,值得去唱了。
    傳達不了的戀情,已經不須要了。
    由於已經再也不有人,值得去愛了。
     */
}

查看目錄有哪些內容

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/"}
    // 返回[]FileStatus和error
    //這個FileStatus是什麼?咱們看一下源碼
    /*
        type FileStatus struct {
            AccesTime        int64  訪問時間
            BlockSize        int64  塊大小,只針對文件(134217728 Bytes,128 MB),目錄的話爲0
            Group            string 所屬組
            Length           int64  文件的字節數(目錄爲0)
            ModificationTime int64  修改時間
            Owner            string 全部者
            PathSuffix       string 文件後綴,說白了就是文件名
            Permission       string 權限
            Replication      int64  副本數
            Type             string 類型,文本的話是FILE,目錄的話是DIRECTORY
        }
    */
    fs_arr, _ := client.ListStatus(path)
    fmt.Println(fs_arr)
    // [{0 0 supergroup 0 1570359570447 dr.who tkinter 755 0 DIRECTORY} {0 134217728 supergroup 184 1570359155457 root whitealbum.txt 644 1 FILE}]

    for _, fs := range fs_arr {
        fmt.Println("文件名:", fs.PathSuffix)
        /*
        文件名: tkinter
        文件名: whitealbum.txt
         */
    }
    
    //FileStatus裏面包含了文件的詳細信息,若是想查看某個文件的詳細信息
    //可使用fs, err := client.GetFileStatus(path)
}

建立文件

package main

import (
    "bytes"
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/黑色相簿.txt"}

    /*
    Create函數接收以下參數。
    data:io.Reader,一個實現了io.Reader接口的struct
    Path:很簡單,就是咱們這裏的path
    overwrite:是否覆蓋,若是爲false表示不覆蓋,那麼要求文件不能存在,不然報錯
    blocksize:塊大小
    replication:副本
    permission:權限
    buffersize:緩存大小
    contenttype:內容類型

    返回一個bool和error
     */
    if flag, err :=client.Create(
        bytes.NewBufferString("這是黑色相簿,不是白色相簿"), //若是不指定內容,就直接bytes.NewBufferString()便可
        path, //路徑
        false,//不覆蓋
        0,
        0,
        0666,
        0,
        "text/html",  //純文本格式
        ); err != nil {
            fmt.Println("建立文件出錯,錯誤爲:", err)
    } else {
        fmt.Println("建立文件成功, flag =", flag)  //建立文件成功, flag = true
    }
}

查看一下

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
    "io/ioutil"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/黑色相簿.txt"}

    reader , _ := client.Open(path, 0, 512, 2048)
    data, _ := ioutil.ReadAll(reader)
    fmt.Println(string(data)) // 這是黑色相簿,不是白色相簿
}

建立目錄

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/a/b/c"}
    
    //遞歸建立
    flag, err := client.MkDirs(path, 0666)
    fmt.Println(flag) // true

    fs_arr, _ := client.ListStatus(gowfs.Path{Name:"/"})
    for _, fs := range fs_arr{
        fmt.Println(fs.PathSuffix)
        /*
        黑色相簿.txt
        a
        tkinter
        whitealbum.txt
         */
    }
}

重命名

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    fs_arr, _ := client.ListStatus(gowfs.Path{Name:"/"})
    for _, fs := range fs_arr{
        fmt.Println(fs.PathSuffix)
        /*
        黑色相簿.txt
        a
        tkinter
        whitealbum.txt
         */
    }

    flag, err := client.Rename(gowfs.Path{Name:"/黑色相簿.txt"}, gowfs.Path{Name:"/blackalbum.txt"})
    fmt.Println(flag) // true
    fs_arr, _ = client.ListStatus(gowfs.Path{Name:"/"})
    for _, fs := range fs_arr{
        fmt.Println(fs.PathSuffix)
        /*
            a
            blackalbum.txt
            tkinter
            whitealbum.txt
        */
    }
}

向已經存在的文件追加內容

package main

import (
    "bytes"
    "fmt"
    "github.com/vladimirvivien/gowfs"
    "io/ioutil"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/whitealbum.txt"}
    reader, _ := client.Open(path, 0, 512, 2048)
    data, _ := ioutil.ReadAll(reader)
    fmt.Println(string(data))
    /*
    白色相簿什麼的,已經無所謂了。
    由於已經再也不有歌,值得去唱了。
    傳達不了的戀情,已經不須要了。
    由於已經再也不有人,值得去愛了。
     */

    //參數1:內容,必須是實現了io.Reader接口
    //參數2:路徑
    //參數3:緩存大小
    flag, err := client.Append(bytes.NewBufferString("\n讓人討厭的冬天又來了"), path, 2048)
    fmt.Println(flag) // true

    reader, _ = client.Open(path, 0, 512, 2048)
    data, _ = ioutil.ReadAll(reader)
    fmt.Println(string(data))
    /*
    白色相簿什麼的,已經無所謂了。
    由於已經再也不有歌,值得去唱了。
    傳達不了的戀情,已經不須要了。
    由於已經再也不有人,值得去愛了。
    
    讓人討厭的冬天又來了。
     */
}

設置文件或目錄的全部者

func (fs *FileSystem) SetOwner(path Path, owner string, group string) (bool, error)

設置文件或目錄的權限

func (fs *FileSystem) SetPermission(path Path, permission os.FileMode) (bool, error)

設置文件或目錄的副本系數

func (fs *FileSystem) SetReplication(path Path, replication uint16) (bool, error)

設置文件或目錄的訪問時間和修改時間

func (fs *FileSystem) SetTimes(path Path, accesstime int64, modificationtime int64) (bool, error)

獲取文件的校驗和

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name: "/whitealbum.txt"}
    f, _ := client.GetFileChecksum(path)
    fmt.Println(f)  // {MD5-of-0MD5-of-512CRC32C 0000020000000000000000001255073187d3e801940eee180acebe4e00000000 28}
    fmt.Println(f.Algorithm, f.Length, f.Bytes) // MD5-of-0MD5-of-512CRC32C 28 0000020000000000000000001255073187d3e801940eee180acebe4e00000000
}

刪除文件

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    path := gowfs.Path{Name:"/blackalbum.txt"}
    //路徑,是否遞歸
    flag, _ := client.Delete(path, true)
    fmt.Println(flag) // true
}

判斷文件是否存在

爲何這裏用藍色了,由於以後的用法就不同了

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, err := gowfs.NewFileSystem(config)
    if err != nil {
        panic(fmt.Sprintln("出現異常,異常信息爲:", err))
    }

    //建立一個shell,可使用下面shell進行操做
    shell := gowfs.FsShell{FileSystem: client}

    //直接傳字符串便可,不須要傳Path了
    flag, _ := shell.Exists("/whitealbum.txt")
    fmt.Println(flag) // true
    flag, _ = shell.Exists("/whitealbum.txt1")
    fmt.Println(flag) // false
}

改變全部者

flag, _ := shell.Chown([]string{"/file1", "/file2", "/file3"}, "owner")

改變所屬組

flag, _ := shell.Chgrp([]string{"/file1", "/file2", "/file3"}, "groupName")

改變權限

flag, _ := shell.Chmod([]string{"/file1", "/file2", "/file3"}, 0666)

查看文件內容

package main

import (
    "bytes"
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, _ := gowfs.NewFileSystem(config)

    shell := gowfs.FsShell{FileSystem: client}

    buf := bytes.Buffer{}
    if err := shell.Cat([]string{"/whitealbum.txt"}, &buf); err != nil {
        fmt.Println("err =", err)
    } else {
        fmt.Println(buf.String())
        /*
            白色相簿什麼的,已經無所謂了。
            由於已經再也不有歌,值得去唱了。
            傳達不了的戀情,已經不須要了。
            由於已經再也不有人,值得去愛了。

            讓人討厭的冬天又來了
        */
    }
}

追加文件內容

package main

import (
    "bytes"
    "fmt"
    "github.com/vladimirvivien/gowfs"
    "io/ioutil"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, _ := gowfs.NewFileSystem(config)

    shell := gowfs.FsShell{FileSystem: client}

    _ = ioutil.WriteFile("aaa.txt", []byte("\n冬馬小三\n"), 0666)
    _ = ioutil.WriteFile("bbb.txt", []byte("雪菜碧池\n"), 0666)
    _ = ioutil.WriteFile("ccc.txt", []byte("打死春哥\n"), 0666)
    _ = ioutil.WriteFile("ddd.txt", []byte("抱走冬馬雪菜"), 0666)

    _, _ = shell.AppendToFile([]string{"aaa.txt", "bbb.txt", "ccc.txt", "ddd.txt"}, "/whitealbum.txt")

    buf := bytes.Buffer{}
    _ = shell.Cat([]string{"/whitealbum.txt"}, &buf)
    fmt.Println(buf.String())
    /*
        白色相簿什麼的,已經無所謂了。
        由於已經再也不有歌,值得去唱了。
        傳達不了的戀情,已經不須要了。
        由於已經再也不有人,值得去愛了。

        讓人討厭的冬天又來了
        冬馬小三
        雪菜碧池
        打死春哥
        抱走冬馬雪菜
    */
}

上傳文件

package main

import (
    "fmt"
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, _ := gowfs.NewFileSystem(config)

    shell := gowfs.FsShell{FileSystem: client}

    //本地路徑,hdfs路徑,是否重寫
    _, _ = shell.Put("aaa.txt", "/aaa.txt", false)

    path := gowfs.Path{Name: "/"}
    fs_arr, _ := client.ListStatus(path)
    for _, fs := range fs_arr {
        fmt.Println(fs.PathSuffix)
        /*
            黑色相簿.txt
            a
            aaa.txt
            tkinter
            whitealbum.txt
        */
    }
}

下載文件

package main

import (
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, _ := gowfs.NewFileSystem(config)

    shell := gowfs.FsShell{FileSystem: client}

    _, _ = shell.Get("/whitealbum.txt", "白色album.txt")
}

刪除文件

package main

import (
    "github.com/vladimirvivien/gowfs"
)

func main() {
    config := gowfs.Configuration{Addr: "xx.xx.xx.xx:50070", User: "satori"}
    client, _ := gowfs.NewFileSystem(config)

    shell := gowfs.FsShell{FileSystem: client}

    _, _ = shell.Rm("/whitealbum.txt")
}

MapReduce定義

MapReduce是一個分佈式運算程序的編程框架,是用戶開發"基於hadoop的數據分析應用"的核心框架

MapReduce的核心功能是將用戶編寫的業務邏輯代碼自帶默認組件組合成一個完整的分佈式運算程序,併發運行在一個hadoop集羣上。

MapReduce優缺點

優勢

  • MapReduce易於編程

    它簡單地實現一些接口,就能夠完成一個分佈式應用程序,這個分佈式應用程序能夠分佈到大量廉價的pc機器上運行。也就是說,你寫一個分佈式應用程序,跟寫一個簡單的串行程序是如出一轍的。就是由於這個特色,使得MapReduce編程很是流行

  • 良好的擴展性

    當你的計算資源不足時,你能夠經過簡單的增長機器來擴展計算能力

  • 高容錯性

    MapReduce設計的初衷就是使程序可以運行在廉價的PC機器上,這就要求它具備很高的容錯性。好比其中一臺機器掛了,它能夠把上面的計算任務轉移到另外一個節點上運行,不至於這個任務徹底失敗。並且這個過程不須要人工參與,是由hadoop內部完成的。

  • 適合PB級以上海量數據的離線處理

    能夠實現上千臺服務器集羣併發工做,提升數據處理能力

缺點

  • 不擅長實時計算

    MapReduce沒法像mysql同樣,能夠在毫秒級或者秒級內返回結果

  • 不擅長流式計算

    流式計算輸入的數據是動態的,而MapReduce的數據數據必須是靜態的,不能動態變化。這是由於MapReduce自身的設計特色決定了數據源必須是靜態的

  • 不支持DAG(有向無環圖)計算

    多個程序之間存在依賴,後一個應用程序的輸入依賴於上一個程序的輸出。在這種狀況,MapReduce不是不能作,而是使用後,每一個MapReduce做業的輸出結果都會寫入到磁盤,而後再從磁盤中讀取,進行下一個操做,這樣作會形成大量的磁盤IO,致使性能很是的低下。

相關文章
相關標籤/搜索