2010年1月23日 星期六

Google Maps with Street View - 台灣街景

GoogleStreetView
原始連結


看來是非常認真的鄉民,跟在 Google Street View 拍攝的車子後面比了 Y 啦!不過這也代表 Google Maps 服務越來越深入台灣了!原先只有台北市,現在許多地方也慢慢補上囉


未來能應用的角度將越來越豐富,例如統整小吃位置等,甚至只需看著 Street View 上的招牌打電話聯絡!未來若 3G 或 WiMax 上網費率能夠越來越平民化,那生活就會越來越便利囉!


2010年1月22日 星期五

成長營








為了避免厲害的 Search Engine,只好偷偷地更名啦。成長營這種東西,高一參加過一次,若沒記錯應該也是寒假的時段,恰好跟這次稍稍地呼應。當時是辦來讓高中同學相互認識,甚至在男女分班的情況下,還有機會跟女生跳舞,沒記錯的話,就是那首呢喃。


這次的成長營,讓我收穫良多,盡管還是沒有接觸到太多太廣的人面,但透過活動,甚至吃飯的時候閒聊,我發現有許多地方還待發展的部分。記得有不少故事是那般地道述,想要成功就得在各個單位待一下,但如果已在某單位獲得不錯的成果或戰績時,又有多少人肯放下身段下放到其他單位呢?


很巧地在一次晚餐的聚會中,碰到從事產業業務相關的工作者,我很感興趣地是要甚麼樣的背景才適合,畢竟那不像是現在大專院校的各大系名,並沒有明確的資格定義,究竟要怎樣挑選負責的人才呢?盡管這個疑問並未解決,但似乎也不必急著解決,就像各大公司的業務,不就肯做就行了?


另外,在結訓前一晚的創意競賽中,恰好整隊就只有我跟同事是從事技術研究的部分,我發現自己經驗太窄,不像業務部門那樣輕鬆地拉廣眼界,每次思考一些東西時,完全地想要控制實作性,所以提出的架構是完全可以實作的,甚至已經變成整合型服務,更讓我想起填寫工作計畫書時,往往填寫的部分早已知道怎樣做了!那這樣未來又怎樣能跳得更遠呢?在這樣思考模式中,很容易就安靜,不想多說,因為想不到了?或許更因為技術的背景局限自己呢?這的確需要保留更多的赤子之心,多看看,多做夢啊。比較好玩的,不曉得是不是陽盛陰衰,還是裡頭太多當完兵?一堆題目都充滿 18 禁的話題,甚至一直圍繞在「抓猴」的話題,囧


說到這也會讓我感受到我們這組異性的相處還滿自然的,可能是其他人本來就認識,或是熟女、已婚了吧?當某個男生講出兩性話題時,有位女性夥伴會直接回他「低級!」,可能對我求學環境幾乎是男女分班的,突然間感到這樣的互動是多麼地自然,有點享受在這樣的氣氛中,實在太難得了!除此之外,原先打算跟同事住在一間晚上可以聊聊天,但不預期地就是被分開,想去問換房間的事,還被工作人員誤會說要睡在同一張床,真是給他誇張了點,最後則是因為對方的手機未開機,就只好享受這意外的安排吧!


未來啊?我的目標尚未明確,或許我待的單位就是會一直如此地下去,希望自己能夠更快地適應這種模式吧,是拓荒者,更是敢死隊啊。


2010年1月20日 星期三

HadoopDB Join Testing on 3-Node Cluster

在還沒有搞清楚 Hive 以前,一直以為 HadoopDB 底部用 Databases 會有所限制,例如有 A, B 和 C 三台電腦構成的 Cluster ,若分別在 A, B 和 C 上各別建立資料庫以及 T1 跟 T2 兩個 Table, 當我透過 Hive 進行 Join 的查詢時,會不會因為資料不在同一個資料料庫裡而查不到呢?或是 Table 不在同一個資料庫裡就無法  Join 呢?如果你已經讀過 Hive 的設計架構,那肯定很清楚在使用 Hive on Hadoop 時,並不需要擔心這些事。


這個實驗很簡單,就只是要測試 HadoopDB 是否真的能提供 Join 功能,這是一開始上司丟給我的問題。當時我還不了解 Hive 因此也稍微存疑。了解 Hive 後,方知這些問題是由 Hive 解決,當然 HadoopDB 就不會碰到一樣的問題啦。但還是花一點點時間把實驗作完囉!


實驗設計:



  • 使用 3-node cluster ,分別是 Cluster01 , Cluster02 和 Cluster03

  • 準備測資 d1 和 d2 以及分別建立 table 的欄位資訊

    • d1_field_definition (t1, table 1)

      • ID int,
        NAME varchar(300)



    • d1

      • 1       A
        2       B
        3       C
        4       D
        5       E
        6       F
        7       G
        8       H
        9       I



    • d2_field_definition (t2 , table 2 )


      • ID int,
        ADDRESS varchar(300)



    • d2

      • 9       I_address
        8       H_address
        7       G_address
        6       F_address
        5       E_address
        4       D_address
        3       C_address
        2       B_address
        1       A_address





  • 一支幫你方便架設 HadoopDB 的 script




實驗過程與結果



  1. 以下指令全部皆在 Cluster01 這台機器的家目錄執行

  2. 將 d1 資料分割 3 份,以及匯入各台機器和建立 Hive 的 Table

    • $ python batch_setup.py -H d1 -t t1 -i t1 -g

    • 備份 HadoopDB.xml 等會要用


      • $ cp HadoopDB.xml HadoopDB_t1.xml



    • 使用 Hive 驗證

      • $ ./SMS_dist/bin/hive
        hive> show tables;
        OK
        t1
        Time taken: 8.772 seconds
        hive> select id,name from t1;
        Total MapReduce jobs = 1
        Number of reduce tasks is set to 0 since there's no reduce operator
        Starting Job = job_201001201134_0008, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0008
        Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0008
        2010-01-20 02:12:42,545 map = 0%,  reduce =0%
        2010-01-20 02:12:50,659 map = 33%,  reduce =0%
        2010-01-20 02:12:56,828 map = 67%,  reduce =0%
        2010-01-20 02:12:57,855 map = 100%,  reduce =0%
        Ended Job = job_201001201134_0008
        OK
        3       C
        6       F
        9       I
        2       B
        5       E
        8       H
        1       A
        4       D
        7       G
        Time taken: 22.447 seconds
        hive> quit;

      • 驗證完記得離開,因為 hive 預設只能一支 client 跟它 query,不 quit 會影響 batch_setup.py 的設定





  3. 將 d2 資料分割 3 份,以及匯入各台機器和建立 Hive 的 Table

    • $ python batch_setup.py -H d2 -t t2 -i t2 -g

    • 備份 HadoopDB.xml 等會要用

      • $ cp HadoopDB.xml HadoopDB_t2.xml



    • 使用 Hive 驗證

      • $ ./SMS_dist/bin/hive
        hive> show tables;
        OK
        t1
        t2
        Time taken: 9.313 seconds
        hive> select id,address from t2;
        Total MapReduce jobs = 1
        Number of reduce tasks is set to 0 since there's no reduce operator
        Starting Job = job_201001201134_0010, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0010
        Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0010
        2010-01-20 02:16:45,971 map = 0%,  reduce =0%
        2010-01-20 02:16:57,115 map = 33%,  reduce =0%
        2010-01-20 02:17:03,240 map = 67%,  reduce =0%
        2010-01-20 02:17:04,283 map = 100%,  reduce =0%
        Ended Job = job_201001201134_0010
        OK
        9       I_address
        8       H_address
        5       E_address
        4       D_address
        3       C_address
        7       G_address
        6       F_address
        2       B_address
        1       A_address
        Time taken: 25.357 seconds
        hive> quit;

      • 驗證完記得離開,因為 hive 預設只能一支 client 跟它 query





  4. 處理 HadoopDB.xml


    • 因為 batch_setup.py 只設計成單一資料的匯入,所以要自行處理,將 table 1 和 table 2 整合在一個檔案內

    • <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
      <DBClusterConfiguration xmlns="http://edu.yale.cs.db.hadoop/DBConfigurationSchema">
          <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="Cluster01">
              <Relations id="t1">
                  <Partitions url="jdbc:postgresql://Cluster01:5432/udb_t1_0" id="0"/>
              </Relations>
              <Relations id="t2">
                  <Partitions url="jdbc:postgresql://Cluster01:5432/udb_t2_0" id="0"/>
              </Relations>
              <Relations id="no_use"/>
          </Nodes>
          <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="Cluster02">
              <Relations id="t1">
                  <Partitions url="jdbc:postgresql://Cluster02:5432/udb_t1_1" id="1"/>
              </Relations>
              <Relations id="t2">
                  <Partitions url="jdbc:postgresql://Cluster02:5432/udb_t2_1" id="1"/>
              </Relations>
              <Relations id="no_use"/>
          </Nodes>
          <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="Cluster03">
              <Relations id="t1">
                  <Partitions url="jdbc:postgresql://Cluster03:5432/udb_t1_2" id="2"/>
              </Relations>
              <Relations id="t2">
                  <Partitions url="jdbc:postgresql://Cluster03:5432/udb_t2_2" id="2"/>
              </Relations>
              <Relations id="no_use"/>
          </Nodes>
      </DBClusterConfiguration>

    • $ hadoop fs -rmr HadoopDB.xml
      Deleted hdfs://Cluster01:9000/user/hadoop/HadoopDB.xml
      $ hadoop fs -put HadoopDB.xml HadoopDB.xml 

    • 測試合併後的 HadoopDB.xml 是否能正常工作

      • hive> select name,id from t1;
        Total MapReduce jobs = 1
        Number of reduce tasks is set to 0 since there's no reduce operator
        Starting Job = job_201001201134_0011, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0011
        Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0011
        2010-01-20 02:20:58,547 map = 0%,  reduce =0%
        2010-01-20 02:21:05,622 map = 33%,  reduce =0%
        2010-01-20 02:21:11,790 map = 67%,  reduce =0%
        2010-01-20 02:21:12,808 map = 100%,  reduce =0%
        Ended Job = job_201001201134_0011
        OK
        C       3
        F       6
        I       9
        B       2
        E       5
        H       8
        A       1
        D       4
        G       7
        Time taken: 29.667 seconds
        hive> select address,id from t2;
        Total MapReduce jobs = 1
        Number of reduce tasks is set to 0 since there's no reduce operator
        Starting Job = job_201001201134_0012, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0012
        Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0012
        2010-01-20 02:21:25,782 map = 0%,  reduce =0%
        2010-01-20 02:21:34,920 map = 33%,  reduce =0%
        2010-01-20 02:21:38,983 map = 67%,  reduce =0%
        2010-01-20 02:21:41,002 map = 100%,  reduce =0%
        Ended Job = job_201001201134_0012
        OK
        I_address       9
        H_address       8
        E_address       5
        D_address       4
        C_address       3
        G_address       7
        F_address       6
        B_address       2
        A_address       1
        Time taken: 18.932 seconds
        hive>






經過上頭的準備工作完成,開始正式測試 Join 囉


