当Scheduler拿不到url的 时候,不能立即退出
在webmagic的多线程抓取中有一个比较麻烦的问题:当Scheduler拿不到url的 时候,不能立即退出,需要等到没抓完的线程都运行完毕,没有新url产生时,才能退出。之前使用Thread.sleep来实现,当拿不到url 时,sleep一段时间再取,确定没有线程执行之后,再退出。
但是这种方式始终不够优雅。Java里面有wait/notify机制可以解决这种同步的问题。于是webmagic 0.4.0用wait/notify机制代替了之前的Thread.sleep机制。代码如下:
while (!Thread.currentThread().isInterrupted() && stat.get() == STAT_RUNNING) {
Request request = scheduler.poll(this);
if (request == null) {
if (threadAlive.get() == 0 && exitWhenComplete) {
break;
}
// wait until new url added
waitNewUrl();
} else {
final Request requestFinal = request;
threadAlive.incrementAndGet();
executorService.execute(new Runnable() {
@Override
public void run() {
try {
processRequest(requestFinal);
} catch (Exception e) {
logger.error("download " + requestFinal + " error", e);
} finally {
threadAlive.decrementAndGet();
signalNewUrl();
}
}
});
}
}
private void waitNewUrl() {
try {
newUrlLock.lock();
try {
newUrlCondition.await();
} catch (InterruptedException e) {
}
} finally {
newUrlLock.unlock();
}
}
这里当线程完成之后,会调用signalNewUrl()来通知主线程,停止等待!
0.4.0发布之后,有用户问我,为什么有的时候抓完无法退出?我开始就怀疑这里可能存在线程安全问题,但是苦于无法复现。
思考了一下,有可能存在这样执行情况:
- threadAlive>0,执行
if (threadAlive.get() == 0 && exitWhenComplete)check跳过,于是准备进入waitNewUrl(); - 此时最后一个子线程执行结束,
threadAlive.decrementAndGet();和signalNewUrl();相继执行; - 此时主线程进入
waitNewUrl(),结果已无线程执行,也无人可以notify它了,于是线程一直等待…
那么似乎在lock里加入double-check就OK了?但是今天看了http://coolshell.cn/articles/4576.html这篇文章,大概意思是:出了问题不要靠猜!一定要复现并测试!
于是决定手动模拟!开启10个线程,并mock了所有部件,循环10000次去执行,代码不贴了,地址:https://github.com/code4craft/webmagic/blob/master/webmagic-core/src/test/java/us/codecraft/webmagic/SpiderTest.java。执行一下,果然到了第13次就卡住了!jstack之后,果然卡在newUrlCondition.await();这里!
然后加入double-check:
private void waitNewUrl() {
try {
newUrlLock.lock();
//double check
if (threadAlive.get() == 0 && exitWhenComplete) {
return;
}
try {
newUrlCondition.await();
} catch (InterruptedException e) {
}
} finally {
newUrlLock.unlock();
}
}
结果执行成功!至此问题解决!
经过这个例子,也大致明白了为什么wait/notify之前总是要先lock。为什么呢?有机会写一篇文章总结一下吧!
很简单,是吧?其实这篇文章只想说明一件事:出了bug不要靠猜!一定要复现并确认解决!
当Scheduler拿不到url的 时候,不能立即退出的更多相关文章
- asp.net mvc url应用
//url加密与解密string res1 = HttpUtility.UrlEncode("7Z2K5Lgk/iI="); //值是7Z2K5Lgk%2fiI%3d string ...
- (5)Quartz学习
原文:http://blog.csdn.net/zxl315/article/details/10879927 介绍Quartz Quartz是一个开源的任务调度系统,它能用来调度很多任务的执行. 运 ...
- scrapy-redis实现爬虫分布式爬取分析与实现
本文链接:http://blog.csdn.net/u012150179/article/details/38091411 一 scrapy-redis实现分布式爬取分析 所谓的scrapy-redi ...
- 大白痴学习webmagic
摘要 webmagic 学习 从头 刚刚开始学,很多东西可能理解错了,还请各位指教 一些基本类: Request:包含要爬行的url和一些附加信息,是Page的一个成员变量 主要成员变量 String ...
- scrapy-redis使用以及剖析
scrapy-redis是一个基于redis的scrapy组件,通过它可以快速实现简单分布式爬虫程序,该组件本质上提供了三大功能: scheduler - 调度器 dupefilter - URL去重 ...
- Python 经典面试题汇总之框架篇
前端和框架 1.谈谈你对http协议的认识 浏览器本质,socket客户端遵循Http协议 HTTP协议本质:通过\r\n分割的规范,请求响应之后断开链接 ==> 短连接.无状态 具体: Htt ...
- Scrapy 框架 配置文件
配置文件 基本配置 #1.项目名称,默认的USER_AGENT由它来构成,也作为日志记录的日志名 BOT_NAME = 'Amazon' #2.爬虫应用路径 SPIDER_MODULES = ['Am ...
- quartz.properties完整版
我们通常是通过quartz.properties属性配置文件(默认情况下均使用该文件)结合StdSchedulerFactory 来使用Quartz的.StdSchedulerFactory 会加载属 ...
- 315道python面试题(参考答案)
第一部分 Python基础篇 1:为什么学习Python 家里有在这个IT圈子里面,也想让我接触这个圈子,然后给我建议学的Python, 然后自己通过百度和向有学过Python的同学了解了Python ...
随机推荐
- Unix/Linux环境C编程新手教程(24) MySQL 5.7.4 for Red Hat Enterprise 7(RHEL7)的安装
远观历史, MySQL的主要目的是为了可以在单处理器核心的商业服务器上执行.现在MySQL的一个变化用户可能不会注意到,那就是甲骨文已经開始又一次架构MySQL的代码,使它大量的模块化.如软件解析器, ...
- ActiveMQ消息队列介绍(转)
ActiveMQ是一个开源兼容Java Message Service (JMS) 1.1面向消息的中件间. 来自Apache Software Foundation. ActiveMQ提供松耦合的应 ...
- 列举一些常见的Python HTTP服务器
要使 Python 写的程序能在 Web 上被访问,还需要搭建一个支持 Python 的 HTTP 服务器.下面列举一些常见的 Python HTTP 服务器,以及它们目前的大致发展情况,以便用户的对 ...
- POJ - 1185 炮兵阵地 (状态压缩)
题目大意:中文题目就不多说大意了 解题思路: 1.每行最多仅仅有十个位置,且不是山地就是平原,那么就能够用1表示山地,0表示平原,将每一行的状态进行压缩了 2.接着找出每行能放炮兵的状态.先不考虑其它 ...
- 1030 - Image Is Everything
Your new company is building a robot that can hold small lightweight objects. The robot will have th ...
- <转载>使CSS文字图片div元素居中方法之水平居中的几个方法
文字居中,文字垂直居中水平居中,图片居中,图片水平居中垂直居中,块元素垂直居中?当我们在做前端开发是时候关于css居中的问题是很常见的.情 况有很多种,不同的情况又有不同的解决方式.水平居中的方式解决 ...
- makefile 必知必会
Makefile 必知必会 Makefile的根本任务是根据规则生成目标文件. 规则 一条规则包含三个:目标文件,目标文件依赖的文件,更新(或生成)目标文件的命令. 规则: <目标文件>: ...
- windows 2003 DNS服务的重建
DNS的重建 网络上转载之文章,据说来自微软官方,留下一份自己用,曾经帮助我解决了DNS方面的困扰,希望能给大家带来帮助 1.打开dns管理器,删除domain.com区域,如果存在_msdcs.do ...
- Python脚本:获取股票信息
在水木上看到有人在问到想用python去获取股票的信息,sina finance上面的那些数据的是通过js控制的,会根据股票代码去获取实时信息然后根据用户友好的方式展示出来.首先,新浪的一个url让我 ...
- hdu 3350
hdu 3350 题意:让你求运算式的结果和运算过程中加法的次数 (a) > (b) ? (a) : (b) 大于取a,小于等于取b MAX( 1 + 2 , 3) 因为(a) > (b) ...