在工作中接触到了php多进程一段时间,在此做个记录,欢迎大家一起讨论学习。
在项目中会涉及到很很多定时任务,这些定时任务会去完成数据处理,通常大量的数据会采用多进程来处理,这篇文章简单记录一下项目中用到的多进程。
下面代码是多进程的定时任务
public function actionStartTest()
{
$this->process_num = $this->process_num > 0 ? $this->process_num : 1;
for ($current = 0; $current < $this->process_num; $current++) {
$command = sprintf(PHP_EXEC . ' ' . YII_PATH . ' test/show-test --key=%s -- process_num=%s --current=%s --limit=%s > /dev/null &', $this->key, $this->process_num, $current, $this->limit);
echo $command . "\n";
exec($command);
usleep(100000);
}
}
/**
* 进行多进程调用
*/
public function actionShowTest()
{
set_time_limit(0);
error_reporting(0);
$this->setMemoryLimit(1024);
echo 'lock-key:'.'test-'.$this->key;
//加锁
$this->lock('test-'.$this->key);
$ret = TestModel::getInstance()->dealData();
//放锁
$this->unlock();
Clog::info('测试展示多进程-' . count($ret));
}
在执行定时任务的加锁代码:
/**
* 进程锁
* @param $processName
*/
public function lock($processName){
if($this->process_num < $this->current){
CommonLog::info(['msg' => '非法进程','processName' => $processName , 'processNum' => $this->process_num , 'current' => $this->current]);
exit(0);
}
$this->_startTime = microtime(true);
$this->_processName = "{$processName}-{$this->current}-{$this->process_num}-{$this->region}";
$this->_lock = CommonModel::lock($this->_processName);
if(!$this->_lock){
CommonLog::info(['msg' => '抢占文件锁失败,已有相同进程运行','processName' => $this->_processName]);
exit(0);
}
}
/**
* 获取锁
* @param $fileName
* @return bool|resource
*/
public static function lock($fileName)
{
$lockFile = sys_get_temp_dir() . '/' . $fileName;
$fp = fopen($lockFile, 'a+');
if (flock($fp, LOCK_EX | LOCK_NB)) {
return $fp;
} else {
return false;
}
}
下面的代码是解锁代码:
public function unlock(){
CommonModel::unlock($this->_lock);
CommonLog::info(['msg' => '进程结束-执行时间为'.(microtime(true) - $this->_startTime),'processName' => $this->_processName]);
}
/**
* 解锁
* @param resource $fp
*/
public static function unlock($fp)
{
$fp && flock($fp, LOCK_UN);
}
处理数据时候的代码,dealData方法中,获取数据会涉及到多进程
$startDate = date('Y-m-d H:i:s', time() - 86400 * 2);
if (\Yii::$app->params['process_num'] > 1) {
$TestList = TestDao::find()
->select(['a.doc_id', 'b.id')
->where(['>', 'a.create_time', $startDate])
->andWhere('a.status = 0 AND b.error_count < 3 and a.id mod :processNum = :current', ['processNum' => \Yii::$app->params['process_num'], 'current' => \Yii::$app->params['current']])
->limit(\Yii::$app->params['limit'])
->asArray()
->all();
} else {
//按照单线程处理即可
}
最后我们只需要加入定时任务即可:
* * * * * /usr/bin/php /home/service/app/test/release/information/yiiinformation test/start-test --key=xxx --process_num=43 --limit=1800