hive> select t1.id, t1.name, t2.address from t1 join t2 on ( t1.id = t2.id );
Total MapReduce jobs = 1
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0013, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0013
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0013
2010-01-20 02:23:24,293 map = 0%,  reduce =0%
2010-01-20 02:23:36,400 map = 17%,  reduce =0%
2010-01-20 02:23:46,544 map = 33%,  reduce =0%
2010-01-20 02:23:52,248 map = 50%,  reduce =0%
2010-01-20 02:23:55,274 map = 67%,  reduce =0%
2010-01-20 02:23:57,291 map = 83%,  reduce =0%
2010-01-20 02:23:58,308 map = 100%,  reduce =0%
2010-01-20 02:24:03,360 map = 100%,  reduce =28%
2010-01-20 02:24:05,381 map = 100%,  reduce =100%
Ended Job = job_201001201134_0013
OK
1       A       A_address
2       B       B_address
3       C       C_address
4       D       D_address
5       E       E_address
6       F       F_address
7       G       G_address
8       H       H_address
9       I       I_address
Time taken: 44.725 seconds


驗證


udb_t1_0=# select * from t1;
 id | name
----+------
  1 | A
  4 | D
  7 | G
(3 rows)


udb_t2_0=# select * from t2;
 id |  address
----+-----------
  7 | G_address
  6 | F_address
  2 | B_address
  1 | A_address
(4 rows)


驗證的結果是對的,在 Cluster01 上只有 Table 1 的 1,4,7 資料,和 Table 2 的 7,6,2,1 資料,再加上這兩個 Table 是在不同的 databases 上,即 udb_t1_0 和  udb_t2_0 ,所以在資料並未集中在某台機器或其資料庫中,HadoopDB 還是可以處理好 Join 的工作啦,別忘了這是原先Hive就設計好的架構囉


以下是其他的測試


hive> select * from t1 join ( select t1.id , t1.name , t2.address
from t1 join t2 on ( t1.id = t2.id ) ) r1 on ( t1.id = r1.id ) ;
Total MapReduce jobs = 2
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0015, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0015
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0015
2010-01-20 02:50:55,389 map = 0%,  reduce =0%
2010-01-20 02:51:07,511 map = 17%,  reduce =0%
2010-01-20 02:51:11,560 map = 33%,  reduce =0%
2010-01-20 02:51:18,632 map = 50%,  reduce =0%
2010-01-20 02:51:21,685 map = 67%,  reduce =0%
2010-01-20 02:51:23,724 map = 83%,  reduce =0%
2010-01-20 02:51:25,750 map = 100%,  reduce =0%
2010-01-20 02:51:30,794 map = 100%,  reduce =17%
2010-01-20 02:51:35,856 map = 100%,  reduce =100%
Ended Job = job_201001201134_0015
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0016, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0016
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0016
2010-01-20 02:51:41,127 map = 0%,  reduce =0%
2010-01-20 02:51:51,224 map = 25%,  reduce =0%
2010-01-20 02:52:04,345 map = 50%,  reduce =0%
2010-01-20 02:52:08,409 map = 100%,  reduce =0%
2010-01-20 02:52:09,441 map = 100%,  reduce =8%
2010-01-20 02:52:21,548 map = 100%,  reduce =100%
Ended Job = job_201001201134_0016
OK
1       A       1       A       A_address
2       B       2       B       B_address
3       C       3       C       C_address
4       D       4       D       D_address
5       E       5       E       E_address
6       F       6       F       F_address
7       G       7       G       G_address
8       H       8       H       H_address
9       I       9       I       I_address
Time taken: 90.89 seconds
hive>


hive> select count(t1.id) from t1 join ( select t1.id , t1.name ,
t2.address from t1 join t2 on ( t1.id = t2.id ) where t2.id > 3 ) r1
on ( t1.id = r1.id ) ;
Total MapReduce jobs = 3
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0017, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0017
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0017
2010-01-20 02:54:57,465 map = 0%,  reduce =0%
2010-01-20 02:55:06,563 map = 17%,  reduce =0%
2010-01-20 02:55:18,722 map = 33%,  reduce =0%
2010-01-20 02:55:26,829 map = 50%,  reduce =0%
2010-01-20 02:55:28,860 map = 67%,  reduce =0%
2010-01-20 02:55:29,878 map = 83%,  reduce =0%
2010-01-20 02:55:30,908 map = 100%,  reduce =0%
2010-01-20 02:55:34,947 map = 100%,  reduce =11%
2010-01-20 02:55:45,039 map = 100%,  reduce =100%
Ended Job = job_201001201134_0017
Number of reduce tasks not specified. Estimated from input data size: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0018, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0018
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0018
2010-01-20 02:55:49,246 map = 0%,  reduce =0%
2010-01-20 02:55:58,324 map = 25%,  reduce =0%
2010-01-20 02:56:09,456 map = 50%,  reduce =0%
2010-01-20 02:56:10,481 map = 75%,  reduce =0%
2010-01-20 02:56:12,516 map = 100%,  reduce =0%
2010-01-20 02:56:19,594 map = 100%,  reduce =8%
2010-01-20 02:56:28,678 map = 100%,  reduce =100%
Ended Job = job_201001201134_0018
Number of reduce tasks determined at compile time: 1
In order to change the average load for a reducer (in bytes):
  set hive.exec.reducers.bytes.per.reducer=<number>
In order to limit the maximum number of reducers:
  set hive.exec.reducers.max=<number>
In order to set a constant number of reducers:
  set mapred.reduce.tasks=<number>
Starting Job = job_201001201134_0019, Tracking URL = http://Cluster01:50030/jobdetails.jsp?jobid=job_201001201134_0019
Kill Command = /home/hadoop/bin/../bin/hadoop job  -Dmapred.job.tracker=Cluster01:9001 -kill job_201001201134_0019
2010-01-20 02:56:34,726 map = 0%,  reduce =0%
2010-01-20 02:56:45,829 map = 100%,  reduce =0%
2010-01-20 02:56:58,937 map = 100%,  reduce =100%
Ended Job = job_201001201134_0019
OK
6
Time taken: 125.945 seconds
hive>


SPAM Mail 和 Yahoo Group - 取消自動加入 Group

最近開始收到一堆垃圾信件,其中有一項是非常擾人的!那就是 Yahoo Group 的邀請函!特別是一堆愛寄垃圾信的人,他們就只要去新增一個 Yahoo Group 後,把他們要寄的人通通加進去,之後就只要對 Group 發信就可以變成寄群組信那樣,達到寄廣告信的目的


可惡的是 Yahoo Group 預設是被加的人,自動默認加入,然後要取消它還得自己去回信取消!搞得自己還得動手處理,今天忍不住想去寄信給 Yahoo ,慶幸地發現已經有人咆哮過了,哈。



目前這種  Yahoo Group 邀請的預設狀態,就跟 Facebook 處理隱私權給我的觀感一樣 :P 感覺什麼都要很 open ,表面上說這是趨勢,骨子裡的主意還不是想合理地使用個人資料吧。


不小心扯遠了,此解決方式:



滿建議這種邀請函再加上使用者確認的 link 不就好了嗎,收到邀請函還需要點選 link 認同才正式加入 Group ,這樣讓收到廣告信的人可以輕鬆刪信,對於真正想加入 Group 的,也可以多點一下就處理好了。


2010年1月14日 星期四

[Bash] 將檔案內的字串進行取代

太習慣寫 php 了,其實也有一陣子用 php 當作 script 管機器,最近則是試過 python ,所以,就來試試 bash 吧!


首先會碰到的問題是如何處理參數,這部分可以在 Getopt and getopts 看到很豐富的範例,接下來還可以逛逛 鳥哥的 Linux 私房菜 - 第十三章、學習 Shell Scripts #善用判斷式 ,就可以完成很多事囉!接著我想做的字串取代,就可以使用 sed 來處理,相關參考資料 Bash Shell: Replace a string with another string in all files using sed and perl -pie


最後,留個範例給自己吧


#!/bin/bash

WORK_DIR=
OPTS_ENABLE="false"

usage_help()
{
        echo "Usage> $0 -i \"Path\"  ..."
        echo "    -i \"/tmp\"          # path for write a config file"
}

args=`getopt i:e $*`
if test $? != 0
        then
                usage_help
        exit 1
fi
set -- $args
for i do
        case "$i" in
                -i) shift; WORK_DIR=$1 ;shift;;
                -e) shift; OPTS_ENABLE="true" ;shift;;
        esac
done

# check install path
if test ! -r $WORK_DIR || test ! -x $WORK_DIR || test ! -w $WORK_DIR ; then
        echo "Please Check WorkDir: [$WORK_DIR]"
        exit 1
fi

FILE_CONFIG=$WORK_DIR/config

# bakcup & replace
if test -r $FILE_CONFIG ; then

        cp $FILE_CONFIG $FILE_CONFIG.bak.`date +%Y%m%d%H%M%S`
        sed -e "s/KEY/$OPTS_ENABLE/g" $FILE_CONFIG > $FILE_CONFIG
fi


其中,若存在 bash shell 裡的變數是帶有路徑的, ex: x=/path/bin.exe , 那使用 sed -e "s/KEY/$x/g" $FILE_CONFIG > $FILE_CONFIG 會出問題,解法就是先把 x 變數中的 '/' 取代成 '\/' 吧!


x=$(echo $x | sed "s/\//\\\\\//g")


免費線上廣播播放軟體 - Hinedo 修正無法播放問題

這陣子開始發現 Hinedo 無法播放廣播了,很容易猜想是 Hichannel 改變了播放廣播的架構,這導致不只 Hinedo 不能播放,其他利用類似原理提供廣播的播放軟體也一樣失效。原本打算抽空去看的,但最後一直沒播出時間,今天無意間閒逛,發現在 PTT 的 EZsoft 看到有人提出修正囉!


請更新 Play.vbs ,把該檔第 6 列進行更新


原樣:base = "http://hichannel.hinet.net/player/radio/index.jsp?radio_id="


更新:base = "http://hichannel.hinet.net/player/radio/mediaplay.jsp?radio_id="


希望 Hinedo 官網也能盡快更新囉!


2010年1月9日 星期六

使用 VIM 進行 HEX Mode 編輯 (16進位編輯)

想說以前都用 UltraEdit 編輯遊戲存檔,沒想到 VIM 也能這樣做。


只要編輯時,下 :%! xxd ,就會以 Hex Mode 顯示檔案,然後編輯完想返回可以再用 :%! xxd -r 接著再存檔囉


生活回顧


圖片來源:http://books.com.tw/


昨天早上,上班前望一望桌上的這本書,已被擺在那兩個禮拜,只看了序。也不知是什麼樣的動力,明明也穿好了鞋,最後還是拎了它出門。這本是高中同學去年底推薦我看一看的書,那天我們正談論著運動彩,只是我真的太少看書,連他在高中畢業前送我的「溫一壺月光下酒」我也都還沒看完,更別說前一陣子完婚的好友送我的「性格組合論」。我大概比較喜歡觀察週遭勝過窩在書房看書吧,但明明我就是個阿宅。


每次要返家,心中會有一點點掙扎,因為從晚上五點下班後,大概花二十分吃飯,偶爾擔心假日車潮會只買個麵包牛奶,再花個 30 分鐘的機車車程到達火車站,並且要找一找停車位,最後再花 2~3 小時搭車回家,所以,大部分從下班到回家休息需要花費五個小時,但我還是喜歡回家。昨天在火車站等車時,開始從頭看這本書,前面還算吸引人,但後面開始提到一些數學式子的名稱時,我卻開始感覺枯燥!這本書將讀者定位在不懂或略懂數學的人,所以並沒有仔細描述那些式子,不曉得是不是這個關係,反而讓我看不下去?


