php中curl未设置超时导致的睡眠进程问题

在我们的业务中,需要定时任务去执行php脚本,对苹果自动续费用户进行续约。某次发现有大量的定时任务在执行,有些开始时间是半年前,所有进程都处于睡眠中。

root     31625  0.0  0.0 108124  1304 ?        Ss   Mar22   0:00 /bin/sh -c /usr/local/php7/bin/php -r 'Crontab::debug("/data/erge_api", "Cron_Apple_Pay_Subscribe");' >> /tmp/cronapplepaysubscribe.log 2>&1
root     31634  0.0  0.0 233520 10064 ?        S    Mar22   0:49 /usr/local/php7/bin/php -r Crontab::debug("/data/erge_api", "Cron_Apple_Pay_Subscribe");

通过lsof命令可以发现,该进程打开了两个socket,一个是连接数据库的,一个是请求苹果服务器的。

php     31634 root    3u  IPv4 3973134538      0t0        TCP localhost:63880->localhost:27017 (ESTABLISHED)
php     31634 root    4u  IPv4 3973134589      0t0        TCP 192.168.10.100:17462->17.154.66.159:https (ESTABLISHED)

再通过strace命令,可以发现进程一直在进行如下调用,而且文件描述符就是4,也就是说,进程一直轮询地获取从苹果服务器那边的数据。

restart_syscall(<... resuming interrupted call ...>) = 0
poll([{fd=4, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
clock_gettime(CLOCK_MONOTONIC, {41212631, 418739828}) = 0
clock_gettime(CLOCK_MONOTONIC, {41212631, 418821856}) = 0
clock_gettime(CLOCK_MONOTONIC, {41212631, 418887370}) = 0

为了确认这个现象,可以在应用程序中加上日志。Logger打印日志的时候还会加上进程ID。

        Logger::crontab($value, 'start', __CLASS__);
        $ch = curl_init($endpoint);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);   
        $response = curl_exec($ch);
        $errno = curl_errno($ch);
        $errmsg = curl_error($ch);
        curl_close($ch);
        Logger::crontab($response, "applepay|notify|receiptdata", __CLASS__);