PHP Асинхронный метод вызова в Yii Framework

Вопрос

Я хочу знать, можно ли асинхронно вызывать метод контроллера Yii из одного из своих действий, в то время как действие отображает представление, оставляя метод для завершения длительной операции. Я хотел бы сделать что-то вроде кода ниже, и мне не нужно возвращать результат из my_long_running_func .

 public function actionCreate() { $model = new Vacancies; if (isset($_POST['Vacancies'])) { $model->setAttributes($_POST['Vacancies']); $model->save(); //I wish :) call_user_func_async('my_long_running_func',$model); } $this->render('create', array( 'model' => $model)); } 

проблема

Я пытаюсь написать действие контроллера в Yii, которое размещает вакансию и уведомляет заинтересованных подписчиков о почте. Проблема в том, что для выполнения запроса уведомления требуется много времени.

Теперь я ищу способ асинхронного запуска запроса, чтобы плакат видел его ответ как можно меньше времени, пока запрос выполняется в фоновом режиме способом, подобным делегатам или событиям C #.

Решения, которые я искал, выполняли асинхронные запросы (запросы) во время действия контроллера, но все, что я хочу сделать, – это запустить асинхронный метод контроллера, и действие должно было дождаться завершения запроса (ов) .

попытка

Я пробовал следующие методы, но запрос по-прежнему медленный для моих тестовых данных около 1500 пользователей.

  • Yii ActiveRecord

     if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id)); $trainees = YumUser::getUsersByRole('Trainees'); if($trainees!=null) { foreach($trainees as $trainee){ $message = new YumMessage; $message->from_user_id = Yii::app()->user->id; $message->title = 'Vacancy Notification: '.date('M j, Y'); $message->message = "A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $message->to_user_id = $trainee->id; $message->save(); } } } } 
  • Объекты доступа Yii

     if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id)); $trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn(); $fid=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'Vacancy Notification: '.date('M j, Y'); if($trainee_ids!=null) { foreach($trainee_ids as $trainee_id){ Yii::app()->db->createCommand() ->insert('message',array('timestamp'=>$ts,'from_user_id'=>$fid,'to_user_id'=>$tid,'title'=>$tt,'message'=>$msg)); } } } } 
  • Подготовленные заявления

     if ($vacancy->save()) { if($vacancy->is_active == 1) { $url = Yii::app()->createUrl('vacancies/view',array('id'=>$model->id)); $trainee_ids=Yii::app()->db->createCommand()->select('user_id')->from('trainee')->queryColumn(); $fu=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'Vacancy Notification: '.date('M j, Y'); $sql="INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) VALUES (:ts,:fu,:tt,:msg,:tu)"; if($trainee_ids!=null) { foreach($trainee_ids as $trainee_id){ $command=Yii::app()->db->createCommand($sql); $command->bindParam(":ts",$ts,PDO::PARAM_INT); $command->bindParam(":fu",$fu,PDO::PARAM_INT); $command->bindParam(":tt",$tt,PDO::PARAM_STR); $command->bindParam(":msg",$msg,PDO::PARAM_STR); $command->bindParam(":tu",$trainee_id,PDO::PARAM_INT); $command->execute(); } } } } 

Исследование

Я также проверил следующие веб-сайты (мне разрешено размещать только две ссылки), но они требуют либо действия, чтобы дождаться завершения запроса, либо потребовать завиток (к которому у меня нет доступа на сервере развертывания) или нужна внешняя библиотека. Я надеялся на собственную реализацию PHP.

  • PHP-моделирование многопоточности
  • Многопоточность в php
  • Асинхронные вызовы PHP?
  • Асинхронная обработка в PHP

редактировать

