欢迎加入Heritrix群(QQ):109148319,10447185 , Lucene/Solr群(QQ) :  118972724

本博客已迁移到本人独立博客: http://www.yun5u.com/

近段时间在搞定Lucene的一些问题,所以Heritrix源码分析暂时告一段落。今天下午在群里有同学提到了Heritrix异常终止的问题以及让Heritrix不停的抓取(就是抓完一遍后载入种子继续抓取,目前他是写个定时器,定时检查Heritrix是否停止,如果停止了则重新初始化Heritrix,让Heritrix重新抓取,但这个方法就不可避免的会导致抓取重复URl,除非从recover.gz导入)。我想这2个问题都可以用我下面这个方法,但对于Heritrix异常终止的问题这个方法也只是治标不治本,要改动的地方蛮多,涉及到Heritrix的设计以及结构,不是一时可以完成。下面就介绍原理以及方法:

1.原理:

      Heritrix的自然停止(就是非人为以及不可抗力因素,如Web UI界面出发停止命令)的依据是判断调度器里面是否还有URL可抓取,如果没有则当前线程退出,如果是单线程抓取的话,则也意味着Heritrix的结束。如果是多线程抓取的话,则每个线程在获取不到URL的时候都会置不在活动状态,当最后一个活动状态线程也获取不到URL的时候则Heritrix也会进入自然停止。所以我们可以在判断Heritrix是否还有URL可抓取的时候做一些处理,比较简单的方法就是重新载入种子以及唤醒所有抓取线程。但由于处在多线程抓取模式中,每个线程都会判断,如此会导致多次载入种子,造成不必要的浪费,所以这里也要做一些同步控制。不要导致过分载入种子,而是每抓完一次则重新载入种子。

2.方法:

    修改org.archive.crawler.frontier.WorkQueueFrontier中的public CrawlURI next()方法,这里也对该方法做一些介绍,具体请看源码注释,改成如下,红色部分为改动部分:

  1. /**
  2. * 从调度中心获取下一个要抓取的URL
  3. *
  4. */
  5. public CrawlURI next() throws InterruptedException, EndedException {
  6. while (true) {//一直不停的循环,直到遇到异常或终止
  7. <span style="color: #ff0000;">// 郭芸修改,用于当队列里没有可抓取的URL的时候去获取种子继续
  8. synchronized (this) {
  9. if (this.controller.getFrontier().isEmpty()) {  //如果没有可抓取的URL
  10. loadSeeds();    //重新载入种子
  11. this.controller.getToePool().notifyAll();   //唤醒所有抓取线程
  12. }
  13. }</span>
  14. long now = System.currentTimeMillis();//开始获取时间
  15. // 检查是否有暂停命令、结束命令以及宽带控制,这里会导致Heritrix结束
  16. preNext(now);
  17. /*
  18. * 允许最多一个线程去填充准备队列(readyClassQueues)
  19. */
  20. if (readyFiller.tryAcquire()) {// 表示没有线程去使用当前变量,当前类1次只允许1个线程同时使用
  21. try {
  22. // 空闲队列数=目标队列数-准备队列数
  23. int activationsNeeded = targetSizeForReadyQueues()
  24. - readyClassQueues.size();
  25. // 如果空闲队列数大于0,并且不在活动状态的队列数不是空的,则表示需要将不在活动状态的队列转移到准备队列
  26. while (activationsNeeded > 0 && !inactiveQueues.isEmpty()) {
  27. activateInactiveQueue();//将不在活动状态队列的URL转移一定数目到活动状态队列
  28. activationsNeeded--;
  29. }
  30. } finally {
  31. readyFiller.release();// 必须释放,这样下次才可以继续使用
  32. }
  33. }
  34. WorkQueue readyQ = null;//准备工作队列
  35. // 获取并移除此准备队列表示的队列的头部(即准备队列的第一个元素)如果该队列没有可用元素,则等待指定的时间,这里是1000毫秒也就是1秒
  36. Object key = readyClassQueues.poll(DEFAULT_WAIT,TimeUnit.MILLISECONDS);// 获得classKey,然后再通过classKey去获得队列
  37. if (key != null) {
  38. readyQ = (WorkQueue) this.allQueues.get(key);// 获得工作队列WorkQueue
  39. }
  40. if (readyQ != null) {
  41. while (true) { // 一直循环,直到抛出异常或终止
  42. CrawlURI curi = null;
  43. synchronized (readyQ) {//锁定准备队列,让其他线程无法获取,避免脏读
  44. curi = readyQ.peek(this); // 从数据库pendingUrls中获取CrawlURI
  45. if (curi != null) {
  46. // 检查该curi是否属于不同的队列
  47. String currentQueueKey = getClassKey(curi);
  48. if (currentQueueKey.equals(curi.getClassKey())) {
  49. //在正确的队列,排放它
  50. noteAboutToEmit(curi, readyQ);
  51. inProcessQueues.add(readyQ);// 加入已处理队列
  52. return curi;
  53. }
  54. curi.setClassKey(currentQueueKey);
  55. readyQ.dequeue(this);       //从调度器中删除刚获取到的URL
  56. decrementQueuedCount(1);    //计数
  57. curi.setHolderKey(null);
  58. } else {
  59. readyQ.clearHeld();
  60. break;
  61. }
  62. }
  63. if (curi != null) {
  64. sendToQueue(curi);  //将获取到的URL发送到它该属于的队列
  65. }
  66. }
  67. } else {
  68. if (key != null) {
  69. logger.severe("Key " + key
  70. + " in readyClassQueues but not allQueues");
  71. }
  72. }
  73. //如果该强烈退出,则抛异常结束循环
  74. if (shouldTerminate) {
  75. throw new EndedException("shouldTerminate is true");
  76. }
  77. //如果没有处理中的队列,则刷新该队列
  78. if (inProcessQueues.size() == 0) {
  79. this.alreadyIncluded.requestFlush();
  80. }
  81. }
  82. }

