首页 > 解决方案 > 如何在事先不知道将要处理的项目数量的情况下停止队列处理?

问题描述

假设我正在以生产者/消费者模型的形式构建一个网络爬虫。

我的爬虫有一个队列,生产者将种子 URL 加入队列。消费者从队列中读取,爬取页面中的链接,并在重复验证(使用“已处理”集合完成)后将它们排入同一队列。

我的问题是我不知道种子 URL 可以有多少页。此外,我不想让爬虫无限制地运行,并且希望threshold完全爬取 500 个页面。

我的代码如下:

int retryCount = 0;
while(true){
    if(!queue.empty()){
        process(queue.poll()); // assume process method runs in multiple threads.
        retryCount = 0; // reset the retry count
    }else{
        Thread.sleep(1000) // wait for 1 second before retrying.
        if(retryCount == threshold){
            break;
        }
        retryCount++;
    }
}

案例 1:如果我的种子 URL 有 5 个页面,那么这 5 个页面会被抓取,并且在某一时刻,队列将变为空,这将在退出 while 循环之前启动重试逻辑。这也有助于我防止在抓取时出现任何网络延迟,即。它就像一个超时。

案例 2:如果我的种子 URL 有更多页面,比如 100,那么我的队列将加载 100,这反过来又会继续加载更多页面。现在如何限制页面抓取限制?

我的方法:

  1. 针对每个种子 URL,我维护一个计数器映射,让我知道当前处理的页面。基于此,我限制了进程调用,然后超时逻辑启动,循环退出。这里的问题是我也需要使地图线程安全,这增加了更多的复杂性。此外,这种方法似乎很老套,因为我依赖于退出的重试而不是适当的关闭。
  2. threshold在 while 循环中检查的带有计数的信号量。我acquire()每次提交到process()0 时,我都会中断循环并等待处理完成。这将主要作为上限停止,但我仍将依赖于下限停止的超时逻辑。
  3. 使用毒丸/哨兵,这将再次成为上限。不知道如何计算出下限。

注意:我真的不能依靠队列为空来打破循环,因为它可能会导致竞争条件错误。此外,队列可以处理多个种子 URL,并且不限于一个域。

请告诉我处理这种情况的最佳方法是什么。

标签: javamultithreadingconcurrencyproducer-consumer

解决方案


推荐阅读