案例分析
前台异步上传文件到云端后台cron
*/10 * * * * /usr/local/bin/php /path/to/upload.php >> /tmp/apkqueue.log
有时候上传一个文件到云端会很耗时,一个cron还没有跑完,下一个cron又开启了,并且一个文件可能被大于一个cron在同时上传。
如果一个脚本的执行时间 比cron的间隔大的多的话,系统就会同时存在多个相同脚本同时执行。再如果脚本的子任务没有加锁的话,那么会出现同一条数据
被多个cron执行,造成系统资源浪费。解决的办法就是加锁控制同一个脚本在系统中任何时刻只有一个进程执行,通过锁的粒度可以做到不同的控制。
以下是我知道的两种策略:
1,执行的脚本加锁
lockf,flock
例:
* * * * * /usr/bin/flock -xn /tmp/full_diff.lock -c 'cd /usr/local/www/admin; /usr/local/bin/php upload.php > /dev/null 2>&1'
30 * * * * (/usr/bin/lockf -s -t 0 /tmp/cdn.push.lock /usr/local/bin/php /path/to/upload.php >> /tmp/log 2>&1)
2,在脚本中加锁
<?php
$fp = fopen("/tmp/lock.txt", "w+");
if (flock($fp, LOCK_EX | LOCK_NB)) { // 进行排它型锁定
run(); //执行任务
flock($fp, LOCK_UN); // 释放锁定
} else {
echo "Couldn't lock the file !";
}
fclose($fp);
?>
<?php
$fp = fopen("/tmp/lock.txt", "w+");
if (flock($fp, LOCK_EX | LOCK_NB)) { // 进行排它型锁定
run(); //执行任务
flock($fp, LOCK_UN); // 释放锁定
} else {
echo "Couldn't lock the file !";
}
fclose($fp);
?>
2,对子任务加锁(如果有)
mysql的innodb update操作是行锁的,可以利用这点对子任务加锁,一条数据代表一个子任务
这样可以更西粒度的控制一个子任务同一时刻只有一个进程在执行,同时还可以开启多个进程。
function run($procid=0)//人为标识的进程id
{
$procNum = 10;//假设开10个进程
$data = select(..) from .. where id%$procNum=$procid;
foreach($data as $row){
exec($row);
}
}
function exec($val)
{
//lock
if(model()->lock($val['id'])) //执行一条update table set status='lock' where id=12312 and status='unlock';
return;
...run..
model()->succ($val['id']);
}
crontab
0 * * * * /proc id=0
1 * * * * /proc id=1
2 * * * * /proc id=2
...
9 * * * * /proc id=9