当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 ...
随机推荐
- matlab三维画图
matlab三维画图主要有三个命令:plot3命令.mesh命令和surf命令. plot3 plot3是三维画图的基本函数,绘制的是最为主要的3D曲线图,最主要的调用格式是: plot3(X,Y,Z ...
- ubuntu: root用户
ubuntu怎么设置root用户 http://blog.csdn.net/chenping314159/article/details/7561339 创建root帐号: 在安装系统时,root账 ...
- Ch02 从零开始实例学习3
提纲:---------------------------- 演练2-3:添加控制器 知识点2-3:控制器的职责 知识点2-4:控制器的类别与方法 ------------------------- ...
- teamviewer无法启动
在 Linux.Mac OS X和 Windows下都可以用,但在 Linux 下无法启动时怎么办? 笔者回家工作时,都会用 Teamviewer 连线到其他 Linux 桌面,但某天起 Teamvi ...
- bootstrap datatable项目封装支持单选多选
自己在开发项目是根据自己的项目后台框架封装的jquery datatable插件基本上能集成到任何项目中使用,当然封装的还不够完美,给大家学习 调侃 使用介绍:query_dataTable({tab ...
- HTML5的优缺点是什么?
HTML5的优缺点是什么?作为HTML的第五次重大修改,HTML5有哪些改进?HTML5又有哪些缺点? 网络标准 HTML5本身是由W3C推荐出来的,它的开发是通过谷歌.苹果,诺基亚.中国移动等几百家 ...
- BLE简介和Android BLE编程
一.BLE和BT区别 其实我知道许多程序员不太喜欢阅读除了代码以外的文档,因为有时这些过于冗长的文档对编程并没有更多的好处,有了协议,接口,demo差不多很多人就能写出很好质量的代码了.但其实更深入的 ...
- c#引用web.config中的ConnectionString
c#引用web.config中的ConnectionString <connectionStrings> <add name="JKXTConnectionString& ...
- WEB应用如何解决安全退出问题
让我先来描述一下这个情况.一位用户第一次请求一个web页面,web应用弹出登录窗口提示用户登录,用户输入用户名,密码,验证码后服务器进行判断,正确后,返回用户请求的页面. 此时,用户有事需要离 ...
- JAVA之File类创建对象构造函数传参数需要注意的几点
java中File类用于创建一个文件对象. 首先看一段代码: 1. package MyText1; import java.io.File; public class MyText1 { publi ...