背景

有一个1300w的record.csv文件,其内容示例如下,文件大小2G。csv文件用逗号分隔,第一部分是唯一id,第二部分是爬取地址。

recordid,entranceimage
adfa-20932-ada-daf1a,http://xxxxx
adfa-20932-ada-dafa2,http://xxxxx

需求:

  1. 单机爬取文件,具有容灾机制
  2. 性能要高,具有稳定性
  3. 部署使用起来要简单,遂不依赖mysql,mongodb等记录,纯文件

需求分解

  1. 要有容灾机制,遂使用status.txt记录上一次执行到record.csv文件多少行。方便服务重启后能继续上一次的记录跑。
  2. 要具有稳定性,不能跑不起来吧。单文件2G不能一次加载到内存中,遂采用nio按行(readLine)读取 record.csv文件
  3. 性能要高 => 采用多线程工作。又因为容灾需要从上一次跑,遂不能丢失。采用生产者将Task放到线程池里,消费者 LinkedBlockingQueue<Future<T>>用队列有序消费。记录日志和status.txt文件行。
  4. 稳定性高,减少项目GC次数,对于频繁创建对象,情况允许创建对象池。
  5. 爬取数据的结果是图片,直接存储到本地文件,遂采用零拷贝
     InputStream is2 = httpUrlConn.getInputStream();
     Path target = Paths.get("",imageData.getPath());
     Files.createDirectories(target.getParent());
     Files.copy(is2, target, StandardCopyOption.REPLACE_EXISTING);

相关资料总结

优化方案

  1. Files 去readLine BufferedReader reader = Files.newBufferedReader(Paths.get(DATA_FILE_NAME), StandardCharsets.UTF_8);
  2. status.txt只记录总行数 log.txt记录日志 分开记录
  3. 对象池 在代码中减少对象的创建 减少jvm gc
  4. 基于zero-copy进行拷贝文件zero-copy
  5. 单生产者 消费者 毒丸问题 消费者停止问题
  6. HttpURLConnection 连接复用问题 =>KeepAlive