出现问题的代码是酱汁的:
process = subprocess.Popen("phantomjs crawler.js {url} {method} {data}", stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
try:
(output, error) = process.communicate(timeout=TIMEOUT)
re_result = re.findall(r"~~~~123(.*?)321~~~~", output.decode(), re.I|re.S)
if len(re_result) == 0:
print_error("Can't find result, %s" % cmd)
return []
res = simplejson.loads(re_result[0])
for item in res:
item["url"] = Parser.get_url(url, item["url"])
return res
except subprocess.TimeoutExpired:
process.kill()
print_error("TIMEOUT: %s" % cmd)
except Exception as e:
print(e)
由于PhantomJS的问题,导致任务超时。超时关闭是在Python中处理的,调用了process.kill()
,但在实际测试中发现PhantomJS进程并没有被kill掉。
为了Debug,我在process.kill()
之前,插入了一句print(process.pid)
。原本以为是kill()
函数没有正常运行,但让人惊讶的是,根据打印出来的pid,进程中并没有subprocess进程残留,打印的pid和未结束的PhantomJS进程pid并不相同,且phantomjs.pid = process.pid + 1。看起来是subprocess在调度任务的时候,开启了两个进程,在父进程被kill的时候,并没有kill子进程(或者两个进程之间根本没有父子关系)。
很快查到了问题所在,shell=True
参数会模拟在shell中执行,先是起了shell进程,再从shell起了phantomjs进程。解决方案(
去掉了shell=True
,改用list格式传参。):
process = subprocess.Popen(["phantomjs", "crawler.js", {url}, {method}, {data}], stdout=subprocess.PIPE, stderr=subprocess.PIPE)