atom 在网络爬虫中的惯用用法

Idiomatic use of atom in a web crawler

例如,在网络爬虫中,它维护了一组全局的已访问 URL。一旦一名工人开始 URL 工作或完成 URL,其他工人不应接受相同的 URL。在 Java 中实现这一点的一种方法是将访问过的 URL 放在 ConcurrentHashMap 中(Set 可能更好)。每个工人在访问 URL

之前先查看地图
if (visited.putIfAbsent(url, true) == null) {
  crawl(url);
} else {
  // do nothing
}

在 Clojure 中,我在 atom 中使用集合。每次我要用最近访问的 URL 交换一个新集合时,交换函数应该检查集合是否已经有这个 URL。如果 URL 存在,工作人员应该从那里停止。为了能够告诉工作人员交换是否成功,我必须将 return 值保存在全局状态中,例如 [visited-urls last-swap-succeeded]

(def state (atom [#{} nil]))
(defn f [state key] (let [[visited-urls l] state] (if (visited-urls key) [visited-urls false] [(conj (visited-urls key) true]))))

工人应该做的

(when (second (swap! state f url)) (crawl url))

它可以工作,但对我来说看起来很丑。问题是交换函数不允许 return 值到调用点。在 Clojure 中有更好的方法吗?

参考文献是为这类事情而制作的。这是一个简单的方法

(when (dosync (when-not (@visited-urls-ref url-to-visit)
                (alter visited-urls-ref conj url-to-visit)))
  ; continue crawling url-to-visit
  )

我无法想象它会为网络爬虫增加任何显着的开销。

就我个人而言,假设访问 url 的顺序并不重要,我将创建一个带有 dedupe 转换器的 core.async 通道,并让所有工作人员 put/take url to/from那个。