在我们的业务中,需要定时任务去执行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__);
通过日志发现,只有start,没有notify的记录,那么问题基本可以确定出现在curl上。curl做了一次http请求,那么有请求就有超时时间,查看curl_setopt选项,发现提供了两个请求超时选项的设置。如果设置为0,则无限等待,默认值没说。两个选项的优先级也没说。不过这里可以通过设置CURLOPT_TIMEOUT来限制curl最长执行时间,最后解决问题。
CURLOPT_CONNECTTIMEOUT 在尝试连接时等待的秒数。设置为0,则无限等待
CURLOPT_TIMEOUT 允许 cURL 函数执行的最长秒数。
网友评论