Heritrix源码分析(十四) 如何让Heritrix不间断的抓取(转)的更多相关文章

  1. Heritrix源码分析(十四)

    近段时间在搞定Lucene的一些问题,所以Heritrix源码分析暂时告一段落.今天下午在群里有同学提到了Heritrix异常终止的问题以及让Heritrix不停的抓取(就是抓完一遍后载入种子继续抓取 ...

  2. Heritrix源码分析(三) 修改配置文件order.xml加快你的抓取速度(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/629891       本博客已迁移到本人独立博客: http://www.yun5u ...

  3. Heritrix源码分析(十五) 各种问题总结(转)

    开博客以及建立Heritrix 群有一段时间了(这里谢谢大家的关注),这篇博客将整理这段时间所遇到的问题.同时由于自己从今年5月份开始就不怎么接触Heritrix,很多东西开始遗忘(不过里面思想没忘) ...

  4. Heritrix源码分析(十五)

    开博客以及建立Heritrix 群有一段时间了(这里谢谢大家的关注),这篇博客将整理这段时间所遇到的问题.同时由于自己从今年5月份开始就不怎么接触Heritrix,很多东西开始遗忘(不过里面思想没忘) ...

  5. ABP源码分析十四:Entity的设计

    IEntity<TPrimaryKey>: 封装了PrimaryKey:Id,这是一个泛型类型 IEntity: 封装了PrimaryKey:Id,这是一个int类型 Entity< ...

  6. jQuery 源码分析(十四) 数据操作模块 类样式操作 详解

    jQuery的属性操作模块总共有4个部分,本篇说一下第3个部分:类样式操作部分,用于修改DOM元素的class特性的,对于类样式操作来说,jQuery并没有定义静态方法,而只定义了实例方法,如下: a ...

  7. Vue.js 源码分析(十四) 基础篇 组件 自定义事件详解

    我们在开发组件时有时需要和父组件沟通,此时可以用自定义事件来实现 组件的事件分为自定义事件和原生事件,前者用于子组件给父组件发送消息的,后者用于在组件的根元素上直接监听一个原生事件,区别就是绑定原生事 ...

  8. Heritrix源码分析(十二) Heritrix的控制中心(大脑)CrawlController(一)(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/650694 本博客已迁移到本人独立博客: http://www.yun5u.com/ ...

  9. Heritrix源码分析(十) Heritrix中的Http Status Code(Http状态码)(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/649737       本博客已迁移到本人独立博客: http://www.yun5u ...

随机推荐

  1. WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据

    数据集(DataSet).数据表(DataTable).集合(Collection)概念是.NET FrameWork里提供数据类型,在应用程序编程过程中会经常使用其来作为数据的载体,属于ADO.NE ...

  2. js小技巧(二)

    //移动的图层,拖动 1.<span style='position:absolute;width:200;height:200;background:red' onmousedown=Mous ...

  3. 如何使用JMeter来实现更大批量的并发的解决方案(即如何设置controller和Agent)

    http://www.testwo.com/blog/6373 近期在用JMeter进行负载测试的 时候,发现使用单台机器模拟测试超过比如500个进程的并发就有些力不从心或者说不能如实的反应实际情况, ...

  4. 项目中遇到的 linq datatable select

    1如何使用DataTable.Select选出来的Rows生成新的DataTable?DataTable dt = 数据源;DataTable dtt = new DataTable();dtt=dt ...

  5. lintcode:寻找旋转排序数组中的最小值 II

    寻找旋转排序数组中的最小值 II 假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2). 你需要找到其中最小的元素. 数组中可能存在重复的元 ...

  6. android学习笔记二:Intent

    1.Intent作用 协助完成各个组建间的通信.如activity间.启动service.Broadcast. 2.Intent构成 1.Componet name:要启动的目的组建. 2.Actio ...

  7. Java:多线程

    创建线程的方式有两种: 第一种:使用线程类Thread或者继承它的子类创建线程对象 第二种:定义接口类实现接口Runnable创建线程对象 多线程的好处:可以整合资源,提高系统资源的利用率 多线程中提 ...

  8. Android 设置闹铃步骤和基础代码

    主要分三步: 1. 设置闹铃时间; 2. 接收闹铃事件广播; 3. 重开机后重新计算并设置闹铃时间;   1. 设置闹铃时间(毫秒) private void setAlarmTime(Context ...

  9. DataGridView过滤区分大小写问题

    DataTable上的过滤方法: 一.可以用DataTable.Select("条件"),返回DataRow[]格式的结果集. DataRow[] drArr = dt.Selec ...

  10. thrift 安装介绍

    一.About  thrift            thrift是一种可伸缩的跨语言服务的发展软件框架.它结合了功能强大的软件堆栈的代码生成引擎,以建设服务,工作效率和无缝地与C + +,C#,Ja ...