最近解决一个蜘蛛爬虫的问题,需求是这样的,每发布一个网站,就要用爬虫去爬该网站所有的链接,爬虫找的是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监控配置学习了一下,收获还是不小的
发表评论
-
No application id has been found 解决办法
2012-10-30 11:28 902选择运行选项->plug-ins 点击右侧的 " ... -
eclipse wizard 占空间的解决办法
2011-11-28 15:37 1045wizardpage区域下部总是会有相当大的一块区域出于空白, ... -
java中的线程停止
2011-11-23 16:27 1264想象一下这个场景,用户刚刚点了连接数据库后,忽然想起这个数据库 ... -
修改jface wizard 按钮
2011-11-16 10:56 1259本向导结束后其实是开始另外一个流程,向导默认完成按钮叫fini ... -
InheritableThreadLocal的使用问题
2011-05-27 10:34 1201单例对象,在多线程环境中使用时,为了避免线程冲突,大家都知道要 ... -
我的缓存方案与jconsole测试流水账
2009-07-24 16:11 850最近项目有缓存方案,后来用jconsole进行内存使用测试,以 ... -
mysql分钟差比较
2009-07-24 16:08 1603update t_visitorinfo set versio ... -
第一次的webservice调用
2009-07-24 16:07 1725项目当中需要调用对方的发布的的一个webservice,需求比 ... -
离开页面或关闭窗口js
2009-07-24 16:07 1448VisitTrack是js对象名称 //给window添加事件 ... -
dwr调用javabean为参数的方法
2009-07-24 16:05 811调用javabean为参数的方法,与简单参数不同的地方1.ja ... -
dwr 联动下拉框
2009-07-24 16:05 1030今天在公司无聊,学学dwr 下拉框联动,看了几个帖子,自己随手 ... -
dwr笔记
2009-07-24 16:03 1080在Input中输入一个值,当该input丢失焦点后,把该值作为 ... -
一个关于form自动提交的白痴问题
2009-07-24 16:03 1220碰到一个需要form自动提交的地方,设置后bo ... -
org.apache.commons.beanutils.BeanUtils用法
2009-07-24 16:01 1452BeanUtils提供对 Java反射和自省API的包装。其主 ...
相关推荐
5. **多线程支持**:为了提高爬取效率,JSpider通常会利用多线程技术并发处理多个URL,每个线程负责一个或多个任务。 6. **结果存储**:抓取到的数据会被存储到数据库、文件系统或其他持久化介质中,便于后续分析和...
总之,Jspider 是一个功能强大的 Java 网络爬虫,其开源特性使得开发者可以根据具体需求进行定制,结合详细的英文文档,用户可以轻松上手并构建出高效、灵活的爬虫项目。无论你是初学者还是经验丰富的开发者,...
总结来说,Jspider是一个功能强大的Java爬虫框架,具备灵活的调度、解析和数据处理能力。通过学习和实践,你可以利用它来高效地获取网络上的信息,为数据分析或业务应用提供数据支持。同时,理解Jspider的内部机制也...
JSpider就是这样一个工具,它能够按照用户设定的规则自动访问网页,并将所需数据存储下来。 2. **URL管理器(URL Manager)**: JSpider中的URL管理器负责跟踪已访问、待访问和重复访问的URL,确保爬虫按照预定策略...
jspider各个结构的详细描述,对使用jspider有较好的帮助,加深对jspider的理解,帮助你扩展自己的jspider
你可以利用它来检查网站的错误(内在的服务器错误等),网站内外部链接检查,分析网站的结构(可创建一个网站地图),下载整个Web站点,你还可以写一个JSpider插件来扩展你所需要的功能。 压缩包包含文件: jspider-0-5-0-...
这个压缩包包含了一个名为JSpider的Java爬虫项目,该项目用于自动化抓取和处理互联网上的网页数据。 【描述】描述中的"java源码"表明我们将会探讨的是实际的Java代码,而"Java网页爬虫"则意味着我们将关注如何使用...
**jspider网络蜘蛛工具**是一种用于...总的来说,jspider作为一个强大的网络爬虫工具,提供了一套便捷的命令行接口和丰富的功能,帮助开发者高效地抓取和处理网络数据。通过合理配置和使用,可以满足各种数据采集需求。
总的来说,JSpider是一个功能丰富的Java网络爬虫,它的设计目标是提供一个可定制的平台,用于各种Web数据的抓取和分析任务。通过其规则、插件和事件过滤器机制,用户可以根据自己的需求构建和扩展爬虫功能,从而实现...
使用javaswing开发,可直接...具体功能是输入一个起始URL,输一个或多个关键词,输入爬虫层次,url限定量,然后会显示treemap,显示所有访问过的URL,匹配上关键词的URL则会显示蓝色,访问出错信息会显示在Message面板
JSpider就是这样一个用Java实现的网页爬虫框架,它为开发者提供了构建定制化爬虫的便利。 **JSpider框架结构** 1. **build.report**:这个目录通常包含项目构建过程中的报告,如编译错误和测试结果。这对于开发和...
Jspider 2.0 是一个基于 Java 开发的高级网络爬虫框架,它专为爬取互联网上的各种信息而设计,如产品图片、详细参数、分类以及品牌等数据。作为一个强大的数据抓取工具,Jspider 提供了丰富的功能,便于开发者高效地...
Java网页爬虫JSpider是一个使用Java语言开发的网络爬虫框架,它允许开发者高效地抓取并处理互联网上的数据。这个实例源码提供了全面的功能,包括URL管理、HTML解析、数据提取以及结果存储等。通过对JSpider的深入...
JSpider 是一个灵活且强大的网页爬虫工具,适用于多种网络数据采集任务。它支持自定义规则,可以轻松适应各种不同的网站结构,并能够高效地处理大量数据。 ##### B. 术语定义 - **爬虫**:自动抓取网页信息的程序...
JAVA源码Java网页爬虫JSpider
java资源Java网页爬虫 JSpider提取方式是百度网盘分享地址
JSpider的设计理念是提供一个灵活、可扩展的平台,让开发者能够方便地定制自己的爬虫任务。在这个源码包中,我们可以看到以下几个关键部分: 1. **build.report**:这个目录通常包含了构建报告,例如Maven或Gradle...
基于java的网页爬虫 JSpider.zip
JSpider的含义是JavaScript与Spider,JSpider每周会更新至少一个网站的JS解密方式。并且把解密的JS文件分享出来,该JS文件可以直接使用Python模块pyexecjs调用执行。共享的文件包括JS源码和Python程序。 欢迎星 解密...
标题中的"jspider-src-0.5.0-dev.zip"是一个Java网络爬虫项目的源代码压缩包,版本为0.5.0开发版。这个项目主要用于抓取互联网上的各种类型的数据,包括PDF和DOC文档以及HTML网页。"爬虫_网络爬虫_网络爬虫 Java...