Fising's Blog

all about web application development

找出脚本卡死的元凶

周末在家,同是做技术的朋友给我发来一个求助:laravel schedule:run 跑起来的几个脚本进程,怎么一直不结束?

我们知道 schedule 是 laravel 框架提供的计划任务支持。通过统一的计划任务入口脚本,挂载全部的子任务。也就是说,我们所写的所有任务,都将作为 schedule:run 的子任务来运行。

来看他的问题。他所说的几个进程如下:

基于对于 laravel 框架的了解,可以大致猜出这几个进程的关系。即:每个进程都是上面一行进程的的子进程。通过 pstree 可以快速验证一下:

由此可以猜测,因为 13308 进程处于阻塞状态,导致其所有的祖先进程全部卡在这里。strace 一下其父进程 13307 进程:

我们发现该进程正在执行 wait4 的系统调用。这个页面有对该函数的说明:

这是一种 BSD 风格的函数,他的作用是等待进程终止。同样在这页文档中我们可以看到对于第一个参数的解释:

当第一个参数 pid 为 -1 时,等待任何子进程的结束。等同于调用 wait3 函数。换言之,13307 进程的确在等待它的子进程 13308 的结束。再来看看 13308 进程:

这个进程正在忙碌着,不断的执行 clock_gettime 和 poll 调用。前者是读取系统时间,我们来关注一下后者。

poll 类似于 select , 它等待一组文件描述符就绪以便执行 I/O 操作。在当前这个例子里,这个编号为 8 的 fd 文件描述符迟迟未能 ready,因此脚本一直被阻塞在这里不能继续。那么 8 这个 fd 到底是什么呢?

原来是一个 socket 连接,他的 inode 为 6969596。在 /proc/net/tcp 这个文件中有当前的连接信息:

可以看到 inode 为 6969596 的连接,rem_address 为:C8B01878:01BB。其中冒号前的部分为 host,后面的部分为 port,转换为十进制表示为:3366983800:443,也就是 200.176.24.120:443。经查,200.176.24.120 这个IP地址位于巴西。

这个脚本是一个网页爬虫,的确会抓取几个网站,不过都是国内的站点。为什么会有巴西的 IP 地址存在?通过一番查找,正确的 IP 地址为 120.24.176.200,这是深圳阿里云机房的 IP 地址。原来, /proc/net/tcp 文件中的远程主机地址采用网络字节序存储。也就是大端法。netstat 看看证实一下:

注意看最后一段 13308/php 证实的确是由于这个网络连接不能正常读写导致的。明确了问题的原因,解决起来就不难了。可以为 curl 设置一个 time 超时选项,相关的选项如下:

 

发表评论