那種感覺就像碩班第一篇自己挑選閱讀的 paper 時,前面的長篇大論,我根本沒耐心閱讀,反而看到數學式子會很專心了解它的意義,也有可能就像看 code 時,我只想去 trace 流程而不想多看一眼註解!然而,這本書並不這般影響我的,我還是配著寒風在車站上的涼椅上看著,畢竟也睡不著吧!


偶爾上了車後,發現坐在我身旁的女生拎著一本比這本書厚三倍的,我偷偷描幾眼,竟然看到直方圓、標準差、四分位差、機率甚至書後的查表!驚覺也太巧了吧。雖然我還不確定她到底是念哪個系所,但我相信跟我手上的這本書有微妙的相關。不過車上比較溫暖,她沒看幾眼也睡了,當然我也不落人後。


猜測著她未來可能的職業時,回想起前陣子跟同事吃飯閒聊起看牙醫預約的事,他說竟然要護士小姐花人力去安排,對我們而言,三兩下就能寫出堪用的預約系統,只是隨即而來的,卻是人力過盛所帶來的失業潮,這倒呼應起我為了架設一些環境寫了一套簡單的 Script ,讓安裝的人可以打不到 30 個字的一道指令,按一下 Enter 後,自動產生至少 15 個指令,甚至某些條件上還可以蹦出 50 個指令以上。當時我想了想,的確,有些程式是很方便的,但它方便到不該存在。最簡單的例子就是遊戲外掛,如果普及後,那遊戲就只剩下數字在說話了。或許,哪一天也會出現致命的關鍵程式,讓一大堆工程師失業吧!?


回到書中,字裡行間,透露出要認真地規劃實驗步驟!而後我也發現,生活上真的充斥著許多數學公式,無論科學或社會學,都想用數學勾勒出事件的模型,像我那位同學,也想找我寫一些預測的程式,但我很快地推掉,理由是修 Dataminig 那門課,當老師提到 Final Project 時,第一句話就是跟大家說不要去做股市的分析。雖然我也多少知道什麼黃金交叉的用法,說穿了那只是平均數值的把戲吧。


回到現實生活中,對於數學的使用,我發現我還有點薄弱,頂多看著車票座位可以推一下靠窗靠走道,或是寫程式上算算硬碟傳輸速度外,似乎就沒多少了。


生活的回顧,沒想到從這本書開始。


工作後兩個月,有點因為環境上的福利在想要不要一直做下去,但第三個月末,開始在想是不是要大膽一點創業。目前聽到幾個人生規劃,有開披隡店的故事,也有幫傳統產業做 SkypeOut 整合的,更有靠寫遊戲搭在大型機台的,細想,難到目前手頭上的能力,不足以發光發熱嗎?我想這個問題,我得多觀察與繼續學習個一、兩年吧,恰好用在這個時期多看看多體驗!還有一點很重要的,那就是公司的福利要靠自己爭取 :P 所以,事情順遂時,那就準時下班吧!人生中的每一天,花 1/3 養身體,再花 1/3 賺錢養活自己跟家人,最後 1/3 的多樣性,就得靠自己爭取囉。


2010年1月8日 星期五

[Linux] 安裝 HadoopDB 於 Hadoop 0.19.2 @ Ubuntu 9.10

+ =HadoopDB
圖片來源:http://hadoop.apache.org/http://wiki.postgresql.org/


雖然,上頭圖片的結合並不代表 HadoopDB 的原意,但 HadoopDB 開發上使用了 PostgreSQL 作為範例。那什麼是 HadoopDB 呢?那就是提出並實作在 DataNode 上架設 Database 的模式,以此整合起來的一個服務架構。傳統 Databases 技術上的鑽研已不是短短幾年可以道盡,然而,目前火紅的 Hadoop ,在資料儲存上預設是採用 HDFS 模式,就是簡單的檔案儲存模式。如果把資料的管理擺在資料庫,那能不能將過去在資料庫上的效能帶入 Hadoop 裡呢?這就是 HadoopDB 想嘗試的方向。HadoopDB:An architectural hybrid of MapReduce and DBMS technologies,也有人用這樣來描述 HadoopDB:An Open Source Parallel Database



圖片來源:http://hadoopdb.sourceforge.net/guide/


上圖是 HadoopDB 的架構圖,有一個 SMS Planner 將 SQL 語法轉成 MapReduce Jobs ,而底層又有一次轉換,將 MapReduce 存取轉成 SQL 語法,在此不多探討,有興趣的請直接看它的 Paper 囉。


由於它是在 Hadoop-0.19.x 開發的,因此我還是用 Hadoop-0.19.2 來架設,至於架設部分可以參考這篇:[Linux] 安裝 Hadoop 0.20.1 Multi-Node Cluster @ Ubuntu 9.10,其中 0.19.2 與 0.20.1 安裝上只有些微的差別,在上述文章中的 hadoop-0.20.1/conf/core-site.xml 與 hadoop-0.20.1/conf/mapred-site.xml 
的內容,只需改寫在 hadoop-0.19.2/conf/hadoop-site.xml 即可。接著下面的介紹也將延續上則安裝教學,以 3-Node Cluster ,分別以 Cluster01、Cluster02 和 Cluster03 作為範例敘述,並且各台使用 hadoop 帳號來操作。



  1. 首先需建立 3-Node Cluster on Hadoop 0.19.x



  2. 以下若是用 hadoop@Cluster0X:~ 代表 Cluster01 ~ Cluster03 都要做的

  3. 對各台安裝設定 PostgreSQL

    • 安裝並為資料庫建立 hadoop 帳號,假定使用密碼為 1234

    • hadoop@Cluster0X:~$ sudo apt-get install postgresql

    • hadoop@Cluster0X:~$ sudo vim /etc/postgresql/8.4/main/pg_hba.conf

      • #local   all         all                               ident
        local   all         all                               trust
        # IPv4 local connections:
        #host    all         all         127.0.0.1/32          md5
        host    all         all         127.0.0.1/32          password
        host    all         all         192.168.0.1/16          password            # 加上Cluster 機器 IP 範圍
        # IPv6 local connections:
        #host    all         all         ::1/128               md5
        host    all         all         ::1/128               password



    • hadoop@Cluster0X:~$ sudo /etc/init.d/postgresql-8.4 restart

    • hadoop@Cluster0X:~$ sudo su - postgres

    • postgres@Cluster0X:~$ createuser hadoop

      • Shall the new role be a superuser? (y/n) y
        postgres@Cluster01:~$ psql
        psql (8.4.2)
        Type "help" for help.

        postgres=# alter user hadoop with password '1234';
        ALTER ROLE
        postgres=# \q



    • 測試其他機器可否連線

      • hadoop@Cluster01:~$ createdb testdb

      • hadoop@Cluster02:~$ psql -h Cluster01 testdb

        • 錯誤訊息

          • psql: FATAL:  no pg_hba.conf entry for host "192.168.56.168", user "hadoop", database "testdb", SSL on
            FATAL:  no pg_hba.conf entry for host "192.168.56.168", user "hadoop", database "testdb", SSL off



        • 正確訊息

          • Password:
            psql (8.4.2)
            SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256)
            Type "help" for help.

            testdb=#









  4. 設定 HadoopDB



    • hadoop@Cluster0X:~$ cp hadoopdb.jar HADOOP_HOME/lib/

    • hadoop@Cluster0X:~$ cp postgresql-8.4-701.jdbc4.jar HADOOP_HOME/lib/

    • hadoop@Cluster0X:~$ vim HADOOP_HOME/conf/hadoop-site.xml


      • <property>
        <name>hadoopdb.config.file</name>
        <value>HadoopDB.xml</value>
        <description>The name of the HadoopDB cluster configuration file</description>
        </property>



        <property>
        <name>hadoopdb.fetch.size</name>
        <value>1000</value>
        <description>The number of records fetched from JDBC ResultSet at once</description>
        </property>


        <property>
        <name>hadoopdb.config.replication</name>
        <value>false</value>
        <description>Tells HadoopDB Catalog whether replication is enabled.
        Replica locations need to be specified in the catalog.
        False causes replica information to be ignored.</description>
        </property>



    • hadoop@Cluster01:~$ vim nodes.txt

      • 192.168.56.168
        192.168.56.169
        192.168.56.170



    • hadoop@Cluster01:~$ vim Catalog.properties

      • #Properties for Catalog Generation
        ##################################
        nodes_file=nodes.txt
        # Relations Name and Table Name are the same
        relations_unchunked=raw
        relations_chunked=poi
        catalog_file=HadoopDB.xml
        ##
        #DB Connection Parameters
        ##
        port=5432
        username=hadoop
        password=1234
        driver=org.postgresql.Driver
        url_prefix=jdbc\:postgresql\://
        ##
        #Chunking properties
        ##
        # the number of databases on a node
        chunks_per_node=3
        # for udb0 ,udb1, udb2 ( 3 nodes = 0 ~ 2 )
        unchunked_db_prefix=udb
        # for cdb0 ,cdb1, ... , cdb8 ( 3 nodes x 3 chunks = 0~8 )
        chunked_db_prefix=cdb
        ##
        #Replication Properties
        ##
        dump_script_prefix=/root/dump_
        replication_script_prefix=/root/load_replica_
        dump_file_u_prefix=/mnt/dump_udb
        dump_file_c_prefix=/mnt/dump_cdb
        ##
        #Cluster Connection
        ##
        ssh_key=id_rsa-gsg-keypair



    • hadoop@Cluster01:~$ java -cp lib/hadoopdb.jar edu.yale.cs.hadoopdb.catalog.SimpleCatalogGenerator Catalog.properties

      • 產生的 HadoopDB.xml 類似下面:
        <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
        <DBClusterConfiguration xmlns="http://edu.yale.cs.db.hadoop/DBConfigurationSchema">
            <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="192.168.56.168">
                <Relations id="raw">
                    <Partitions url="jdbc:postgresql://192.168.56.168:5432/udb0" id="0"/>
                </Relations>
                <Relations id="poi">
                    <Partitions url="jdbc:postgresql://192.168.56.168:5432/cdb0" id="0"/>
                    <Partitions url="jdbc:postgresql://192.168.56.168:5432/cdb1" id="1"/>
                    <Partitions url="jdbc:postgresql://192.168.56.168:5432/cdb2" id="2"/>
                </Relations>
            </Nodes>
            <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="192.168.56.169">
                <Relations id="raw">
                    <Partitions url="jdbc:postgresql://192.168.56.169:5432/udb1" id="1"/>
                </Relations>
                <Relations id="poi">
                    <Partitions url="jdbc:postgresql://192.168.56.169:5432/cdb3" id="3"/>
                    <Partitions url="jdbc:postgresql://192.168.56.169:5432/cdb4" id="4"/>
                    <Partitions url="jdbc:postgresql://192.168.56.169:5432/cdb5" id="5"/>
                </Relations>
            </Nodes>
            <Nodes Password="1234" Username="hadoop" Driver="org.postgresql.Driver" Location="192.168.56.170">
                <Relations id="raw">
                    <Partitions url="jdbc:postgresql://192.168.56.170:5432/udb2" id="2"/>
                </Relations>
                <Relations id="poi">
                    <Partitions url="jdbc:postgresql://192.168.56.170:5432/cdb6" id="6"/>
                    <Partitions url="jdbc:postgresql://192.168.56.170:5432/cdb7" id="7"/>
                    <Partitions url="jdbc:postgresql://192.168.56.170:5432/cdb8" id="8"/>
                </Relations>
            </Nodes>
        </DBClusterConfiguration>



    • hadoop@Cluster01:~$ hadoop dfs -put HadoopDB.xml HadoopDB.xml



  5. 建立資料表、測試資料匯入各台機器的資料庫中,並且在 Hive 上建立相對應的資料表

    • 在此以 raw 這個 talbe 當作範例。假設 HadoopDB.xml 對 raw 這個 table 敘述有 3 個,即上述範例的 udb0 、udb1 和 udb2,那就要分別去上頭指定的機器上建立資料庫

      • hadoop@Cluster01:~$ createdb udb0
        hadoop@Cluster02:~$ createdb udb1
        hadoop@Cluster03:~$ createdb udb2



    • 並且依輸入的資料建立資料表

      • hadoop@Cluster01:~$ psql udb0
        udb0=#
        CREATE TABLE raw (
        ID int,
        NAME varchar(300)
        );

      • 同理如 Cluster02 跟 Cluster03



    • 資料匯入

      • hadoop@Cluster01:~$ psql udb0
        udb0=# COPY RAW FROM '/home/hadoop/p0' WITH DELIMITER E'\t' ;

      • 關於 /home/hadoop/p0 的資料主要從原本依開始的大檔案,使用 HadoopDB 所提供的切割工具處理的

        • $ hadoop jar lib/hadoopdb.jar edu.yale.cs.hadoopdb.dataloader.GlobalHasher src_in_hdfs out_in_hdfs 3 '\n' 0

        • $ hadoop fs -get out_in_hdfs/part-00000 /home/hadoop/p0



      • 假設資料擺在 /home/haddop/p0 並且欄位以 tab 分隔

      • 同理也要處理 Cluster02 跟 Cluster03



    • 最後,在 Hive 上頭建立相對應的資料表 (只需用一台機器執行)


      • 假設 Hive 使用的資料表將儲存在 HDFS 的 /db

      • hadoop@Cluster01:~ $ hadoop dfs -mkdir /db

      • hadoop@Cluster01:~ $ SMS_dist/bin/hive
        CREATE EXTERNAL TABLE raw (
        ID int,
        NAME string
        )
        ROW FORMAT DELIMITED
        FIELDS TERMINATED BY '|'
        STORED AS
        INPUTFORMAT 'edu.yale.cs.hadoopdb.sms.connector.SMSInputFormat'
        OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
        LOCATION '/db/raw';

      • 其中 /db/raw 的 basename 要跟 table 名稱一樣(各台資料庫裡的資料表與Hive建立的資料表),另外對於資料欄位的型別也別忘了轉換囉





  6. 以上設定完後,即可在一台機器上(Ex: Cluster1) 執行 $ SMS_dist/bin/hive 看看成果

    • hadoop@Cluster01:~ $ SMS_dist/bin/hive
      hive> show tables;
      hive> select name from raw;




