`
pocketduck
  • 浏览: 19690 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jspider的一个bug

阅读更多

  最近解决一个蜘蛛爬虫的问题,需求是这样的,每发布一个网站,就要用爬虫去爬该网站所有的链接,爬虫找的是jspider,碰到的第一个问题是,如果同时发布很多网站,每个发布动作都会起一个爬虫实例

     

SpiderContext context = SpiderContextFactory.createContext(url);
SpiderNest nest = new SpiderNest();
Spider spider = nest.breedSpider(context);
spider.crawl(context);

   这样导致爬虫服务器直接被压死,虽然加了负载均衡,用4台jboss分担压力,到发布高峰的时候,仍然会被压死

后来采取的方案是加入一个线程池,把爬取网站任务放到队列里,第一次用的线程池是ScheduledThreadPool

this.ES = Executors.newScheduledThreadPool(this.threadPoolCount);
    for (int i = 0; i < this.threadCount; ++i)
      this.ES.scheduleWithFixedDelay(new TrackThread(this), 200L, 5L, TimeUnit.MILLISECONDS);
  }

 

  升级后经过一段时间后来发现,工作的5个线程都不在继续工作,但是一直处于等待状态,经过简单分析,认为可能爬虫任务进入假死状态,始终不完成任务,导致我的工作线程一直在等待,变成只能接受任务,没人干活的状态了,由于时间紧迫,采取了一个临时解决方案,即等待队列数目大于100时,就认为线程池里线程已经废掉了,就重新创造一个线程池从队列中取任务

  虽说能解决一定问题,但是在极端情况下,后来的线程池即使创建了,但是没完成队列里的任务后,又都over了,又要等待到达一定数目重新创建,这样会导致创造的线程永远干不完活,并且“僵尸线程”到达一定数量后,jvm最终也会挂掉

  后来经过一个周末,决定换一个线程池,这次换成了ThreadPoolExecutor

  

//创建
queue = new   ArrayBlockingQueue<Runnable>(queueSize);
threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, consuskSleepTime,TimeUnit.SECONDS, queue,
new ThreadPoolExecutor.DiscardOldestPolicy());

//将任务加入队列中
	public void addUrlToArray(URL url) {
		threadPool.execute(new TrackThread(url));
	}

 

悲剧再一次重演,过了一段时间后,线程又不干活了,又都处于等待状态,这次要动真格的了,打开jconsole,观察线程运行情况

 

发现叫Spider的线程全部处于wait状态,终于确认,这不是我线程池的问题,其实是jspider自己的问题,漫长痛苦的读源码阶段开始了,经过观察,发现每次Spider出问题之前都会报错

14:07:59,897 ERROR [STDERR] Exception in thread "Spider 3" 
14:07:59,897 ERROR [STDERR] java.lang.NullPointerException
14:07:59,897 ERROR [STDERR] 	at net.javacoding.jspider.core.impl.SpiderContextImpl.throttle(SpiderContextImpl.java:154)
14:07:59,897 ERROR [STDERR] 	at net.javacoding.jspider.core.task.work.SpiderHttpURLTask.prepare(SpiderHttpURLTask.java:38)
14:07:59,898 ERROR [STDERR] 	at net.javacoding.jspider.core.threading.WorkerThread.run(WorkerThread.java:130)

 

打开这里的源码,发现是这样的

/**
     * Thread's overridden run method.
     */
    public synchronized void run() {
        running = true;

        Log log = LogFactory.getLog(WorkerThread.class);
        log.debug("Worker thread (" + this.getName() + ") born");

        synchronized (stp) {
            stp.notify();
        }

        while (running) {
            if (assigned) {
                state = WORKERTHREAD_BLOCKED;
                task.prepare();
                state = WORKERTHREAD_BUSY;
                try {
                    task.execute();
                    task.tearDown();
                } catch (Exception e) {
                    log.fatal("PANIC! Task " + task + " threw an excpetion!", e);
                    System.exit(1);
                }

                synchronized (stp) {
                    assigned = false;
                    task = null;
                    state = WORKERTHREAD_IDLE;
                    stp.notify();
                    this.notify(); // if some thread is blocked in stopRunning();
                }

            }
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        /* notify the thread pool that we died. */
        log.debug("Worker thread (" + this.getName() + ") dying");
    }

 

  问题就在这里,由于这里代码带过于局限片面,无从下手,开始找jspider的资料,经过一段时间分析,jspider的工作原理大致是这样的,有两个线程池,里面有两种线程,大概做两种事,第一个线程名字都叫Thinker xx,(见上面的图片),他负责发现某网站的url,完后第二个线程池里面的程序负责分析这些url,第二个线程就是报错的叫Spider xx的,当某个Spider线程完成一个任务后,就会通知“我干完活了”,并且当前线程进入wait()状态,等待下一个任务,当Thinker发现的所有url都爬取完后,当前任务就完成

  而问题在于,如果某个蜘蛛线程抛出异常,就导致该线程不会执行后面的操作27~32.而直接进入wait状态,最终导致该thinker下的所有Spider都进入wait状态,进入“假死”。经过看更多源码,发现当时作者的意思是如果spider异常,直接系统退出,而这显然不符合我的需求,我需要某Spider抛出异常,不影响整个进程,大不了这个url不爬就是了。进一步分析,每个Spider完成任务后,要做两件事,一个是将当前线程置为可用状态,并且通知整个context该任务完成。

   由于jspider太过于复杂,中间省去1w字,最终方案是,只要该任务异常,也要强行通知“我干完活了”就ok了,修改过的代码如下 (12~25行部分)

public synchronized void run() {
        running = true;
        Log log = LogFactory.getLog(WorkerThread.class);
        log.debug("Worker thread (" + this.getName() + ") born");
        synchronized (stp) {
            stp.notify();
        }
        while (running) {
       
            if (assigned) {
                state = WORKERTHREAD_BLOCKED;    
                try {
                          if(null!=task){
                	 task.prepare();
                                state = WORKERTHREAD_BUSY;
                                task.execute();
         	          }
                } catch (Exception e) {
                    log.fatal("PANIC! Task " + task + " threw an excpetion!", e);
                    System.out.println("WorkerThread error================"+e);

                   // System.exit(0);
                }finally{//解决某个spider线程异常后,导致整个Thinker线程无法结束
                	 task.tearDown();
                }

                synchronized (stp) {
                    assigned = false;
                    task = null;
                    state = WORKERTHREAD_IDLE;
                    stp.notify();
                    this.notify(); // if some thread is blocked in stopRunning();
                }

            }
            try {
                wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        /* notify the thread pool that we died. */
        log.debug("Worker thread (" + this.getName() + ") dying");
    }

 

经过模拟抛出异常测试,终于该问题解决,结论发现,老外也有范低级错误的时候~其实修改代码不过10行,但是前前后后折腾了一个星期,把线程知识又重新温习了一遍,并且把jmeter和jconsole以及jboss的jvm监控配置学习了一下,收获还是不小的

分享到:
评论

相关推荐

    Java网页爬虫 JSpider

    5. **多线程支持**:为了提高爬取效率,JSpider通常会利用多线程技术并发处理多个URL,每个线程负责一个或多个任务。 6. **结果存储**:抓取到的数据会被存储到数据库、文件系统或其他持久化介质中,便于后续分析和...

    Jspider 一款开源网络爬虫,功能强大可自己定制来满足自身的需求

    总之,Jspider 是一个功能强大的 Java 网络爬虫,其开源特性使得开发者可以根据具体需求进行定制,结合详细的英文文档,用户可以轻松上手并构建出高效、灵活的爬虫项目。无论你是初学者还是经验丰富的开发者,...

    Jspider纯Java爬虫

    总结来说,Jspider是一个功能强大的Java爬虫框架,具备灵活的调度、解析和数据处理能力。通过学习和实践,你可以利用它来高效地获取网络上的信息,为数据分析或业务应用提供数据支持。同时,理解Jspider的内部机制也...

    jspider.rar

    JSpider就是这样一个工具,它能够按照用户设定的规则自动访问网页,并将所需数据存储下来。 2. **URL管理器(URL Manager)**: JSpider中的URL管理器负责跟踪已访问、待访问和重复访问的URL,确保爬虫按照预定策略...

    jspider 框架资料

    jspider各个结构的详细描述,对使用jspider有较好的帮助,加深对jspider的理解,帮助你扩展自己的jspider

    JSpider Web Spider引擎

    你可以利用它来检查网站的错误(内在的服务器错误等),网站内外部链接检查,分析网站的结构(可创建一个网站地图),下载整个Web站点,你还可以写一个JSpider插件来扩展你所需要的功能。 压缩包包含文件: jspider-0-5-0-...

    java源码:Java网页爬虫 JSpider.zip

    这个压缩包包含了一个名为JSpider的Java爬虫项目,该项目用于自动化抓取和处理互联网上的网页数据。 【描述】描述中的"java源码"表明我们将会探讨的是实际的Java代码,而"Java网页爬虫"则意味着我们将关注如何使用...

    jspider网络蜘蛛工具

    **jspider网络蜘蛛工具**是一种用于...总的来说,jspider作为一个强大的网络爬虫工具,提供了一套便捷的命令行接口和丰富的功能,帮助开发者高效地抓取和处理网络数据。通过合理配置和使用,可以满足各种数据采集需求。

    JSpider.ppt

    总的来说,JSpider是一个功能丰富的Java网络爬虫,它的设计目标是提供一个可定制的平台,用于各种Web数据的抓取和分析任务。通过其规则、插件和事件过滤器机制,用户可以根据自己的需求构建和扩展爬虫功能,从而实现...

    网络爬虫Jspider

    使用javaswing开发,可直接...具体功能是输入一个起始URL,输一个或多个关键词,输入爬虫层次,url限定量,然后会显示treemap,显示所有访问过的URL,匹配上关键词的URL则会显示蓝色,访问出错信息会显示在Message面板

    基于Java的网页爬虫 JSpider.zip

    JSpider就是这样一个用Java实现的网页爬虫框架,它为开发者提供了构建定制化爬虫的便利。 **JSpider框架结构** 1. **build.report**:这个目录通常包含项目构建过程中的报告,如编译错误和测试结果。这对于开发和...

    Jspider2.0

    Jspider 2.0 是一个基于 Java 开发的高级网络爬虫框架,它专为爬取互联网上的各种信息而设计,如产品图片、详细参数、分类以及品牌等数据。作为一个强大的数据抓取工具,Jspider 提供了丰富的功能,便于开发者高效地...

    基于Java的实例源码-网页爬虫 JSpider.zip

    Java网页爬虫JSpider是一个使用Java语言开发的网络爬虫框架,它允许开发者高效地抓取并处理互联网上的数据。这个实例源码提供了全面的功能,包括URL管理、HTML解析、数据提取以及结果存储等。通过对JSpider的深入...

    jspider 说明文档

    JSpider 是一个灵活且强大的网页爬虫工具,适用于多种网络数据采集任务。它支持自定义规则,可以轻松适应各种不同的网站结构,并能够高效地处理大量数据。 ##### B. 术语定义 - **爬虫**:自动抓取网页信息的程序...

    JAVA源码Java网页爬虫JSpider

    JAVA源码Java网页爬虫JSpider

    java资源Java网页爬虫JSpider

    java资源Java网页爬虫 JSpider提取方式是百度网盘分享地址

    Java网页爬虫 JSpider源码

    JSpider的设计理念是提供一个灵活、可扩展的平台,让开发者能够方便地定制自己的爬虫任务。在这个源码包中,我们可以看到以下几个关键部分: 1. **build.report**:这个目录通常包含了构建报告,例如Maven或Gradle...

    基于java的网页爬虫 JSpider.zip

    基于java的网页爬虫 JSpider.zip

    JSpider:JSpider会每周更新至少一个网站的JS解密方式,欢迎Star,交流微信:13298307816

    JSpider的含义是JavaScript与Spider,JSpider每周会更新至少一个网站的JS解密方式。并且把解密的JS文件分享出来,该JS文件可以直接使用Python模块pyexecjs调用执行。共享的文件包括JS源码和Python程序。 欢迎星 解密...

    jspider-src-0.5.0-dev.zip_doc_pdf 爬虫_网络爬虫_网络爬虫 Java

    标题中的"jspider-src-0.5.0-dev.zip"是一个Java网络爬虫项目的源代码压缩包,版本为0.5.0开发版。这个项目主要用于抓取互联网上的各种类型的数据,包括PDF和DOC文档以及HTML网页。"爬虫_网络爬虫_网络爬虫 Java...

Global site tag (gtag.js) - Google Analytics