Llamada a un método asincrónico PHP en el marco Yii

Pregunta

Deseo saber si es posible invocar asincrónicamente un método de controlador Yii desde una de sus acciones mientras la acción representa una vista, dejando que el método complete una operación de larga ejecución. Me encantaría hacer algo como el siguiente código y no necesito devolver un resultado de 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)); } 

Problema

Intento escribir una acción de controlador en Yii que publique una vacante y notifique a los suscriptores interesados ​​de la publicación. El problema es que lleva mucho tiempo ejecutar la consulta de notificación.

Ahora estoy buscando una forma de ejecutar asíncronamente la consulta para que el póster vea su respuesta en el menor tiempo posible mientras la consulta se ejecuta en segundo plano de forma similar a los delegates o eventos de C #.

Las soluciones que busqué en Google realizaron solicitudes asíncronas durante el curso de la acción del controlador, pero todo lo que quiero hacer es ejecutar un método del controlador de forma asincrónica y la acción tuvo que esperar hasta que se completaran las solicitudes .

Intentó

He intentado los siguientes métodos, pero la consulta aún es lenta para mis datos de prueba de aproximadamente 1500 usuarios.

  • 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 https://stackoverflow.com/questions/10284966/php-asynchronous-method-call-in-the-yii-framework/{$url}."; $message->to_user_id = $trainee->id; $message->save(); } } } } 
  • Objetos de acceso a datos 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 https://stackoverflow.com/questions/10284966/php-asynchronous-method-call-in-the-yii-framework/{$url}."; $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)); } } } } 
  • Declaraciones preparadas

     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 https://stackoverflow.com/questions/10284966/php-asynchronous-method-call-in-the-yii-framework/{$url}."; $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(); } } } } 

Investigación

También he consultado los siguientes sitios web (solo tengo permiso para publicar dos enlaces), pero o bien requieren que la acción espere a que se complete la solicitud o necesita curl (a lo que no tengo acceso en el servidor de implementación) o necesita una biblioteca externa. Esperaba una implementación PHP nativa.

  • PHP multihilo simulado
  • Multithreading en php
  • Llamadas PHP asincrónicas?
  • Procesamiento asincrónico en PHP

Editar

Pude reducir considerablemente el tiempo de respuesta reescribiendo mi consulta de esta manera (moviendo el bucle de usuario a la capa de la base de datos):

 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 https://stackoverflow.com/questions/10284966/php-asynchronous-method-call-in-the-yii-framework/{$url}."; $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)); } 

No obstante, sería bueno que alguien pudiera publicar una forma de llamar a las funciones de forma asíncrona.

Normalmente, la solución para este tipo de problemas sería integrar un bus de mensajes en su sistema. Podría considerar un producto como Beanstalkd . Esto requiere instalar software en su servidor. Supongo que esta sugerencia se llamaría “usar una biblioteca externa”.

Si puede acceder al servidor de implementación y puede agregar cronjob (o tal vez una instancia de sysadmin), podría considerar un cronjob que hace una llamada php-cli a un script que lee trabajos de una cola de trabajos en su base de datos que llena el controlador método.

Si no puede instalar el software en el servidor que está ejecutando, podría considerar usar una solución SAAS como Iron.io para alojar la funcionalidad del bus por usted. Iron.io está utilizando lo que se llama una cola de inserción . Con una cola de inserción, el bus de mensajes realiza activamente una solicitud (push) a los oyentes registrados con el contenido del mensaje. Esto podría funcionar, ya que no requiere que hagas una solicitud curl.

Si ninguno de los anteriores es posible, tus manos están atadas. Otra publicación que es bastante relevante sobre el tema: Procesamiento escalable y demorado de PHP

Intentaría esto, aunque no estoy al 100% de que Yii funcione correctamente, pero es relativamente simple y vale la pena ir:

 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); } } 

Aquí hay un tipo de sugerencia completamente diferente. ¿Qué hay de registrarse para el evento onEndRequest que dispara la función end () de CWebApplication ?

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

Tendrías que registrarte para el evento y descubrir cómo pasar tu modelo de alguna manera, pero el código se ejecutaría correctamente después de que todos los datos hayan sido descargados al navegador …