上述是採用 unchunked 當作範例,如果改用 chunked 模式,假設有三台機器,並且打算使用三個資料庫,那設定上就會變成 3 x 3 的數量,也就是共有 9 個資料庫要設定,包括建立資料庫、建立資料表,匯入資料等等的,因此實作上應該寫隻 script 處理,在 HadoopDB 網站上有一個建立 /my_data 目錄範例,我把它稍微改寫:


#!/usr/bin/python
import sys, os, thread, commands
import getopt  
       
DEBUG_MODE = True
                       
completed = {} 
create_db_cmd_list = [ ''
        , 'createdb testdb'
        , 'echo "CREATE TABLE Helo ( ID int );" | psql testdb'
        , 'dropdb testdb'
]              
                       
cmd_list = []
cmd_list.extend( create_db_cmd_list )

def ParseHadoopXML( file_path ) :
        return 
               
def executeThread(node, *args ):
        #Make sure key is accessible and is the correct key name.
        #os.system("ssh -i key -o 'StrictHostKeyChecking=no' %s \'mkdir /my_data \'" %(node))
        #You could replace the mkdir command with another command line or add more command lines,
        # as long as you prefix the command with the ssh connection.

        if DEBUG_MODE :
                print "\tShow Only"
        for cmd in cmd_list :
                if cmd == None or cmd == '' :
                        continue;
                cmd = cmd.strip()
                if cmd == '' :
                        continue;
                cmd_exec = "ssh %s \'%s\'" % (node , cmd )
                print "\t" , cmd_exec
                if DEBUG_MODE == False :
                        os.system( cmd_exec )
        completed[node] = "true"

def main( argv=None ):
        hostfile = "nodes.txt"
        internalips = open(hostfile,'r').readlines()

        for i in internalips:
                os.system('sleep 1')

                node_info = i.strip() ,
                thread.start_new_thread(executeThread, node_info  )

        while (len(completed.keys()) < len(internalips)):
                os.system('sleep 2')

        print "Execution Completed"

if __name__ == "__main__":
        main()


最後,我則是來個大改寫,若要使用十分建議先在用在虛擬環境,看看流程對不對,當然,最好是自己先手動設定過,等流程清楚後再設計符合自己的需求。使用上要先準備的資料:



  1. 將原始擺在 HDFS ,預設資料以 new line 分格,每筆資料以 Tab 分格欄位,例如:

    • 1    changyy
      2    hello world
      3    hadoop



  2. 在本機端建立 nodes.txt ,裡頭敘述 Cluster 各台機器的 IP,用 New Line('\n') 符號分格,例如:

    • 192.168.56.168
      192.168.56.169
      192.168.56.170



  3. 建立資料表的欄位敘述,預設此用 table_create 檔案,例如:

    • ID int ,
      NAME varchar(250)



  4. 最後,可以透過 $python this.py -help 查看有什麼可以設定,只不過是用很破的英文描述


純粹 show 出將會執行的指令,請留意它將會刪除哪些目錄、資料庫等等
$ python this.py --source_dir_in_hdfs src

真正運行
$ python this.py --source_dir_in_hdfs src -go

預設是 unchunked ,若想設定可以用 --chunk_num 設定
$ python this.py --source_dir_in_hdfs src --chunk_num 3


實際運行例子:




  • hadoop@Cluster01:~$ cat nodes.txt
    192.168.56.168
    192.168.56.169
    192.168.56.170

  • hadoop@Cluster01:~$ cat table_create
    ID int,
    NAME varchar(250)

  • hadoop@Cluster01:~$ python batch_setup.py --source_dir_in_hdfs src

     Current Status is just Debug Mode for show all commands
     please set '-g' or '--go' option to execute them after check all commands.(look at the 'rm -rf' and 'hadoop fs -rmr')

    $ /usr/bin/java -cp /home/hadoop/lib/hadoopdb.jar edu.yale.cs.hadoopdb.catalog.SimpleCatalogGenerator /tmp/Catalog.properties

    => Start to put the HadoopDB.xml into HDFS

    $ /home/hadoop/bin/hadoop fs -rmr HadoopDB.xml
    $ /home/hadoop/bin/hadoop fs -put HadoopDB.xml HadoopDB.xml

    => The data source(src) would be partitioned into 3 parts(tmp_out_hadoopdb) by the delimiter (\n)

    $ /home/hadoop/bin/hadoop fs -rmr tmp_out_hadoopdb
    $ /home/hadoop/bin/hadoop jar /home/hadoop/lib/hadoopdb.jar edu.yale.cs.hadoopdb.dataloader.GlobalHasher src tmp_out_hadoopdb 3 '\n' 0

    => To configure your nodes...

        ssh 192.168.56.168 "dropdb udb_hadoopdb_0"
        ssh 192.168.56.168 "createdb udb_hadoopdb_0"
        ssh 192.168.56.168 "echo \"create table hadoopdb ( id int, name varchar(250) );\" | psql udb_hadoopdb_0"
        ssh 192.168.56.168 "rm -rf /tmp/out_for_global_parition"
        ssh 192.168.56.168 "/home/hadoop/bin/hadoop fs -get tmp_out_hadoopdb/part-00000 /tmp/out_for_global_parition"
        ssh 192.168.56.168 "echo \"COPY hadoopdb FROM '/tmp/out_for_global_parition' WITH DELIMITER E'\t';\" | psql udb_hadoopdb_0"
        ssh 192.168.56.168 "rm -rf /tmp/out_for_global_parition"
        ssh 192.168.56.170 "dropdb udb_hadoopdb_2"
        ssh 192.168.56.170 "createdb udb_hadoopdb_2"
        ssh 192.168.56.170 "echo \"create table hadoopdb ( id int, name varchar(250) );\" | psql udb_hadoopdb_2"
        ssh 192.168.56.170 "rm -rf /tmp/out_for_global_parition"
        ssh 192.168.56.170 "/home/hadoop/bin/hadoop fs -get tmp_out_hadoopdb/part-00002 /tmp/out_for_global_parition"
        ssh 192.168.56.170 "echo \"COPY hadoopdb FROM '/tmp/out_for_global_parition' WITH DELIMITER E'\t';\" | psql udb_hadoopdb_2"
        ssh 192.168.56.170 "rm -rf /tmp/out_for_global_parition"
        ssh 192.168.56.169 "dropdb udb_hadoopdb_1"
        ssh 192.168.56.169 "createdb udb_hadoopdb_1"
        ssh 192.168.56.169 "echo \"create table hadoopdb ( id int, name varchar(250) );\" | psql udb_hadoopdb_1"
        ssh 192.168.56.169 "rm -rf /tmp/out_for_global_parition"
        ssh 192.168.56.169 "/home/hadoop/bin/hadoop fs -get tmp_out_hadoopdb/part-00001 /tmp/out_for_global_parition"
        ssh 192.168.56.169 "echo \"COPY hadoopdb FROM '/tmp/out_for_global_parition' WITH DELIMITER E'\t';\" | psql udb_hadoopdb_1"
        ssh 192.168.56.169 "rm -rf /tmp/out_for_global_parition"
    $ /home/hadoop/bin/hadoop fs -rmr tmp_out_hadoopdb

    => To setup the external table for Hive

    $ /home/hadoop/bin/hadoop fs -mkdir /db
    $ /home/hadoop/bin/hadoop fs -rmr /db/hadoopdb
    $  echo "drop table hadoopdb;" | /home/hadoop/SMS_dist/bin/hive
    $  echo "create external table hadoopdb ( id int, name string )  ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' STORED AS  INPUTFORMAT 'edu.yale.cs.hadoopdb.sms.connector.SMSInputFormat'  OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'  LOCATION '/db/hadoopdb'; " | /home/hadoop/SMS_dist/bin/hive


    => All Execution Completed...



#!/usr/bin/python
# At Python 2.6.4
# Yuan-Yi Chang
# 2010/01/07 15:09
#
import sys, os, thread, commands
import re, os.path
from optparse import OptionParser

BIN_JAVA = '/usr/bin/java'
BIN_HADOOP = '/home/hadoop/bin/hadoop'
BIN_HIVE = '/home/hadoop/SMS_dist/bin/hive'
JAR_HADOOPDB = '/home/hadoop/lib/hadoopdb.jar'

completed = {}
cmd_for_node = {}

