• 网络爬虫:采用“负载均衡”策略来优化网络爬虫


    前言:

      这里说的负载均衡并非大家熟悉的网络中的负载均衡。

      只是这里我使用了和负载均衡同样的一种思维来优化程序罢了,其实就是压力分摊。


    问题描述:

      对于上一篇《分离生产者和消费者来优化爬虫程序》博客中遗留的问题:线程阻塞。

      当我们的程序运行到一定时间之后,会出现线程池中的500条线程不够用的情况,进而程序长期处于等待的状态。


    压力测试实验:

      本实验基于之前的爬虫程序,线程池中的线程最大为320条。下面是对在主线程中以不同时间间隔执行程序的测试结果:

    sleep 300ms



    sleep 500ms



    sleep 1000ms



    内存使用状态图:



    代码优化逻辑:

    1.python代码优化

    import sys
    reload(sys)
    sys.setdefaultencoding('utf8')
    from time import clock, sleep
    import threading
    
    from list_web_parser import ListWebParser
    import get_html_response as geth
    
    def visited_html(html):
        myp = ListWebParser()
        get_html = geth.get_html_response(html)
        myp.feed(get_html)
        link_list = myp.getLinkList()
        myp.close()
    
        for item in link_list:
            if item[0] and item[1]:
                print item[0], '$#$', item[1]
    
        global thread_done_flag
        thread_done_flag = True
    
    def count_down():
        start = clock()
        while True:
            sleep(1)
            end = clock()
            if int(end - start) >= 2:
                print 'TIME OUT'
                global thread_done_flag
                thread_done_flag = True
                break
    
    thread_done_flag = False
    
    def start_work(url):
        thread1 = threading.Thread(target=visited_html, args=(url,))
        thread2 = threading.Thread(target=count_down)
    
        thread1.setDaemon(True)
        thread2.setDaemon(True)
    
        thread1.start()
        thread2.start()
    
        while not thread_done_flag:
            ''
    
    if __name__ == "__main__":
        if not sys.argv or len(sys.argv) < 2:
            print 'You leak some arg.'
        start_work(sys.argv[1])
      这段代码做了一件事,主线程跟随第一个子线程结束而结束。

      目的是为了让程序在1秒钟之内结束运行,而超过1秒的html解析,我们将抛弃。我想这是合理的。因为我们不可能让Python一直占用我们的线程资源,这样很快线程就会出现阻塞。而且,随着我们解析HTML的线程数的增加。CPU的消耗也很快,这样我们的计算机就会出现卡顿的情况。


    2.Java代码优化

    public void visittingUrl(String startAddress) {
    	    // url 合法性判断
    	    if (startAddress == null) {
                return;
            }
    	    
    	    // 种子url 入库
    	    SpiderBLL.insertEntry2DB(startAddress);
    	    
    	    // 解析种子url
    	    PythonUtils.fillAddressQueueByPython(mUnVisitedQueue, startAddress, 0);
    	    
    		if (mUnVisitedQueue.isQueueEmpty()) {
                System.out.println("Your address cannot get more address.");
                return;
            }
    		
            boolean breakFlag = false;
            int index = 0;
            
            startThread();
            
    		while (!breakFlag) {
    		    
    			WebInfoModel model = mUnVisitedQueue.poll();
    			if (model == null) {
    			    System.out.println("------ 此URL为NULL ------");
                    continue;
                }
    			
    			// 判断此网站是否已经访问过
    			if (DBBLL.isWebInfoModelExist(model)) {
    			    // 如果已经被访问,进入下一次循环
    			    System.out.println("已存在此网站(" + model.getName() + ")");
    				continue;
    			}
    			
    			poolQueueFull(mThreadPool);
    			
    			System.out.println("LEVEL: [" + model.getLevel() + "] NAME: " + model.getName());
    			
    			mThreadPool.execute(new ParserRunner(mResultSet, model, index++, mResultMap));
    			
    			SystemBLL.cleanSystem(index);
    			
    			// 对已访问的address进行入库
    			DBBLL.insert(model);
    			
    			model = null;
    			
    			SystemBLL.sleep(300);
    		}
    		
    		mThreadPool.shutdown();
    	}
      Java代码的代码主要体现在,我们每次调用Python进行解析HTML时,都会sleep 300毫秒。这样我们CPU的压力就转移到时间上了。而这300毫秒其实对整体程序的影响不大,算是优点大于缺点吧。


    关于上一篇:

    1.覆盖equals时总要覆盖hashCode

      我们需要覆盖WebInfoModel的equals和hashCode方法,目的是我们把这个对象保存到HashSet中,需要保证它的唯一性。那么我们就必须自己来写一些唯一性的策略:重写equals方法。而重写equals时,必须要重写hashCode方法。关于这一点,大家可以参看笔者的另一篇博客《Effective Java:对于所有对象都通用的方法

    @Override
        public int hashCode() {
            return (name.hashCode() + address.hashCode() + level);
        }
        
        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof WebInfoModel)) {
                return false;
            }
            
            if (((WebInfoModel)obj).getName() == name && ((WebInfoModel)obj).getAddress() == address && ((WebInfoModel)obj).getLevel() == level) {
                return true;
            }
            
            return false;
        }

    遗留的问题:

    1.python无故停止运行



    下一步的目标:

    1.解决python程序停止运行的Bug

    2.分布式

    (终于,终于可以开始利用分布式来优化我的蜘蛛程序了。想想还有一点小激动呢 ^_^)

  • 相关阅读:
    catch tcl tk
    C语言的指针深入理解外加一精华帖
    Linux Shell编程4
    shell之测试语法
    linux 用户空间 和 内核空间 延时函数
    linux 用户空间 和 内核空间 延时函数
    C语言的指针深入理解外加一精华帖
    面向对象的编程技巧
    awk用法小结
    awk用法小结
  • 原文地址:https://www.cnblogs.com/fengju/p/6336048.html
Copyright © 2020-2023  润新知