Мне удалось значительно сократить время отклика, переписав мой запрос таким образом (перемещение петли пользователя на уровень базы данных):

 public function actionCreate() { $user=YumUser::model()->findByPk(Yii::app()->user->id); $model = new Vacancies; $model->corporate_id=$user->professional->institution->corporate->id; $model->date_posted=date('Ym-d'); $model->last_modified=date('Ymd H:i:s'); if (isset($_POST['Vacancies'])) { $model->setAttributes($_POST['Vacancies']); if ($model->save()) { if($model->is_active == 1) { $url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id)); $fu=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'New Vacancy: '.$model->title; $sql='INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) SELECT :ts,:fu,:tt,:msg,t.user_id FROM trainee t'; Yii::app()->db->createCommand($sql)->execute(array(':ts'=>$ts,':fu'=>$fu,':tt'=>$tt,':msg'=>$msg)); } if (Yii::app()->getRequest()->getIsAjaxRequest()) Yii::app()->end(); else $this->redirect(array('view', 'id' => $model->id)); } } $this->render('create', array( 'model' => $model)); } на public function actionCreate() { $user=YumUser::model()->findByPk(Yii::app()->user->id); $model = new Vacancies; $model->corporate_id=$user->professional->institution->corporate->id; $model->date_posted=date('Ym-d'); $model->last_modified=date('Ymd H:i:s'); if (isset($_POST['Vacancies'])) { $model->setAttributes($_POST['Vacancies']); if ($model->save()) { if($model->is_active == 1) { $url = Yii::app()->createAbsoluteUrl('vacancies/view',array('id'=>$model->id)); $fu=Yii::app()->user->id; $msg="A new vacancy has been posted at <a href='{$url}'>{$url}</a>."; $ts = time(); $tt = 'New Vacancy: '.$model->title; $sql='INSERT INTO message (timestamp,from_user_id,title,message,to_user_id) SELECT :ts,:fu,:tt,:msg,t.user_id FROM trainee t'; Yii::app()->db->createCommand($sql)->execute(array(':ts'=>$ts,':fu'=>$fu,':tt'=>$tt,':msg'=>$msg)); } if (Yii::app()->getRequest()->getIsAjaxRequest()) Yii::app()->end(); else $this->redirect(array('view', 'id' => $model->id)); } } $this->render('create', array( 'model' => $model)); } 

Несмотря на это, было бы неплохо, если бы кто-то мог написать способ вызова функций асинхронно.

Как правило, решением для таких проблем было бы объединение шины сообщений в вашу систему. Вы можете рассмотреть такой продукт, как Beanstalkd . Это требует установки программного обеспечения на вашем сервере. Я предполагаю, что это предложение будет называться «с использованием внешней библиотеки».

Если вы можете получить доступ к серверу развертывания, и вы можете добавить cronjob (или, может быть, sysadmin can), вы можете рассмотреть cronjob, который вызывает вызов php-cli скрипту, который читает задания из очереди заданий в вашей базе данных, которая заполняется контроллером метод.

Если вы не можете установить программное обеспечение на сервер, на котором вы работаете, вы можете рассмотреть возможность использования решения SAAS, такого как Iron.io, для размещения функций шины. Iron.io использует так называемую push-очередь . С помощью push-очереди шина сообщения активно выполняет запрос (push) к зарегистрированным слушателям с содержимым сообщения. Это может сработать, так как это не требует от вас запроса на завивание.

Если ни одно из указанных выше не возможно, ваши руки связаны. Другое сообщение, которое весьма актуально по теме: Масштабируемая, отложенная обработка PHP

Я бы попробовал это, хотя я не на 100% уверен, что Yii будет работать правильно, но его относительно просто и стоит:

 public function actionCreate() { $model = new Vacancies; if (isset($_POST['Vacancies'])) { $model->setAttributes($_POST['Vacancies']); $model->save(); //I wish :) } HttpResponse::setContentType('text/html'); HttpResponse::setData($this->render('create', array( 'model' => $model), true); HttpResponse::send(); flush(); // writes the response out to the client if (isset($_POST['Vacancies'])) { call_user_func_async('my_long_running_func',$model); } } 

Вот совершенно другой тип предложения. Как насчет регистрации для события onEndRequest, которое запускается функцией end () CWebApplication ?

 public function end($status=0, $exit=true) { if($this->hasEventHandler('onEndRequest')) $this->onEndRequest(new CEvent($this)); if($exit) exit($status); } 

Вам нужно будет зарегистрироваться на мероприятие и выяснить, как передать свою модель каким-то образом, но код будет правильно запускаться после того, как все данные будут сброшены в браузер …