11

質問

アクションがビューをレンダリングしている間に、そのアクションの 1 つから 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 アクティブレコード

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

リサーチ

次の Web サイトも確認しました (リンクを 2 つしか投稿できません) が、要求が完了するまで待機するアクションが必要であるか、curl が必要です (展開サーバーではアクセスできません)。外部ライブラリが必要です。ネイティブの 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('Y-m-d');
    $model->last_modified=date('Y-m-d 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));
}

それにもかかわらず、関数を非同期的に呼び出す方法を誰かが投稿できればいいのですが。

4

3 に答える 3

4

通常、この種の問題の解決策は、システムにメッセージ バスを統合することです。Beanstalkdのような製品を検討できます。これには、サーバーにソフトウェアをインストールする必要があります。この提案は「外部ライブラリの使用」と呼ばれると思います。

配置サーバーにアクセスでき、cronjob を追加できる場合 (または、sysadmin ができる場合)、コントローラーによって埋められたデータベース内のジョブ キューからジョブを読み取るスクリプトへの php-cli 呼び出しを実行する cronjob を検討できます。方法。

実行中のサーバーにソフトウェアをインストールできない場合は、Iron.ioなどの SAAS ソリューションを使用してバス機能をホストすることを検討できます。Iron.io は、プッシュ キューと呼ばれるものを使用しています。プッシュ キューを使用すると、メッセージ バスはメッセージ コンテンツを使用して、登録済みのリスナーに対して要求 (プッシュ) をアクティブに実行します。curl リクエストを実行する必要がないため、これでうまくいく可能性があります。

上記のいずれも不可能な場合は、手を縛られています。このテーマに非常に関連する別の投稿: Scalable, Delayed PHP Processing

于 2014-02-24T06:48:20.757 に答える
2

Yii が適切に動作するとは 100% ではありませんが、これは比較的簡単で試してみる価値があります。

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);
    }
}
于 2012-04-23T17:46:17.907 に答える
1

これは、まったく異なるタイプの提案です。CWebApplication の end() 関数によって起動される onEndRequest イベントに登録するのはどうですか?

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

イベントに登録し、何らかの方法でモデルを渡す方法を理解する必要がありますが、すべてのデータがブラウザーにフラッシュされた後、コードは適切に実行されます...

于 2012-04-23T18:57:52.617 に答える