Java 動手寫爬蟲: 4、日誌埋點輸出 & 動態配置支持

第四篇, 日誌埋點輸出 & 動態配置支持

前面基本上實現了一個很是簡陋的爬蟲框架模型,不少關鍵鏈路都沒有日誌,在分析問題時,就比較麻煩了,所以就有了這一篇博文html

其次就是解決前幾篇遺留的容易解決的問題java

實際上,日誌的輸出應該貫穿在實際的開發過程當中的,因爲以前寫得比較隨意,直接System.out了, 因此如今就來填坑了git

1.日誌埋點設計

採用 logback 左右日誌輸出, 這裏有一篇博文可供參考 《Logback 簡明使用手冊github

埋點的關鍵鏈路編程

  1. 當前爬取的任務信息
  2. 爬取任務的耗時
  3. 應用的狀態(如爬取了多少個,還剩下多少個待爬取等)
  4. 爬取結果輸出
  5. 其餘一些信息

實現比較簡單,在pom中添加依賴api

<!--日誌-->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.7</version>
</dependency>

添加配置文件微信

logback-test.xmlapp

<?xml version="1.0" encoding="UTF-8"?>

<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
        </encoder>
    </appender>

    <logger name="com.quick.hui.crawler" level="DEBUG"/>


    <root level="INFO">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>

代碼中埋點框架

.... (直接參考源碼便可)工具

2. 爬取頻率控制

不少網站會對訪問的頻率進行限制,這是一個最基礎的防爬手段了,因此咱們的爬取須要一個能夠設置爬取任務的頻率控制

1. 設計

目的

  • 採用一個比較簡單的方案,每次從隊列中獲取爬取任務時,sleep指定的時間,來實現爬取頻率的限制
  • 對此咱們設計得稍微高級一點,這個sleep時間,咱們但願是能夠動態配置的

方案

採用配置項來解決這個,(爲了後續的拓展,讀取配置搞成面向接口的編程方式),咱們先提供一個基礎的,根據本地配置文件來讀取頻率控制參數

實現

由於採用配置文件的方式,因此一個用於讀取配置文件的輔助工具類是必須的

1. 配置文件讀取 FileConfRead

@Slf4j
public class FileConfRead implements IConfRead {


    public Config initConf(String path) {
        try {
            Properties properties = read(path);

            Config config = new Config();
            config.setSleep(properties.getProperty("sleep"), 0);
            config.setEmptyQueueWaitTime(properties.getProperty("emptyQueueWaitTime"), 200);

            return config;
        } catch (Exception e) {
            log.error("init config from file: {} error! e: {}", path, e);
            return new Config();
        }
    }


    private Properties read(String fileName) throws IOException {
        try (InputStream inputStream = FileReadUtil.getStreamByFileName(fileName)) {
            Properties pro = new Properties();
            pro.load(inputStream);
            return pro;
        }
    }


    private File file;
    private long lastTime;

    public void registerCheckTask(final String path) {
        try {
            file = FileReadUtil.getFile(path);
            lastTime = file.lastModified();


            ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
            scheduledExecutorService.scheduleAtFixedRate(() -> {
                        if (file.lastModified() > lastTime) {
                            lastTime = file.lastModified();
                            ConfigWrapper.getInstance().post(new ConfigWrapper.UpdateConfEvent());
                        }
                    },
                    1,
                    1,
                    TimeUnit.MINUTES);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

實現類主要繼承接口 IConfRead, 接口中定義了兩個方法,一個用於獲取配置信息,一個用於註冊配置信息的變更監聽事件

public interface IConfRead {

    /**
     * 初始化配置信息
     *
     * @param var
     * @return
     */
    Config initConf(String var);


    /**
     * 註冊配置信息更新檢測任務
     *
     * @param path
     */
    void registerCheckTask(final String path);
}

回到具體的實現,讀取配置文件信息比較簡單,直接使用jdk的Properties文件的讀寫方式,接下來則是註冊監聽事件的實現上,咱們的設計思路以下:

  • 獲取配置文件的更新時間
  • 每隔一段時間主動去查看下配置文件的更新時間,判斷是否更新過
    • 若更新,則從新加載配置文件,覆蓋以前的
    • 若無更新,直接放過

2. 配置類 Config

這裏定義全部的配置信息,方便後續的維護和查閱

@Getter
@Setter
@ToString
public class Config {

    /**
     * 爬取任務的間隔時間
     */
    private long sleep;


    /**
     * 從隊列中獲取任務,返回空時,等待時間以後再進行重試
     */
    private long emptyQueueWaitTime;


    public void setSleep(String str, long sleep) {
        this.sleep = NumUtils.str2long(str, sleep);
    }

    public void setEmptyQueueWaitTime(String str, long emptyQueueWaitTime) {
        this.emptyQueueWaitTime = NumUtils.str2long(str, emptyQueueWaitTime);
    }
}

3. 配置信息獲取封裝類 ConfigWrapper

這裏封裝了獲取配置信息的接口,內部維護配置信息的變動事件,咱們採用EventBus來實現事件的監聽

@Slf4j
public class ConfigWrapper {
    private static final String CONFIG_PATH = "conf/crawler.properties";

    private EventBus eventBus;


    private IConfRead confRead;

    private Config config;

    private static volatile ConfigWrapper instance;

    private ConfigWrapper() {
        confRead = new FileConfRead();
        confRead.registerCheckTask(CONFIG_PATH);
        config = confRead.initConf(CONFIG_PATH);


        // 註冊監聽器
        eventBus = new EventBus();
        eventBus.register(this);
    }


    public static ConfigWrapper getInstance() {
        if (instance == null) {
            synchronized (ConfigWrapper.class) {
                if (instance == null) {
                    instance = new ConfigWrapper();
                }
            }
        }

        return instance;
    }


    @Subscribe
    public void init(UpdateConfEvent event) {
        config = confRead.initConf(event.conf);

        if (log.isDebugEnabled()) {
            log.debug("time:{} processor:{} update config! new config is: {}",
                    event.now, event.operator, config);
        }
    }


    public Config getConfig() {
        return config;
    }


    public void post(Object event) {
        eventBus.post(event);
    }

    @Getter
    @Setter
    public static class UpdateConfEvent {
        private long now = System.currentTimeMillis();

        private String operator = "System";

        private String conf = CONFIG_PATH;
    }
}

3. 源碼地址

項目地址: https://github.com/liuyueyi/quick-crawler

日誌埋點對應的tag: v0.006

動態配置對應的tag: v0.007

相關連接

歡迎關注個人微信公衆號

小灰灰的公衆號

相關文章
相關標籤/搜索