分布式爬虫(四)| 青训营笔记

143 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第四天

监控报警系统

7.png


监控报警系统的加入主要是为了让使用者可以主动发现节点宕机,而不是被动地发现,因为实际中爬虫程序可能是持续不断运行的,并且我们会在多个节点上部署我们的爬虫程序,因此很有必要对节点进行监控,并且在节点出现问题时可以及时发现并修正,需要注意的是,监控报警系统是一个独立的进程,需要单独启动。\

基本原理

首先需要先在ZooKeeper中创建一个/ispider节点:\

[zk: localhost:2181(CONNECTED) 1] create /ispider ispider
Created /ispider


监控报警系统的开发主要依赖于ZooKeeper实现,监控程序对ZooKeeper下面的这个节点目录进行监听:\

[zk: localhost:2181(CONNECTED) 0] ls /ispider
[]


爬虫程序启动时会在该节点目录下注册一个临时节点目录:\

[zk: localhost:2181(CONNECTED) 0] ls /ispider
[192.168.43.166]


当节点出现宕机时,该临时节点目录就会被ZooKeeper删除。\

[zk: localhost:2181(CONNECTED) 0] ls /ispider
[]


同时因为我们监听了节点目录/ispider,所以当ZooKeeper删除其下的节点目录时(或增加一个节点目录),ZooKeeper会给我们的监控程序发送通知,即我们的监控程序会得到回调,这样便可以在回调程序中执行报警的系统动作,从而完成监控报警的功能。\

ZooKeeper Java API使用说明

可以使用ZooKeeper原生的Java API,我在另外写的一个RPC框架(底层基于Netty实现远程通信)中就是使用原生的API,不过显然代码会复杂很多,并且本身需要对ZooKeeper有更多的学习和了解,这样用起来才会容易一些。

所以为了降低开发的难度,这里使用第三方封装的API,即curator,来进行ZooKeeper客户端程序的开发。\

爬虫系统ZooKeeper注册

在启动爬虫系统时,我们的程序都会启动一个ZooKeeper客户端来向ZooKeeper来注册自身的节点信息,主要是IP地址,并在/ispider节点目录以创建一个以该爬虫程序所在的节点IP地址命名的节点,如/ispider/192.168.43.116,实现的代码如下:\

/**
* 注册ZooKeeper
*/
private void registerZK() {
String zkStr = "uplooking01:2181,uplooking02:2181,uplooking03:2181";
int baseSleepTimeMs = 1000;
int maxRetries = 3;
RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
CuratorFramework curator = CuratorFrameworkFactory.newClient(zkStr, retryPolicy);
curator.start();
String ip = null;
try {
    // 向ZooKeeper的具体目录注册写节点创建节点
    ip = InetAddress.getLocalHost().getHostAddress();
    curator.create().withMode(CreateMode.EPHEMERAL).forPath("/ispider/" + ip, ip.getBytes());
} catch (UnknownHostException e) {
    e.printStackTrace();
} catch (Exception e) {
    e.printStackTrace();
}
} 


应该注意到的是,我们创建的节点为临时节点,要想实现监控报警功能,必须要为临时节点。\

监控程序

首先需要先监听ZooKeeper中的一个节点目录,在我们的系统中,设计是监听/ispider这个节点目录:\

public SpiderMonitorTask() {
String zkStr = "uplooking01:2181,uplooking02:2181,uplooking03:2181";
int baseSleepTimeMs = 1000;
int maxRetries = 3;
RetryPolicy retryPolicy = new ExponentialBackoffRetry(baseSleepTimeMs, maxRetries);
curator = CuratorFrameworkFactory.newClient(zkStr, retryPolicy);
curator.start();
try {
    previousNodes = curator.getChildren().usingWatcher(this).forPath("/ispider");
} catch (Exception e) {
    e.printStackTrace();
}
} 


在上面注册了ZooKeeper中的watcher,也就是接收通知的回调程序,在该程序中,执行我们报警的逻辑:\

/**
* 这个方法,当监控的ZooKeeper对应的目录一旦有变动,就会被调用
* 得到当前最新的节点状态,将最新的节点状态和初始或者上一次的节点状态作比较,那我们就知道了是由谁引起的节点变化
* @param event
*/
@Override
public void process(WatchedEvent event) {
try {
    List<String> currentNodes = curator.getChildren().usingWatcher(this).forPath("/ispider");
    //            HashSet<String> previousNodesSet = new HashSet<>(previousNodes);
    if(currentNodes.size() > previousNodes.size()) { // 最新的节点服务,超过之前的节点服务个数,有新的节点增加进来
        for(String node : currentNodes) {
            if(!previousNodes.contains(node)) {
                // 当前节点就是新增节点
                logger.info("----有新的爬虫节点{}新增进来", node);
            }
        }
    } else if(currentNodes.size() < previousNodes.size()) {  // 有节点挂了    发送告警邮件或者短信
        for(String node : previousNodes) {
            if(!currentNodes.contains(node)) {
                // 当前节点挂掉了 得需要发邮件
                logger.info("----有爬虫节点{}挂掉了", node);
                MailUtil.sendMail("有爬虫节点挂掉了,请人工查看爬虫节点的情况,节点信息为:", node);
            }
        }
    } // 挂掉和新增的数目一模一样,上面是不包括这种情况的,有兴趣的朋友可以直接实现包括这种特殊情况的监控
    previousNodes = currentNodes;   // 更新上一次的节点列表,成为最新的节点列表
} catch (Exception e) {
    e.printStackTrace();
}
// 在原生的API需要再做一次监控,因为每一次监控只会生效一次,所以当上面发现变化后,需要再监听一次,这样下一次才能监听到
// 但是在使用curator的API时则不需要这样做


当然,判断节点是否挂掉,上面的逻辑还是存在一定的问题的,按照上面的逻辑,假如某一时刻新增节点和删除节点事件同时发生,那么其就不能判断出来,所以如果需要更精准的话,可以将上面的程序代码修改一下。\

邮件发送模块

使用模板代码就可以了,不过需要注意的是,在使用时,发件人的信息请使用自己的邮箱。

下面是爬虫节点挂掉时接收到的邮件:\

8.png


实际上,如果购买了短信服务,那么通过短信API也可以向我们的手机发送短信。