def initHadoopDB(    data_in_hdfs = None , data_delimiter = '\n' , data_field_delimiter = '\t' ,
            data_partition_out = None ,
            nodes_in_file = 'nodes.txt' , chunks_per_node = 3 ,
            table_name = None , table_field_info = None ,
            db_user = 'hadoop' , db_pass='1234' , db_field_delimiter = '|' , hive_db_dir_in_hdfs = '/db' ,
  
         tmp_path_for_catelog = '/tmp/Catalog.properties' ,
out_hadoop_xml = 'HadoopDB.xml' , hadoop_xml_in_hdfs = 'HadoopDB.xml' ,
            DEBUG_MODE = True ) :
    if data_in_hdfs is None :
        print 'Please input the path of the data source in HDFS'
        return False
    if data_partition_out is None :
        print 'Please input the path for the data source parition in HDFS'
        return False
    if table_name is None or re.match( '/[a-z0-9_]+/'  , table_name ) :
        print 'Please input the table name with [a-z0-9_] only'
        return False
    if table_field_info is None or os.path.isfile( table_field_info ) is False :
        print 'Please check the "table_field_info" : ' + str(table_field_info)
        return False
        
    if os.path.isfile( nodes_in_file ) is False :
        print 'Please check the "nodes_in_file" : ' + nodes_in_file
        return False
    if chunks_per_node < 0 :
        print 'Please check the "chunks_per_node" : ' + chunks_per_node + ' , 0 for no chunk'
        return False

    data_delimiter = data_delimiter.replace( '\n' , '\\n' ).replace( '\t' , '\\t' )
    data_field_delimiter = data_field_delimiter.replace( '\t' , '\\t' ).replace( '\n' , '\\n' )
    db_field_delimiter = db_field_delimiter.replace( '\t' , '\\t' ).replace( '\n' , '\\n' )

    make_catelog = ''
    #Properties for Catalog Generation'
    ##################################
    make_catelog += 'nodes_file='+nodes_in_file+'\n'
    if chunks_per_node < 2 :
        make_catelog += 'relations_chunked=no_use' + '\n'
        make_catelog += 'relations_unchunked='+table_name + '\n'
    else:
        make_catelog += 'relations_unchunked=' + 'no_use' + '\n'
        make_catelog += 'relations_chunked='+table_name + '\n'
    make_catelog += 'catalog_file=' + out_hadoop_xml + '\n'
    ##
    #DB Connection Parameters
    ##
    make_catelog += 'port=5432' + '\n'
    make_catelog += 'username=' + db_user + '\n'
    make_catelog += 'password=' + db_pass + '\n'
    make_catelog += 'driver=org.postgresql.Driver' + '\n'
    make_catelog += 'url_prefix=jdbc\\:postgresql\\://'+ '\n'
    ##
    #Chunking properties
    ##
    make_catelog += 'chunks_per_node=' + str(chunks_per_node) + '\n'
    make_catelog += 'unchunked_db_prefix=udb_' + table_name + '_' + '\n'
    make_catelog += 'chunked_db_prefix=cdb_'+ table_name + '_' + '\n'
    ##
    #Replication Properties
    ##
    make_catelog += 'dump_script_prefix=/root/dump' + '\n'
    make_catelog += 'replication_script_prefix=/root/load_replica_' + '\n'
    make_catelog += 'dump_file_u_prefix=/mnt/dump_udb' + '\n'
    make_catelog += 'dump_file_c_prefix=/mnt/dump_cdb'+ '\n'
    ##
    #Cluster Connection
    ##
    make_catelog += 'ssh_key=id_rsa-gsg-keypair' + '\n'
    
    try:
        f = open( tmp_path_for_catelog , 'w' )
        f.write( make_catelog )
        f.close()
    except:
        print 'Error to write a catelog:'+tmp_path_for_catelog
        return False
  
 cmd_exec = BIN_JAVA + ' -cp ' + JAR_HADOOPDB + '
edu.yale.cs.hadoopdb.catalog.SimpleCatalogGenerator ' +
tmp_path_for_catelog
    if DEBUG_MODE :
        print '$ ' + cmd_exec
    else:
        os.system( cmd_exec )

    if os.path.isfile( out_hadoop_xml ) is False :
        print 'Please check the "out_hadoop_xml" : ' + out_hadoop_xml
        return False

    print '\n=> Start to put the HadoopDB.xml into HDFS\n'

    if DEBUG_MODE :
        print '$ ' + BIN_HADOOP + ' fs -rmr ' + hadoop_xml_in_hdfs
        print '$ ' + BIN_HADOOP + ' fs -put ' + out_hadoop_xml + ' ' + hadoop_xml_in_hdfs
    else:
        os.system( BIN_HADOOP + ' fs -rmr ' + hadoop_xml_in_hdfs )
        os.system( BIN_HADOOP + ' fs -put ' + out_hadoop_xml + ' ' + hadoop_xml_in_hdfs  )

    partition_num = 0
    node_list = []
    try:
        tmp_list = open( nodes_in_file ,'r').readlines()
        for line in tmp_list :
            line = line.strip()
            if line <> '' :
                node_list.append( line )
        partition_num = len( node_list )
    except:
        print 'Please check the "nodes_in_file" : ' + nodes_in_file
        return False

    if partition_num > 1 :
  
     cmd_exec = BIN_HADOOP + ' jar ' + JAR_HADOOPDB + '
edu.yale.cs.hadoopdb.dataloader.GlobalHasher ' + data_in_hdfs + ' ' +
data_partition_out + ' ' + str(partition_num) + ' \'' + data_delimiter
+ '\' 0 '

        print '\n=> The data
