例如,在网络抓取工具中,它会维护一组全球访问过的网址。一旦工作人员开始处理URL或已完成URL,其他工作人员不应使用相同的URL。在Java中实现这一点的一种方法是将访问过的URL放在ConcurrentHashMap
中(可能是Set更好)。每个工作人员在访问URL之前都会查看地图
if (visited.putIfAbsent(url, true) == null) {
crawl(url);
} else {
// do nothing
}
在Clojure中,我在atom
中使用了一个集合。每次我要使用最新访问过的URL交换新集时,交换函数应该检查集合是否已经具有此URL。如果URL存在,那么工作人员应该从那里停止。为了能够告诉工作者交换是否成功,我必须将返回值保存在全局状态,如[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))
它有效,但对我来说看起来很难看。问题是交换功能不允许返回呼叫站点的值。在Clojure中有更好的方法吗?
答案 0 :(得分:5)
Refs是为这种事做的。这是一个简单的方法
(when (dosync (when-not (@visited-urls-ref url-to-visit)
(alter visited-urls-ref conj url-to-visit)))
; continue crawling url-to-visit
)
我无法想象它会为网络抓取工具增加任何重大开销。
就个人而言,假设访问网址的顺序并不重要,我会创建一个带有dedupe
传感器的core.async频道,并让所有工作人员将网址放入/取出。