文章介紹瞭如何整合虛擬化和Hadoop,讓Hadoop集羣跑在VPS虛擬主機上,通過雲向用戶提供存儲和計算的服務。
現在硬件越來越便宜,一臺非品牌服務器,2顆24核CPU,配48G內存,2T的硬盤,已經降到2萬塊人民幣以下了。這種配置如果簡單地放幾個web應用,顯然是奢侈的浪費。就算是用來實現單節點的hadoop,對計算資源浪費也是非常高的。對於這麼高性能的計算機,如何有效利用計算資源,就成爲成本控制的一項重要議題了。
通過虛擬化技術,我們可以將一臺服務器,拆分成12臺VPS,每臺2核CPU,4G內存,40G硬盤,並且支持資源重新分配。多麼偉大的技術啊!現在我們有了12個節點的hadoop集羣, 讓Hadoop跑在雲端,讓世界加速。
前言
ZooKeeper是一個強大的分佈式協作系統,用ZooKeeper可以方便地實現先進先出(FIFO)隊列。給“隊列”的技術現實多一種選擇,標準化我們的程序結構。另一篇,分步式同步隊列實現,請參考:ZooKeeper實現分佈式隊列Queue
關於ZooKeeper的基本使用,請參考:ZooKeeper僞分步式集羣安裝及使用。
1. 分佈式先進先出(FIFO)隊列
在計算機科學中,消息隊列(Message queue)是一種進程間通信或同一進程的不同線程間的通信方式。消息隊列提供了異步的通信協議,消息的發送者和接收者不需要同時與消息隊列互交。消息會保存在隊列中,直到接收者取回它。
先進先出(FIFO)隊列,是消息隊列最基本的一種實現形式,先發出的先消費。
2. 設計思路
實現的思路也非常簡單,在/queue-fifo的目錄下創建 SEQUENTIAL 類型的子目錄 /x(i),這樣就能保證所有成員加入隊列時都是有編號的,出隊列時通過 getChildren( ) 方法可以返回當前所有的隊列中的元素,然後消費其中最小的一個,這樣就能保證FIFO。
應用實例
圖標解釋
1.app1,app2,app3是3個獨立的業務系統
2.zk1,zk2,zk3是ZooKeeper集羣的3個連接點
3./queue-fifo,是znode的隊列,按順序存儲數據
4./queue-fifo/x1,是znode隊列中,1號排對者,由app1提交
5./queue-fifo/x2,是znode隊列中,2號排對者,由app2提交
6.app3是消費者,通過zk3連接到znode隊列中,找到/queue-fifo中順序最少的節點消費,刪除消費後的節點(紅色線表示)
注:
1). app1可以通過zk2提交,app2也可通過zk3提交
2). app1可以提交3次請求,生成x1,x2,x3多個節點
3). app1可以作爲消費者,消費隊列數據
3. 程序實現
1). 單節點模擬實驗
模擬app1,通過zk1,生產2個節點,然後再消費3個節點。
public static void doOne() throws Exception { String host1 = "192.168.1.201:2181"; ZooKeeper zk = connection(host1); initQueue(zk); produce(zk, 1); produce(zk, 2); cosume(zk); cosume(zk); cosume(zk); zk.close(); } |
創建一個與服務器的連接
public static ZooKeeper connection(String host) throws IOException { ZooKeeper zk = new ZooKeeper(host, 60000, null); return zk; } |
出始化隊列
public static ZooKeeper connection(String host) throws IOException { return new ZooKeeper(host, 60000, new Watcher() { public void process(WatchedEvent event) { } }); } |
生產者
public static void produce(ZooKeeper zk, int x) throws KeeperException, InterruptedException { System.out.println("create /queue-fifo/x" + x + " x" + x); zk.create("/queue-fifo/x" + x, ("x" + x).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } |
消費者
public static void cosume(ZooKeeper zk) throws KeeperException, InterruptedException { List list = zk.getChildren("/queue-fifo", true); if (list.size() > 0) { long min = Long.MAX_VALUE; for (String num : list) { if (min > Long.parseLong(num.substring(1))) { min = Long.parseLong(num.substring(1)); } } System.out.println("delete /queue/x" + min); zk.delete("/queue-fifo/x" + min, 0); } else { System.out.println("No node to cosume"); } } |
啓動main函數
public static void main(String[] args) throws Exception { doOne(); } |
運行結果:
/queue-fifo is exist! create /queue-fifo/x1 x1 create /queue-fifo/x2 x2 delete /queue/x10000000032 delete /queue/x20000000033 No node to cosume |
完全符合我的們預期,由於produce時,我們創建的節點模式是EPHEMERAL_SEQUENTIAL,所以系統會在x(i)(n),隨機生成n=0000000032,輸出爲x10000000032。
接下來我們看分佈式環境。
2). 分佈式模擬實驗
app1通過zk1生產x1, app2通過zk2生產x2, app3通過zk3消費3個節點
public static void doAction(int client) throws Exception { String host1 = "192.168.1.201:2181"; String host2 = "192.168.1.201:2182"; String host3 = "192.168.1.201:2183"; ZooKeeper zk = null; switch (client) { case 1: zk = connection(host1); initQueue(zk); produce(zk, 1); break; case 2: zk = connection(host2); initQueue(zk); produce(zk, 2); break; case 3: zk = connection(host3); initQueue(zk); cosume(zk); cosume(zk); cosume(zk); break; } } |
啓動main函數
public static void main(String[] args) throws Exception { if (args.length == 0) { doOne(); } else { doAction(Integer.parseInt(args[0])); } } |
程序啓動方法,分3次啓動,命令行傳不同的參數,分別是1,2,3
run1: 執行app1–>zk1
#日誌輸出 /queue-fifo is exist! create /queue-fifo/x1 x1 |
run2: 執行app2–>zk2
#日誌輸出 /queue-fifo is exist! create /queue-fifo/x2 x2 |
run3: 執行app3–>zk3
#日誌輸出 /queue-fifo is exist! delete /queue/x10000000034 delete /queue/x20000000035 No node to cosume |
我們完成分佈式隊列的實驗,由於時間倉促。文字說明及代碼難免有一些問題,請發現問題的同學幫忙指正。
下面貼一下完整的代碼:
package org.conan.zookeeper.demo; import java.io.IOException; import java.util.List; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.ZooDefs.Ids; import org.apache.zookeeper.ZooKeeper; public class FIFOZooKeeper { public static void main(String[] args) throws Exception { if (args.length == 0) { doOne(); } else { doAction(Integer.parseInt(args[0])); } } public static void doOne() throws Exception { String host1 = "192.168.1.201:2181"; ZooKeeper zk = connection(host1); initQueue(zk); produce(zk, 1); produce(zk, 2); cosume(zk); cosume(zk); cosume(zk); zk.close(); } public static void doAction(int client) throws Exception { String host1 = "192.168.1.201:2181"; String host2 = "192.168.1.201:2182"; String host3 = "192.168.1.201:2183"; ZooKeeper zk = null; switch (client) { case 1: zk = connection(host1); initQueue(zk); produce(zk, 1); break; case 2: zk = connection(host2); initQueue(zk); produce(zk, 2); break; case 3: zk = connection(host3); initQueue(zk); cosume(zk); cosume(zk); cosume(zk); break; } } // 創建一個與服務器的連接 public static ZooKeeper connection(String host) throws IOException { return new ZooKeeper(host, 60000, new Watcher() { public void process(WatchedEvent event) { } }); } public static void initQueue(ZooKeeper zk) throws KeeperException, InterruptedException { if (zk.exists("/queue-fifo", false) == null) { System.out.println("create /queue-fifo task-queue-fifo"); zk.create("/queue-fifo", "task-queue-fifo".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); } else { System.out.println("/queue-fifo is exist!"); } } public static void produce(ZooKeeper zk, int x) throws KeeperException, InterruptedException { System.out.println("create /queue-fifo/x" + x + " x" + x); zk.create("/queue-fifo/x" + x, ("x" + x).getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL); } public static void cosume(ZooKeeper zk) throws KeeperException, InterruptedException { List list = zk.getChildren("/queue-fifo", true); if (list.size() > 0) { long min = Long.MAX_VALUE; for (String num : list) { if (min > Long.parseLong(num.substring(1))) { min = Long.parseLong(num.substring(1)); } } System.out.println("delete /queue/x" + min); zk.delete("/queue-fifo/x" + min, 0); } else { System.out.println("No node to cosume"); } } } |