我是Rust的新手,我想把我制作的Go网络爬虫移植到Rust上。在Go中,我创建了一个供多个worker使用(和共享)的hashmap (go例程生成相同的函数)。使用Mutexes很容易解决这个问题,但我不能掌握如何在Rust中做同样的事情。
Crawler结构是:
struct Crawler {
client: reqwest::Client,
target: String,
visited: Arc<Mutex<HashSet<String>>>,
queue: Arc<Mutex<Queue<String>>>,
base_url: String,
fetch_any_domain: bool,
workers: u8,
}
在爬虫程序的impl
中,我添加了运行函数:
fn run(&self) {
{
match self
.queue
.lock()
.unwrap()
.add(self.convert_link_to_abs(self.target.as_str()))
{
Err(e) => println!("{}", e),
_ => (),
}
}
while self.queue.lock().unwrap().size() > 0 {
match self.queue.lock().unwrap().remove() {
Ok(link) => match self.fetch(link.as_str()) {
Ok(content) => match self.get_links(content) {
Ok(()) => println!("added new links"),
Err(e) => println!("{}", e),
},
Err(e) => println!("{}", e),
},
Err(e) => println!("{}", e),
}
}
}
我试着同时调用它,就像这样:
let mut threads = vec![];
let c = Arc::new(Mutex::new(crawler));
for _i in 0..workers {
let cc = c.clone();
threads.push(thread::spawn(move || {
let guard = cc.lock().unwrap();
guard.run();
}));
}
for t in threads {
let _ = t.join();
}
代码以某种方式运行,但它几乎立即被卡住,没有处理任何东西。我确信我只需要习惯Rust方法,但是有人能建议一下实现多线程爬虫的最好方法是什么吗?
非常感谢
发布于 2020-03-04 04:56:57
问题不在于HashSet,而在于队列。如果用标准库中的Vec替换外部板条箱中的队列,并拆分一些语句,它将工作得很好。
fn run(&self) {
{
self.queue
.lock()
.unwrap()
.push(self.convert_link_to_abs(self.target.as_str()))
}
while self.queue.lock().unwrap().len() > 0 {
let x = self.queue.lock().unwrap().pop();
match x {
Some(link) => match self.fetch(&link) {
Ok(content) => match self.get_links(content) {
Ok(()) => println!("added new links"),
Err(e) => println!("{}", e),
},
Err(e) => println!("{}", e),
},
_ => {}
}
}
}
最大的变化是我从match语句之外的队列中弹出。我认为如果在match中有整个.lock().unwrap().pop()
语句,那么整个match块的内容都会被锁住。
但是,我不确定如果您对您使用的队列机箱执行相同的操作,它为什么不能工作。我也是一个Rust初学者,所以其中一些对我来说也是不清楚的。
我对您的代码所做的更改可以在这里看到:https://pastebin.com/ZrXrsgzf。我测试了它,它运行了(至少它通过了它最初被卡住的地方)。
我最近还在Rust中实现了一个网络爬虫,并在here上写了关于它的文章。
https://stackoverflow.com/questions/60103516
复制相似问题