爬虫(三)-NIO下载服务NIOFetcher的设计

来源:互联网 发布:探险电影知乎 编辑:程序博客网 时间:2024/06/07 04:48

       之前写的一个爬虫下载部分使用HttpClient,效率相当不敢恭维,最近打算使用NIO实现该部分,记录一下设计及实现过程中遇到的的问题。

一.基本思路

        以下为可能会犯的错误

         1.连接到同一服务器的SocketChannel数量过多

            看到很多人用SocketChannel模拟Http请求实现方式几乎都是为每一个域名下的URL注册一个SocketChannel,这种方式有一个弊端,不论从横向(多域名)还是纵向(同一域名),SocketChannel的总量不好控制,这就使我们很难在带宽与下载效率上进行权衡。

          2.频繁的打开、关闭SocketChannel

             我们很期望从本机到远程服务器能够建立几个相对稳定的长连接,以减少频繁打开、关闭Socket连接的资源消耗,但是还有另外一个问题:我们需要礼貌爬取,即使你选择"疯狂"爬取,也要考虑远程服务器强制关闭Socket连接的问题。权衡一下,我更倾向于使Socket可以保持一定时间的连接(可以叫做半长连接),打开一定时间的Socket连接在完成最后一次下载任务后就要关闭。

    

综合以上两个因素,我选择为同一域名的URL注册一定数量的(可配置,以适应下载服务器的性能与带宽的变动)Socket连接,以该值为阀值并辅以Socket连接服务时长对Socket连接数量进行横向与纵向的控制。


二.设计

   主要结构如下图所示:

                                         

其中IURLRegister与IFetchedURLHandler分别供爬行模块与文件持久化模块调用。


三.主要接口及实现类代码

         

package com.crazygeeker.search.fetcher;/** * A service designed for download remote file. * <p>Provide basic operations such as <tt>start</tt><tt>shutdown</tt></p>  * @author JohnWang * @version 1.0 * @date 2013-11-11 */public interface IFetcher {void start();void shutdown();}


package com.crazygeeker.search.fetcher;import com.crazygeeker.search.common.model.URLBean;/** * register the url waiting for downloading *  * @author JohnWang * @version 1.0 * @date 2013-11-11 */public interface IURLBeanRegister {/** *  * @param urlBean * @return */boolean register(URLBean urlBean);}


package com.crazygeeker.search.fetcher;import com.crazygeeker.search.common.model.URLBean;/** * An interface for data upload * @author JohnWang * @version 1.0 * @date 2013-11-11 */public interface IFetchedURLHandler {URLBean getURLBean();}

          


package com.crazygeeker.search.fetcher;import java.io.IOException;import java.nio.channels.Selector;import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;import java.util.concurrent.PriorityBlockingQueue;import java.util.concurrent.atomic.AtomicBoolean;import com.crazygeeker.search.common.model.URLBean;import com.crazygeeker.search.common.utils.LoggerUtils;/** *  * @author JohnWang * @version 1.0 * @date 2013-11-11 */public abstract class AbstractNIOFetcher implements IFetcher {protected Selector selector;/** *  */protected AtomicBoolean isRunning = new AtomicBoolean(false);/** *  */protected BlockingQueue<URLBean> pendingQueue = new PriorityBlockingQueue<URLBean>(); /** *  */protected BlockingQueue<URLBean> fetchedQueue = new LinkedBlockingQueue<URLBean>();/** *  */public void start() {try {selector = Selector.open();} catch (IOException e) {e.printStackTrace();LoggerUtils.fetcherLogger.error("error.cannot_open_selector",e);}isRunning.set(true);LoggerUtils.fetcherLogger.info("selector has bean opened");service();}protected abstract void service();/** *  */public void shutdown(){try {selector.close();} catch (IOException e) {e.printStackTrace();LoggerUtils.fetcherLogger.error("error.cannot_close_selector",e);}LoggerUtils.fetcherLogger.info("selector has bean closed");}}


原创粉丝点击