ZooKeeper
ZooKeeper 是一個開源的分散式協調服務,ZooKeeper框架最初是在“Yahoo!"上構建的,用於以簡單而穩健的方式訪問他們的應用程式。 後來,Apache ZooKeeper成為Hadoop,HBase和其他分散式框架使用的有組織服務的標準。 例如,Apache HBase使用ZooKeeper跟蹤分散式資料的狀態。ZooKeeper 的設計目標是將那些複雜且容易出錯的分散式一致性服務封裝起來,構成一個高效可靠的原語集,並以一系列簡單易用的介面提供給使用者使用。
ZooKeeper 通常用於:命名服務、配置管理、叢集管理、分散式協調/通知、分散式鎖和分散式佇列等等。
各個爬蟲的節點通過註冊到 ZooKeeper 從而實現爬蟲叢集的管理。NetDiscovery 正是藉助了 ZooKeeper 的特性來監控爬蟲叢集。
NetDiscovery 是一款基於 Vert.x、RxJava 2 等框架實現的通用爬蟲框架。它包含了豐富的特性。
爬蟲叢集的監控
NetDiscovery 包含了 Spider 和 SpiderEngine。 Spider 用於實現爬蟲的業務邏輯,Spider 可以新增到 SpiderEngine,由 SpiderEngine 來管理各個 Spider 的生命週期。
但是 SpiderEngine 部署到每一個節點之後,SpiderEngine 如何進行監控和管理呢?
可以將 SpiderEngine 在執行時,先註冊到 ZooKeeper。(需要事先在 ZooKeeper 叢集建立 /netdiscovery 節點)
/**
* 啟動SpiderEngine中所有的spider,讓每個爬蟲並行執行起來。
*
*/
public void run() {
if (Preconditions.isNotBlank(spiders)) {
registerZK();
......  
}
}
/**
* 將當前 SpiderEngine 註冊到 zookeeper 指定的目錄 /netdiscovery 下
*/
private void registerZK() {
if (Preconditions.isNotBlank(zkStr) && useZk) {
log.info("zkStr: {}", zkStr);
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
CuratorFramework client = CuratorFrameworkFactory.newClient(zkStr, retryPolicy);
client.start();
try {
String ipAddr = InetAddress.getLocalHost().getHostAddress() + "-" + defaultHttpdPort + "-" + System.currentTimeMillis();
String nowSpiderEngineZNode = "/netdiscovery/" + ipAddr;
client.create().withMode(CreateMode.EPHEMERAL).forPath(nowSpiderEngineZNode,nowSpiderEngineZNode.getBytes());
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製程式碼
另外,需要使用 NetDiscovery Monitor 的 CuratorManager 類。 它藉助 Zookeeper 的 Watcher 機制,監聽已經註冊到 /netdiscovery 這個父 zNode 下的各個子 zNode ,也就是各個 SpiderEngine。
Watcher機制是指 ZooKeeper 客戶端向 ZooKeeper 伺服器註冊 Watcher 的同時,會將 Watcher 物件儲存在客戶端的 WatchManager 中。ZooKeeper 伺服器觸發 Watcher 事件後,會向客戶端傳送通知,客戶端執行緒從 WatchManager 中回撥 Watcher 執行相應的功能。
/**
* 當前所監控的父的 zNode 下若是子 zNode 發生了變化:新增,刪除,修改
* <p>
* 下述方法都會觸發執行
*
* @param event
*/
@Override
public void process(WatchedEvent event) {
List<String> newZodeInfos = null;
try {
newZodeInfos = client.getChildren().usingWatcher(this).forPath("/netdiscovery");
//根據初始化容器的長度與最新的容器的長度進行比對,就可以推匯出當前 SpiderEngine 叢集的狀態:新增,當機/下線,變更...
//哪個容器中元素多,就迴圈遍歷哪個容器。
if (Preconditions.isNotBlank(newZodeInfos)) {
if (newZodeInfos.size()>allZnodes.size()){
//明確顯示新增了哪個 SpiderEngine 節點
for (String nowZNode:newZodeInfos) {
if (!allZnodes.contains(nowZNode)){
log.info("新增 SpiderEngine 節點{}", nowZNode);
}
}
}else if (newZodeInfos.size()<allZnodes.size()){
// 當機/下線
// 明確顯示哪個 SpiderEngine 節點當機/下線了
for (String initZNode : allZnodes) {
if (!newZodeInfos.contains(initZNode)) {
log.info("SpiderEngine 節點【{}】下線了!", initZNode);
// 如果有下線的處理,則處理(例如發郵件、簡訊等)
if (serverOfflineProcess!=null) {
serverOfflineProcess.process();
}
}
}
}else {
// SpiderEngine 叢集正常執行;
// 當機/下線了,當時馬上重啟了,總的爬蟲未發生變化
}
}
} catch (Exception e) {
e.printStackTrace();
}
allZnodes = newZodeInfos;
}
複製程式碼
所以需要單獨執行一個程式,例如:
public class TestCuratorManager {
public static void main(String[] args) {
CuratorManager curatorManager = new CuratorManager();
curatorManager.start();
}
}
複製程式碼
下圖反映了 ZooKeeper 如何監控 SpiderEngine 叢集。
總結
爬蟲框架 github 地址:github.com/fengzhizi71…
本文介紹瞭如何使用 ZooKeeper 來監控爬蟲的叢集。未來,NetDiscovery 還會增加更為通用的功能。
Java與Android技術棧:每週更新推送原創技術文章,歡迎掃描下方的公眾號二維碼並關注,期待與您的共同成長和進步。