数据抓取本身流程很简单,但是当遇到网站的种类变多或者要采集的数据变多的时候,性能问题会称为数据抓取中要首要解决的问题。 这几天同事在测试采集数据的时候总是遇到反应很慢的情况。今晚趁着洗完澡脑子清醒,针对部分问题重构了下;做下记录。
这次遇到的问题主要是代理的问题,场景是这样的:
- 我有100个代理,
- 系统初始化的时候,我把这100个代理中放到一个队列(ArrayBlockingQueue)中。
- 然后平均每个采集类(一个采集类对应一个网站或者一类网站)分配10个代理。
- 问题来了,当我网站超过10个到11个的时候,这时候启动系统,第十一个采集类会无法从代理中获取代理。于是第十一个类一直在登真队列中哪个好心人把代理资源放回来。可惜它永远登不到那天。
问题的原因其实是我多虑了,因为代理资源本身是可以多个采集类共用的。而我起初的考虑是为了让代理资源能够均衡使用,不至于某个代理频繁的被使用,而某些代理永远没有用到(浪费了)。
我的解决方法是这样的:
- 把代理资源放在一个List中,而不是队列中。
- 每个采集类获取代理的时候从list中顺序获取,从第1个代理获取,到100个以后,再重头开始继续依次获取。
- 由于List中无法自己标记当前获取到第几个了,且在多线程环境下,所以使用了AtomicInteger来做计数器。
-
参考代码如下:
import java.io.InputStream; import java.util.List; import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
public class ProxieQueue {
private static Logger logger = LoggerFactory.getLogger(ProxieQueue.class); private static List<Proxie> proxieList = com.google.common.collect.Lists.newArrayList(); private static AtomicInteger listCounter =new AtomicInteger(); private ProxieQueue() { } static { try { InputStream is =Configurations.class.getClassLoader().getResourceAsStream("proxies.txt"); List<String> lineList = IOUtils.readLines(is); ; for (String line : lineList) { String[] strArray = line.split(":"); Proxie proxie = new Proxie(); proxie.setUrl(strArray[0]); proxie.setUser(strArray[1]); proxie.setPass(strArray[2]); proxie.setPort(Integer.valueOf(strArray[3])); proxieList.add(p); } } catch (Exception e) { logger.error("读取代理配置失败", e); } } public static Proxie get() { int index = listCounter.getAndIncrement(); if(index == proxieList.size()){ //获取到结尾后,从头开始继续获取 index= listCounter.getAndSet(0); } return proxieList.get(index); } }
- 另外我是在系统初始化的时候就将各个网站的采集类初始化好的,这样其实没有必要,完全可以在采集这个网站的时候再进行初始化即可。