source('+data_in_hdfs+') would be partitioned into
'+str(partition_num)+' parts('+data_partition_out+') by the delimiter
('+data_delimiter+')\n'
        if DEBUG_MODE :
            print '$ ' + BIN_HADOOP + ' fs -rmr ' + data_partition_out
            print '$ ' + cmd_exec
        else:
            os.system( BIN_HADOOP + ' fs -rmr ' + data_partition_out )
            os.system( cmd_exec )
    else:
        print '\n=> The number of datanodes should be > 1\n'
        return False

    HadoopDB_Info = ''
    try:
        HadoopDB_Info = open( out_hadoop_xml , 'r' ).read()
    except:
        print 'Error at read "out_hadoop_xml" : ' + out_hadoop_xml
        return False
    if HadoopDB_Info is '' :
        print 'The info in the file is empty : ' + HadoopDB_Info
        return False

    DB_TABLE_CREATE_INFO = ''
    try:
        DB_TABLE_CREATE_INFO = open( table_field_info , 'r' ).read().strip()
    except:
        print 'Error at read "table_field_info" : ' + table_field_info
        return False

    if DB_TABLE_CREATE_INFO is '' :
        print 'The info in the file is empty : ' + DB_TABLE_CREATE_INFO
        return False
    DB_TABLE_CREATE_INFO = DB_TABLE_CREATE_INFO.replace( "\n" , ' ' ).replace( '"' , '\\"' ).lower()
    DB_TABLE_CREATE_INFO = 'create table ' + table_name + ' ( ' + DB_TABLE_CREATE_INFO + ' );'

    #print node_list
    partition_index = 0
    for node in node_list:
        cmd_for_node[ node ] = []
        if chunks_per_node is 0 :    # use unchunked mode
            db_list = re.findall( '' + node +':[\d]+/(udb_' + table_name + '_'+'[\w]+)' , HadoopDB_Info )
            for sub_db in db_list :
                # Create Database & Table
                cmd_for_node[ node ].append( 'dropdb ' + sub_db )
                cmd_for_node[ node ].append( 'createdb ' + sub_db )
                cmd_for_node[ node ].append( 'echo "'+DB_TABLE_CREATE_INFO+'" | psql '+ sub_db )
                cmd_for_node[ node ].append( 'rm -rf /tmp/out_for_global_parition' )
  
             cmd_for_node[ node ].append( BIN_HADOOP + ' fs -get ' +
data_partition_out + '/part-%0.5d /tmp/out_for_global_parition' %
partition_index )
                cmd_for_node[ node ].append( 'echo
"COPY '+table_name+' FROM \'/tmp/out_for_global_parition\' WITH
DELIMITER E\''+data_field_delimiter+'\';" | psql '+ sub_db )
                cmd_for_node[ node ].append( 'rm -rf /tmp/out_for_global_parition' )
        else:
            db_list = re.findall( '' + node +':[\d]+/(cdb_' + table_name + '_'+'[\w]+)' , HadoopDB_Info )
            if db_list <> None :
                cmd_for_node[ node ].append( 'rm -rf /tmp/*out_for_global_parition' )
  
             cmd_for_node[ node ].append( BIN_HADOOP + ' fs -get ' +
data_partition_out + '/part-%0.5d /tmp/out_for_global_parition' %
partition_index )
                cmd_for_node[ node ].append( 'cd
/tmp; ' + BIN_JAVA + ' -cp ' + JAR_HADOOPDB + '
edu.yale.cs.hadoopdb.dataloader.LocalHasher out_for_global_parition ' +
str( chunks_per_node ) + ' \'' + data_delimiter + '\' 0 ' )
                sub_part = 0
                for sub_db in db_list :
                    # Create Database & Table
                    cmd_for_node[ node ].append( 'dropdb ' + sub_db )
                    cmd_for_node[ node ].append( 'createdb ' + sub_db )
                    cmd_for_node[ node ].append( 'echo "'+DB_TABLE_CREATE_INFO+'" | psql '+ sub_db )
  
                 cmd_for_node[ node ].append( 'echo "COPY
'+table_name+' FROM \'/tmp/'+str(sub_part)+'-out_for_global_parition\'
WITH DELIMITER E\''+data_field_delimiter+'\';" | psql '+ sub_db )

                    sub_part = sub_part + 1
                    #cmd_for_node[ node ].append( 'rm -rf /tmp/'+str(sub_part)+'-out_for_global_parition' )
                
                cmd_for_node[ node ].append( 'rm -rf /tmp/*out_for_global_parition' )

        partition_index = partition_index + 1
    
    print '\n=> To configure your nodes...\n'

    for node in node_list:
        thread.start_new_thread( executeThreadForNode , ( node, DEBUG_MODE ) )

    while (len(completed.keys()) < len(node_list) ) :
        os.system('sleep 2')

    if DEBUG_MODE :
        print '$ ' + BIN_HADOOP + ' fs -rmr ' + data_partition_out
    else:
        os.system( BIN_HADOOP + ' fs -rmr ' + data_partition_out )

    print '\n=> To setup the external table for Hive\n'
    if DEBUG_MODE :
        print '$ ' + BIN_HADOOP + ' fs -mkdir ' + hive_db_dir_in_hdfs
        print '$ ' + BIN_HADOOP + ' fs -rmr ' + hive_db_dir_in_hdfs + '/' + table_name
    else:
        os.system( BIN_HADOOP + ' fs -mkdir ' + hive_db_dir_in_hdfs )
        os.system( BIN_HADOOP + ' fs -rmr ' + hive_db_dir_in_hdfs + '/' + table_name  )
    
    cmd_exec = ' echo "drop table '+table_name+';" | ' + BIN_HIVE
    if DEBUG_MODE :
        print '$ ' + cmd_exec
    else:
        os.system( cmd_exec )

    create_hive_external_table =     ' ROW FORMAT DELIMITED FIELDS TERMINATED BY \'' + db_field_delimiter + '\''
    create_hive_external_table +=    ' STORED AS '
    create_hive_external_table +=    ' INPUTFORMAT \'edu.yale.cs.hadoopdb.sms.connector.SMSInputFormat\' '
    create_hive_external_table +=    ' OUTPUTFORMAT \'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat\' '
    create_hive_external_table +=    ' LOCATION \'' + hive_db_dir_in_hdfs + '/' + table_name + '\'; '

  
 DB_TABLE_CREATE_INFO = DB_TABLE_CREATE_INFO.replace( ";" , ' '
).replace( 'precision' , '' ).replace( 'create table' , 'create
external table' )
    DB_TABLE_CREATE_INFO = re.sub( 'varchar\([\d]+\)|text' , 'string' , DB_TABLE_CREATE_INFO )
    create_hive_external_table = DB_TABLE_CREATE_INFO + create_hive_external_table

    cmd_exec = ' echo "'+create_hive_external_table+'" | ' + BIN_HIVE
    if DEBUG_MODE :
        print '$ ' + cmd_exec
    else:
        os.system( cmd_exec )

def executeThreadForNode(node,DEBUG_MODE=True, *args ):
    for cmd in cmd_for_node[node] :
        if cmd == None or cmd == '' :
            continue;
        cmd = cmd.strip()
        if cmd == '' :
            continue;
        cmd = cmd.replace( '"' , '\\"' )
        cmd_exec = "ssh %s \"%s\"" % (node , cmd )
        print "\t" , cmd_exec
        if DEBUG_MODE == False :
            os.system( cmd_exec )
    completed[node] = "true"

def main( argv=None ):

    parser = OptionParser()
  
 parser.add_option( "-H" , "--source_dir_in_hdfs" ,
dest="source_dir_in_hdfs" , default=None, help="dir for data source in
HDFS" )
    parser.add_option( "-D" , "--source_data_delimiter" ,
dest="source_data_delimiter" , default='\n' , help="record delimtier
for the source" )
    parser.add_option( "-F" ,
"--source_field_delimiter" , dest="source_field_delimiter" ,
default='\t' , help="field delimiter for a record" )
  
 parser.add_option( "-P" , "--source_partition_dir" ,
dest="source_partition_dir" , default="tmp_out_hadoopdb" , help="temp
dir in HDFS for source partition" )
    parser.add_option( "-N"
, "--node_list_file" , dest="node_list_file" , default="nodes.txt" ,
help="path for a file saved each node's IP address" )
    parser.add_option( "-c" , "--chunk_num" , dest="chunk_num" , default=0 , help="number of databases for each node" )
  
 parser.add_option( "-t" , "--table_name" , dest="table_name" ,
default="hadoopdb" , help="table name for creation on Hive and
databases" )
    parser.add_option( "-i" , "--table_field_info_file"
, dest="table_field_info_file" , default="table_create", help="file for
table field definition only" )
    parser.add_option( "-u" ,
"--db_username" , dest="db_username" , default="hadoop" ,
help="username for login the databases on each node" )
  
 parser.add_option( "-p" , "--db_password" , dest="db_password" ,
default="1234" , help="password for login the databases on each node" )
  
 parser.add_option( "-d" , "--db_field_delimiter" ,
dest="db_field_delimiter" , default="|" , help="field delimiter for the
databases" )
    parser.add_option( "-w" , "--hive_db_dir" ,
dest="hive_db_dir" , default='/db' , help="path in HDFS for Hive to
save the tables" )
    parser.add_option( "-f" , "--catalog_properties"
, dest="catalog_properties" , default='/tmp/Catalog.properties' ,
help="output file for Catalog.Properties" )
    parser.add_option(
"-x" , "--hadoopdb_xml" , dest="hadoopdb_xml" , default="HadoopDB.xml"
, help="output file for HadoopDB.xml" )
    parser.add_option( "-y"
, "--hadoopdb_xml_in_hdfs" , dest="hadoopdb_xml_in_hdfs" ,
default="HadoopDB.xml" , help="filename for HadoopDB.xml in HDFS" )
  
 parser.add_option( "-g" , "--go" , action="store_false" , dest="mode"
, default=True , help="set it to execute the commands" )

    ( options, args ) = parser.parse_args()
    #print options
    #return    

  
 #initHadoopDB(    data_in_hdfs='src' , data_partition_out='tmp_out' ,
table_name='justtest' , table_field_info='table_create'  )

    if options.source_dir_in_hdfs is None :
        print "Please input the source dir in HDFS by '--source_dir_in_hdfs' "
        return
    if os.path.isfile( options.node_list_file ) is False :
        print "Please check the '" + options.node_list_file + "' path and setup by '--node_list_file'"

    if options.mode is True :
  
     print "\n Current Status is just Debug Mode for show all
commands\n please set '-g' or '--go' option to execute them after check
all commands.(look at the 'rm -rf' and 'hadoop fs -rmr')\n"
    
  
 initHadoopDB( data_in_hdfs = options.source_dir_in_hdfs,
data_delimiter = options.source_data_delimiter, data_field_delimiter =
options.source_field_delimiter,data_partition_out =
options.source_partition_dir, nodes_in_file = options.node_list_file,
chunks_per_node = options.chunk_num, table_name = options.table_name,
table_field_info = options.table_field_info_file, db_user =
options.db_username, db_pass = options.db_password, db_field_delimiter
= options.db_field_delimiter, hive_db_dir_in_hdfs =
options.hive_db_dir, tmp_path_for_catelog = options.catalog_properties,
out_hadoop_xml = options.hadoopdb_xml, hadoop_xml_in_hdfs =
options.hadoopdb_xml_in_hdfs, DEBUG_MODE = options.mode )

    print "\n\n=> All Execution Completed..."

if __name__ == "__main__":
    main()


另外有一些常見問題也順便紀錄:


hadoop@Cluster01:~$ echo "drop table justest;" | /home/hadoop/SMS_dist/bin/hive
Hive history file=/tmp/hadoop/hive_job_log_hadoop_201001081445_409015456.txt
hive> drop table justest;
FAILED:
Error in metadata: javax.jdo.JDOFatalDataStoreException: Failed to
start database 'metastore_db', see the next exception for details.
NestedThrowables:
java.sql.SQLException: Failed to start database 'metastore_db', see the next exception for details.
FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask
hive>


這類訊息是因為同時有兩個 client 在用 hive,同一時間只能有一個 client 在操作。



hadoop@Cluster01:~$ sudo apt-get install postgresql
hadoop@Cluster01:~$ sudo vim /etc/postgresql/8.4/main/pg_hba.conf


#local   all         all                               ident
local   all         all                               trust
# IPv4 local connections:
#host    all         all         127.0.0.1/32          md5
host    all         all         127.0.0.1/32          password
host    all         all         10.0.0.1/8          password            # 加上Cluster機器上的範圍
# IPv6 local connections:
#host    all         all         ::1/128               md5
host    all         all         ::1/128               password


hadoop@Cluster01:~$ sudo vim /etc/postgresql/8.4/main/postgresql.conf


listen_addresses = '*'


hadoop@Cluster01:~$ sudo /etc/init.d/postgresql-8.4 restart
hadoop@Cluster01:~$ sudo su - postgres
postgres@Cluster01:~$ createuser hadoop


Shall the new role be a superuser? (y/n) y


postgres@Cluster01:~$ psql

psql (8.4.2)
Type "help" for help.

postgres=# alter user hadoop with password '1234';
ALTER ROLE
postgres=# \q

postgres@Cluster01:~$ exit


logout



[PHP] 使用官方 Plurk API 實作簡單的機器人 - 靠機器人救 Karma!以 Yahoo News 為例

Yahoo! News & Plurk


靠機器人救 Karma!由於 Karma 值的不穩定,乾脆實做一隻 Plurk 機器人好了,這次實做參考 [PHP] Official Plurk API 之PHP - cURL 使用教學


Update @ 2011/07/26:新增縮網址的程式碼,因為新聞連結長度超過 144 字數的限制。


整體上就是實做一個撈 Yahoo 新聞的小程式,理論上是要用 RSS 才對,但因為我只想看焦點新聞,所以就改撈他的首頁進行處理,可以自行更新 getNews 函數吧!而這隻程式可以搭配 Windows 的排程或是 Unix 的 Crontab 運作,而程式環境上還要求 PHP-Curl 的支援囉!由於它非常陽春,所以我連帳密錯誤都沒確認就是了 :P


<?php
$cookie_db = '/tmp/http_cookie';
$log_db = '/tmp/job_log';
$hash_db = '/tmp/http_yahoo';
$bin_grep = '/bin/grep';

$plurk_api_key = 'YOUR_PLURK_API_KEY';
$plurk_id = 'LOGIN_ID';
$plurk_passwd = 'LOGIN_PASSWORD';

if( file_exists( $log_db ) )
    return;

        $result = do_act(
            'http://www.plurk.com/API/Users/login' ,
            'POST' ,
            array(
                'api_key'       => $plurk_api_key ,
                'username'      => $plurk_id ,
                'password'      => $plurk_passwd
            ) ,
            $cookie_db
        );

$source = getNews();
foreach( $source as $data )
{
    if( !check_exists( $data['url'] ) )
    {
        $target_url = NULL;
        if( strlen( $data['url'] ) < 100 )
           $target_url = $data['url'];
        else
            $target_url = getTinyurl( $data['url'] );

        if( !empty( $target_url ) )
        {
            $plurk = '[News]'.$target_url.' ('.$data['title'].')';
            $result = do_act(
                'http://www.plurk.com/API/Timeline/plurkAdd' ,
                'POST' ,
                array(
                    'api_key'       => $plurk_api_key ,
                    'qualifier'     => 'shares' , // loves , likes , shares , ...
                    'content'       => $plurk
                ) ,
                $cookie_db
            );
        }
        put_db( $data['url'] );
    }
}
exit;

function check_exists( $url )
{
    global $hash_db , $bin_grep;
    if( !file_exists( $hash_db ) )
        return false;
    $cmd = $bin_grep.' -c "'. md5($url) .'" '.$hash_db;
    $result = shell_exec( $cmd );
    $result = trim( $result );
    //echo "[$cmd]\n[$result]\n";
    return !empty( $result );
}
function put_db( $url )
{
    global $hash_db , $log_db ;
    if( !file_exists( $hash_db ) )
        $fp = fopen( $hash_db , 'w' );
    else
        $fp = fopen( $hash_db , 'a' );
    if( $fp )
    {
        fwrite( $fp , md5($url) . "\n" );
        fclose( $fp );
    }
    else
        file_put_contents( $log_db , "Error" );
}
function getNews()
{
    global $log_db;
    $out = array();
    $raw = file_get_contents( 'http://tw.yahoo.com/' );
    $pattern = '<label>';
    $raw = stristr( $raw , $pattern );
    if( empty( $raw ) )
    {
        file_put_contents( $log_db , "Parser Error 1" );
        return $out;
    }
    $raw = substr( $raw , strlen( $pattern ) );
    $pattern = '<ol>';
    $finish = strpos( $raw , $pattern );
    if( $finish < 0 )
    {
        file_put_contents( $log_db , "Parser Error 2" );
        return $out;
    }
    $raw = substr( $raw , 0 , $finish );
    if( empty( $raw ) )
        return $out;
    $pattern = '{<h3[^>]*>[^<]*<a href="(.*?)"[^>]*>(.*?)</a></h3>}is';
    if( preg_match_all( $pattern , $raw , $matches ) )
    {
        for( $i=0 , $cnt=count( $matches[1] ) ; $i<$cnt ; ++$i )
        {
            array_push( $out , array(
                'url' => strstr( $matches[1][$i] , 'http:' ) ,
                'title' => $matches[2][$i] )
            );
        }
    }
    else
        file_put_contents( $log_db , "Parser Error 3" );
    return $out;
}
function do_act( $target_url , $type , $data , $cookie_file = NULL )
{
        $ch = curl_init();
        if( $type == 'GET' )    // GET
        {
                $target_url .= http_build_query( $data );
                curl_setopt($ch, CURLOPT_URL, $target_url );
        }
        else                    // POST
        {
                curl_setopt( $ch , CURLOPT_URL , $target_url );
                curl_setopt( $ch , CURLOPT_POST , true );
                curl_setopt( $ch , CURLOPT_POSTFIELDS , http_build_query( $data ) );
        }

        if( isset( $cookie_file ) )     // cookie
        {
                curl_setopt( $ch , CURLOPT_COOKIEFILE , $cookie_file );
                curl_setopt( $ch , CURLOPT_COOKIEJAR , $cookie_file );
        }

        curl_setopt( $ch , CURLOPT_RETURNTRANSFER , true );
        //curl_setopt( $ch , CURLOPT_FOLLOWLOCATION , true );
        //curl_setopt( $ch , CURLOPT_SSL_VERIFYPEER , false );

        $result = curl_exec( $ch );
        curl_close( $ch );
        return $result;
}
function getTinyurl( $url )
{
        $new_url = @file_get_contents( 'http://tinyurl.com/api-create.php?url='.urlencode($url) );
        $new_url = trim( $new_url );
        if( !empty( $new_url ) )
            return $new_url;
        return NULL;
}
?>


2010年1月4日 星期一

[Linux] 安裝 Hadoop 0.20.1 Multi-Node Cluster @ Ubuntu 9.10


圖片來源:http://hadoop.apache.org/


今天共用了三台機器,終於真正架了一個 Hadoop Cluster ,在這之前都只是安裝在一台機器上:[Linux] 安裝單機版 Hadoop 0.20.1 Single-Node Cluster (Pseudo-Distributed) @ Ubuntu 9.04。由於想嘗試 HadoopDB
,因此在工作場所裡透過 VMWare Workstation 安裝 Hadoop 0.19.2 版,並且碰到一堆有的沒有的問題,連
Hadoop 的 wordcount 範例都跑不起來!盡管最後有設定成功,但太不熟了,回寢室後再試一次,順便替自己架設個人把玩的開發環境啦!


環境:Windows 7 x64 + VirtualBox
3.12 r56127 ,預計安裝 3 台機器的 Cluster 囉。原本打算使用 ubuntu-9.10-server-amd64.iso
,但 VirtualBox 啟動有些問題,因此改成 ubuntu-9.10-server-i386.iso ,並且在安裝過程中,給予 20
GB 硬碟空間,以及最後建立 hadoop 使用者,接下來的架設方式是設定好一台虛擬機器後,透過複製虛擬機器的方式來新增機器。


安裝好一台虛擬機器後,緊接著進行更新與軟體的安裝



  • VirtualBox 環境設定

    • 新增網卡

      • 預設只有一張網卡,代號應該是 eth0 ,通常會用它來連外,常用的設置是 NAT 方式

      • 建議再加一張網卡,用來讓自己的電腦(Host OS)能連進虛擬機器(Guest OS),詳情可參考這篇 讓本機可連虛擬機器,虛擬機器可連外 - VirtualBox 網路設定,雖然這並非需要,但有這個就可以透過 ssh client (Putty)連進去,而不需要透過 VirualBox 的介面。

      • 接下來還需要建立一個固定的 IP ,而第二張 eth1 網卡採用 VirtualBox 之 「僅限主機」介面卡 (Host-Only)
        即可,它提供 DHCP 服務,而 eth1 除了可以用來與自己的電腦溝通,還可以另外再對 eth1 多設定一個 Static IP 供
        Cluster 內部溝通



    • 在 Linux or Ubuntu 的機器上,若設定完網卡後,或是使用複製的虛擬機器時,透過 $ ifconfig 卻看不到網路卡,可以使用以下方式試試

      • $ sudo rm /etc/udev/rules.d/70-persistent-net.rules
        $ sudo reboot



    • 透過 $ ifconfig 可以看到 eth1 網卡後,接著使用 $ sudo dhclient eth1 更新來獲得一組 IP ,而 Host-Only 本來就有提供 DHCP 服務,此時主要用來測試網路的設定是否正常

    • 如果都沒問題,那就可以設定讓它開機就自動弄好,並且將 eth1:0 設定成 Static IP

      • $ sudo vim /etc/network/interfaces
        # The loopback network interface
        auto lo
        iface lo inet loopback

        # The primary network interface
        auto eth0
        iface eth0 inet dhcp

        # Host-Only
        auto eth1
        iface eth1 inet dhcp

        auto eth1:0
                iface eth1:0 inet static
                address 192.168.56.168
                netmask 255.255.0.0

        • 之所以設定成 192.168.56.168 是因為預設的 Host-Only 是 192.168.56.x 網域的,用同網域的另一個好處是自己的電腦 (Host OS)可以用這個固定 IP 來連線,不需每次要用就得查詢一次,但實際使用上此 DHCP 都分配 192.168.56.101 之後的 IP ,因此也需要小心設定,以免衝 IP 囉。





    • 設定完後,除了可以透過 $ sudo reboot 啟用 IP ,也可以透過指令更新 $ sudo /etc/init.d/network restart



  • 更新系統

    • $ sudo apt-get update
      $ sudo apt-get dist-upgrade
      $ sudo apt-get upgrade

    • 另外還有個東西叫做 aptitude 對我而言也挺新鮮的,詳情請逛逛 Aptitude - Ubuntu 正體中文 Wiki


      • $ sudo aptitude safe-upgrade
        $ sudo aptitude full-upgrade
        $ sudo aptitude dist-upgrade





  • 安裝軟體或相關準備

    • 建立暫放軟體的地方 /home/tmp

      • $ sudo mkdir /home/tmp ; sudo chown -R hadoop:hadoop /home/tmp ;



    • $ sudo apt-get install ssh rsync 

      • 有些東西已有安裝,執行上道指令只試再次確認囉,像是 openssh-server 等。安裝完 openssh-server 後,建議可以透過 Putty 登入,接下來的操作會比較方便。



    • 安裝 Java 環境

      • 第一種方式:$ sudo apt-get install sun-java6-sdk

      • 第二種方式:為了方便製作成 Portable 環境,採用類似 Tarball 的安裝方式,請至 Java SE Downloads - Sun Developer Network (SDN) 下載 Linux 的 SDK6 (Ex: jdk-6u17-linux-i586.bin),目前打算用 Java 1.6 的開發環境。如果使用 64-Bit 環境,下載時可以選擇 Linux x64 。

        • 將下載得來的 jdk-6u17-linux-i586.bin 擺在 /home/tmp (可以用sftp傳進去)。





    • 下載 Hadoop

      • $ cd /home/tmp ;
        $ wget http://ftp.mirror.tw/pub/apache/hadoop/core/hadoop-0.20.1/hadoop-0.20.1.tar.gz

      • 以下是我自己額外的使用

        • $ wget http://ftp.mirror.tw/pub/apache/hadoop/core/hadoop-0.19.2/hadoop-0.19.2.tar.gz

        • $ wget 'http://downloads.sourceforge.net/project/hadoopdb/hadoopdb/0.1.1/hadoopdb-all-0.1.1.0.tar.gz?use_mirror=nchc'

        • $ wget http://jdbc.postgresql.org/download/postgresql-8.4-701.jdbc4.jar

        • 好用的 screen

          • $ vim ~/.screenrc

            • caption always "%{bw}%M/%d %c %{wb} %-w%{c}%n %t%{w}%+w%{k} %=%{G}[%H] %l%"











  • 經過上述的軟體設定後,再接著做事之前,可以考慮先備份一下目前的虛擬環境,以方便往後取出備份

    • "c:\Program Files\Sun\VirtualBox\VBoxManage.exe" clonevdi Cluster01.vdi Cluster_backup.vdi

    • 由於 VirtualBox 的 *.vdi 有一些硬體資訊載裡頭(EX:UUID),所以無法像 VMWare 那樣可以選 I copy it 的選項來自動幫你處理,因此就需要透過上述的指令來複製虛擬機器。



  • 設置統一的 Hadoop 環境

    • 選擇你想要安裝的版本進行設定,此例以 hadoop 0.20.1

    • $ cd /home/tmp ;
      $ tar -xvf hadoop-0.20.1.tar.gz;
      $ sh jdk-6u17-linux-i586.bin ;

    • 接著把 hadoop-0.20.1 和 jdk1.6.0_17 搬到家目錄吧!

      • $ cp -r hadoop-0.20.1/* ~/
        $ mv jdk1.6.0_17 ~/



    • 設定 Java 環境

      • $ vim conf/hadoop-env.sh

        • export JAVA_HOME=/home/hadoop/jdk1.6.0_17





    • 設定停用 IPv6

      • $ vim conf/hadoop-env.sh



        • HADOOP_OPTS=-Djava.net.preferIPv4Stack=true



      • 可能是因為 Hadoop 對 IPv6 網路支援不穩,常常會莫名連不到其他機器,設定這個至少在此刻還滿重要的!



    • 設定 /etc/hosts 與 /etc/hostname ,以三台電腦為例,目前這台當作 Cluster01 ,假設之後還會有 Cluster02 和 Cluster03 ( elalic 網友提醒!請勿使用底線"_"來描述電腦,有連不到機器的現象發生)


      • $ sudo vim /etc/hostname
        Cluster01

      • $ sudo vim /etc/hosts
        #127.0.1.1      Cluster01   (此行代表註解)
        192.168.56.168  Cluster01
        192.168.56.169  Cluster02
        192.168.56.170  Cluster03



    • 設定 conf/core-site.xml 、 conf/mapred-site.xml


      • $ vim ~/conf/core-site.xml

        •         <property>
                          <name>fs.default.name</name>
                          <value>hdfs://Cluster01:9000</value>
                          <description>The name of the default file system. A URI whose
                          scheme and authority determine the FileSystem implementation. The
                          uri's scheme determines the config property (fs.SCHEME.impl) naming
                          the FileSystem implementation class.  The uri's authority is used to
                          determine the host, port, etc. for a filesystem.</description>
                  </property>



      • $ vim ~/conf/mapred-site.xml

        •         <property>
                          <name>mapred.job.tracker</name>
                          <value>Cluster01:9001</value>
                          <description>The host and port that the MapReduce job tracker runs
                          at.  If "local", then jobs are run in-process as a single map
                          and reduce task.
                          </description>
                  </property>





    • 再啟動 Hadoop 前,將另外兩台電腦備妥,使用複製虛擬機器的方式新增,並且使用 VirtualBox 開啟它們,使得同一時間有三台虛擬機器


      • "c:\Program Files\Sun\VirtualBox\VBoxManage.exe" clonevdi Cluster01.vdi Cluster02.vdi
        "c:\Program Files\Sun\VirtualBox\VBoxManage.exe" clonevdi Cluster01.vdi Cluster03.vdi

      • 接著在 VirutalBox [檔案]->[虛擬媒體管理員]->[硬碟]->[加入] ,將上述
        Cluster02.vdi 和 Cluster03.vdi 加入,接著回到 VirtualBox
        主畫面點選[新增]機器,設定好你要的名稱(Ex: Cluster02 和 Cluster03)以及 Linux/Ubuntu
        後,有個地方是要新增"虛擬硬碟",請選用"使用現有硬碟",依序分別挑選 Cluster02.vdi 和 Cluster03.vdi
        以開啟兩台虛擬機器。

      • 請幫它們加上二張網路卡 - 選擇 「僅限主機」介面卡 (VirtualBox Host-Only)

      • 開機後請先更新 /etc/hostname 及 /etc/network/interfaces

        • $ sudo vim /etc/hostname

          • 修改成 Cluster02 或 Cluster03



        • $ sudo vim /etc/network/interfaces

          • auto eth1:0
                    iface eth1:0 inet static
                    address 192.168.56.169 # or address 192.168.56.170
                    netmask 255.255.0.0





      • 機器啟動後,會發現無法連線以及看不到網路卡 ( $ ifconfig ) ,請執行以下動作即可解決

        • $ sudo rm /etc/udev/rules.d/70-persistent-net.rules
          $ sudo reboot





    • 以下在 Cluster01 操作


      • 設定 SSH 登入免密碼

        • $ ssh-keygen -t rsa -P ''
          $ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys


          • 可以用 ssh localhost 測試是否成功,若能不輸入密碼登入則代表成功



        • 將 Cluster 01 上的 authorized_keys 複製給 Cluster02 和 Cluster03

          • $ scp ~/.ssh/authorized_keys hadoop@Cluster02:~/.ssh/authorized_keys
            $ scp ~/.ssh/authorized_keys hadoop@Cluster03:~/.ssh/

            • 若出現 scp: /home/hadoop/.ssh/: Is a directory 這類訊息,則先去該台機器使用 ssh 囉,例如連去其他台機器等



          • 分別執行 $ ssh Cluster01 、 $ ssh Cluster02 和 $ssh Cluster03 試試,若可以免密碼登入即沒問題







      • 設定 conf/masters 和 conf/slaves

        • $ vim conf/masters

          • Cluster01



        • $ vim conf/slaves

          • Cluster02

            Cluster03





      • 初始化 NameNode

        • $ ~/bin/hadoop namenode -format





      • 啟動 Hadoop

        • $ ~/bin/start-all.sh


          • 應該會看到類似的訊息:
            hadoop@Cluster01:~$ ~/bin/start-all.sh
            starting namenode, logging to /home/hadoop/bin/../logs/hadoop-hadoop-namenode-Cluster01.out
            Cluster02: starting datanode, logging to /home/hadoop/bin/../logs/hadoop-hadoop-datanode-Cluster02.out
            Cluster03: starting datanode, logging to /home/hadoop/bin/../logs/hadoop-hadoop-datanode-Cluster03.out
            Cluster01: starting secondarynamenode, logging to /home/hadoop/bin/../logs/hadoop-hadoop-secondarynamenode-Cluster01.out
            starting jobtracker, logging to /home/hadoop/bin/../logs/hadoop-hadoop-jobtracker-Cluster01.out
            Cluster02: starting tasktracker, logging to /home/hadoop/bin/../logs/hadoop-hadoop-tasktracker-Cluster02.out
            Cluster03: starting tasktracker, logging to /home/hadoop/bin/../logs/hadoop-hadoop-tasktracker-Cluster03.out



        • $ ~/jdk1.6.0_17/bin/jps

          • 應該會看到類似的訊息:
            hadoop@Cluster01:~$ jdk1.6.0_17/bin/jps
            1783 Jps
            1631 SecondaryNameNode
            1693 JobTracker
            1470 NameNode



        • $ ~/bin/hadoop dfsadmin -report

          • 應該會看到類似的訊息:
            hadoop@Cluster01:~$ ~/bin/hadoop dfsadmin -report
            Configured Capacity: 39905583104 (37.16 GB)
            Present Capacity: 34398027776 (32.04 GB)
            DFS Remaining: 34397978624 (32.04 GB)
            DFS Used: 49152 (48 KB)
            DFS Used%: 0%
            Under replicated blocks: 0
            Blocks with corrupt replicas: 0
            Missing blocks: 0

            -------------------------------------------------
            Datanodes available: 2 (2 total, 0 dead)

            Name: 192.168.56.103:50010
            Decommission Status : Normal
            Configured Capacity: 19952791552 (18.58 GB)
            DFS Used: 24576 (24 KB)
            Non DFS Used: 2753777664 (2.56 GB)
            DFS Remaining: 17198989312(16.02 GB)
            DFS Used%: 0%
            DFS Remaining%: 86.2%
            Last contact: Mon Jan 04 22:59:29 CST 2010


            Name: 192.168.56.102:50010
            Decommission Status : Normal
            Configured Capacity: 19952791552 (18.58 GB)
            DFS Used: 24576 (24 KB)
            Non DFS Used: 2753777664 (2.56 GB)
            DFS Remaining: 17198989312(16.02 GB)
            DFS Used%: 0%
            DFS Remaining%: 86.2%
            Last contact: Mon Jan 04 22:59:28 CST 2010





        • 以 WordCount 測試 Hadoop 看看

          • $ ~/bin/hadoop fs -mkdir input

          • $ ~/bin/hadoop fs -put README.txt input/

          • $ ~/bin/hadoop jar hadoop-0.20.1-examples.jar wordcount input output
            10/01/04 23:01:44 INFO input.FileInputFormat: Total input paths to process : 1
            10/01/04 23:01:44 INFO mapred.JobClient: Running job: job_201001042257_0001
            10/01/04 23:01:45 INFO mapred.JobClient:  map 0% reduce 0%
            10/01/04 23:01:54 INFO mapred.JobClient:  map 100% reduce 0%
            10/01/04 23:02:06 INFO mapred.JobClient:  map 100% reduce 100%
            10/01/04 23:02:08 INFO mapred.JobClient: Job complete: job_201001042257_0001
            10/01/04 23:02:08 INFO mapred.JobClient: Counters: 17
            10/01/04 23:02:08 INFO mapred.JobClient:   Job Counters
            10/01/04 23:02:08 INFO mapred.JobClient:     Launched reduce tasks=1
            10/01/04 23:02:08 INFO mapred.JobClient:     Launched map tasks=1
            10/01/04 23:02:08 INFO mapred.JobClient:     Data-local map tasks=1
            10/01/04 23:02:08 INFO mapred.JobClient:   FileSystemCounters
            10/01/04 23:02:08 INFO mapred.JobClient:     FILE_BYTES_READ=1836
            10/01/04 23:02:08 INFO mapred.JobClient:     HDFS_BYTES_READ=1366
            10/01/04 23:02:08 INFO mapred.JobClient:     FILE_BYTES_WRITTEN=3704
            10/01/04 23:02:08 INFO mapred.JobClient:     HDFS_BYTES_WRITTEN=1306
            10/01/04 23:02:08 INFO mapred.JobClient:   Map-Reduce Framework
            10/01/04 23:02:08 INFO mapred.JobClient:     Reduce input groups=0
            10/01/04 23:02:08 INFO mapred.JobClient:     Combine output records=131
            10/01/04 23:02:08 INFO mapred.JobClient:     Map input records=31
            10/01/04 23:02:08 INFO mapred.JobClient:     Reduce shuffle bytes=1836
            10/01/04 23:02:08 INFO mapred.JobClient:     Reduce output records=0
            10/01/04 23:02:08 INFO mapred.JobClient:     Spilled Records=262
            10/01/04 23:02:08 INFO mapred.JobClient:     Map output bytes=2055
            10/01/04 23:02:08 INFO mapred.JobClient:     Combine input records=179
            10/01/04 23:02:08 INFO mapred.JobClient:     Map output records=179
            10/01/04 23:02:08 INFO mapred.JobClient:     Reduce input records=131

          • $ ~/bin/hadoop dfs -cat output/p*



        • 關掉 hadoop


          • $ ./bin/stop-all.sh
            stopping jobtracker
            Cluster02: stopping tasktracker
            Cluster03: stopping tasktracker
            stopping namenode
            Cluster02: stopping datanode
            Cluster03: stopping datanode
            Cluster01: stopping secondarynamenode







    • 整體上就這樣,果然回家做一次就非常順,一次就成功啊!收工!




以下是一些參考資料



除此之外,在公司安裝 Hadoop 0.19.2 時,碰到了以下問題



但真正發現,出問題的是因為 datanode 有一台的 /etc/hosts 設定有問題,修正後就沒問題啦。


其他在 hadoop 0.20.1 碰到的問題



  • hdfs.DFSClient: DataStreamer Exception:
    org.apache.hadoop.ipc.RemoteException: java.io.IOException: File
    /path/file could only be replicated to 0 nodes, instead of 1

    • $ hadoop fs -mkdir in

      $ hadoop fs -put README.txt in

      10/01/19 08:35:13 WARN hdfs.DFSClient: DataStreamer Exception:
      org.apache.hadoop.ipc.RemoteException: java.io.IOException: File
      /user/hadoop/in/README.txt co

      uld only be replicated to 0 nodes, instead of 1

              at org.apache.hadoop.hdfs.server.namenode.FSNamesystem.getAdditionalBlock(FSNamesystem.java:1267)

              at org.apache.hadoop.hdfs.server.namenode.NameNode.addBlock(NameNode.java:422)

              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

              at java.lang.reflect.Method.invoke(Method.java:597)

              at org.apache.hadoop.ipc.RPC$Server.call(RPC.java:508)

              at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:959)

              at org.apache.hadoop.ipc.Server$Handler$1.run(Server.java:955)

              at java.security.AccessController.doPrivileged(Native Method)

              at javax.security.auth.Subject.doAs(Subject.java:396)

              at org.apache.hadoop.ipc.Server$Handler.run(Server.java:953)



              at org.apache.hadoop.ipc.Client.call(Client.java:739)

              at org.apache.hadoop.ipc.RPC$Invoker.invoke(RPC.java:220)

              at $Proxy0.addBlock(Unknown Source)

              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

              at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

              at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

              at java.lang.reflect.Method.invoke(Method.java:597)

              at org.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:82)

              at org.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:59)

              at $Proxy0.addBlock(Unknown Source)

              at org.apache.hadoop.hdfs.DFSClient$DFSOutputStream.locateFollowingBlock(DFSClient.java:2904)

              at org.apache.hadoop.hdfs.DFSClient$DFSOutputStream.nextBlockOutputStream(DFSClient.java:2786)

              at org.apache.hadoop.hdfs.DFSClient$DFSOutputStream.access$2000(DFSClient.java:2076)

              at org.apache.hadoop.hdfs.DFSClient$DFSOutputStream$DataStreamer.run(DFSClient.java:2262)



      10/01/19 08:35:13 WARN hdfs.DFSClient: Error Recovery for block null bad datanode[0] nodes == null

      10/01/19 08:35:13 WARN hdfs.DFSClient: Could not get block locations. Source file "/user/hadoop/in/README.txt" - Aborting...

      put: java.io.IOException: File /user/hadoop/in/README.txt could only be replicated to 0 nodes, instead of 1

    • 解法:請再三確認各台機器的 /etc/hosts 與 IP 狀況(ifconfig),如果都沒有問題,那等一陣子才試試,有時候環境剛初始化完會有這個現象,過一陣子之後就好了,另外可用 hadoop dfsadmin -report 來確認 datanode 是否正常跑起來,以及到各台機器用 jdk1.6.0_17/bin/jps 來查看 slaves ,正常要會有 TaskTracker 和 DataNode 。