沃梦达 / 编程问答 / php问题 / 正文

Yii框架中的PHP异步方法调用

PHP Asynchronous Method Call In The Yii Framework(Yii框架中的PHP异步方法调用)

本文介绍了Yii框架中的PHP异步方法调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否可以在操作呈现视图时从其操作之一异步调用 Yii 控制器方法,让该方法完成长时间运行的操作.我很想做类似下面的代码的事情,我不需要从 my_long_running_func 返回结果.

I want to know if it is possible to asynchronously invoke a Yii controller method from one of its actions while the action renders a view, leaving the method to complete a long running operation. I would love to do something like the code below and I don't need to return a result from 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 中编写一个控制器操作,用于发布职位空缺并通知感兴趣的订阅者.问题是执行通知查询需要很长时间.

Problem

I am trying to write a controller action in Yii that posts a vacancy and notifies interested subscribers of the post. The problem is that it takes a long time to execute the notification query.

现在我正在寻找一种异步运行查询的方法,以便发布者在尽可能短的时间内看到他的响应,同时查询以类似于 C# 委托或事件的方式在后台运行.

Now I am searching for a way to asynchronously run the query so the poster sees his response in as little time as possible while the query runs in the background in a way similar to C# delegates or events.

我搜索的解决方案在控制器操作过程中执行了异步请求,但我想要做的就是异步运行控制器的方法,并且操作必须等待直到请求完成.

The solutions I googled up performed asynchronous request(s) during the course of the controller action but all I want to do is to run a method of the controller asynchronously and the action had to wait till the request(s) were completed.

我尝试了以下方法,但对于我大约 1500 个用户的测试数据,查询仍然很慢.

I have tried the following methods but the query is still slow for my test data of about 1500 users.

  • Yii ActiveRecord

  • 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 数据访问对象

  • Yii Data Access Objects

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

  • 准备好的声明

  • Prepared Statements

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

  • 我还检查了以下网站(我只允许发布两个链接),但它们要么需要等待请求完成的操作,要么需要 curl(我在部署时无权访问)服务器)或需要一个外部库.我希望有一个原生的 PHP 实现.

    I have also checked the following websites (I'm only allowed to post two links) but they either require the action to wait for the request to be completed or need curl (which I don't have access to on the deployment server) or need an external library. I was hoping for a native PHP implementation.

    • PHP 模拟多线程
    • php 中的多线程
    • 异步 PHP 调用?
    • PHP 中的异步处理

    通过以这种方式重写我的查询(将用户循环移动到数据库层),我能够显着减少响应时间:

    I was able to decrease response time considerably by rewriting my query in this way (moving the user loop to the database layer):

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

    尽管如此,如果有人可以发布一种异步调用函数的方法就好了.

    Notwithstanding, it would be nice if someone could post a way to call functions asynchronously.

    推荐答案

    通常,此类问题的解决方案是在您的系统中集成消息总线.你可以考虑像 Beanstalkd 这样的产品.这需要在您的服务器上安装软件.我想这个建议会被称为使用外部库".

    Typically, the solution for these kind of problems would be to integrate a message-bus in your system. You could consider a product like Beanstalkd. This requires installing software on your server. I suppose this suggestion would be called "using an external library".

    如果您可以访问部署服务器并且可以添加 cronjob(或者系统管理员可以),您可以考虑使用 cronjob 执行 php-cli 调用脚本,该脚本从数据库中已填充的作业队列中读取作业通过控制器方法.

    If you can access the deployment server and you can add cronjob (or maybe a sysadmin can) you could consider a cronjob that does a php-cli call to a script that reads jobs from a job queue in your database which is filled by the controller method.

    如果您无法在正在运行的服务器上安装软件,您可以考虑使用像 Iron.io 这样的 SAAS 解决方案 为您托管总线功能.Iron.io 正在使用所谓的推送队列.使用推送队列,消息总线会主动向已注册的侦听器发送消息内容的请求(推送).这可能有效,因为它不需要您执行 curl 请求.

    If you cannot install software on the server you're running, you could consider using a SAAS solution like Iron.io to host the bus functionality for you. Iron.io is using what is called a push queue. With a push queue the message bus actively performs a request (push) to the registered listeners with the message content. This might work since it doesn't require you to do a curl request.

    如果以上都不可能,你的手就被束缚住了.另一篇与该主题非常相关的帖子:可扩展的延迟 PHP 处理

    If none of the above is possible, your hands are tied. Another post which is quite relevant on the subject: Scalable, Delayed PHP Processing

    这篇关于Yii框架中的PHP异步方法调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

    本文标题为:Yii框架中的PHP异步方法调用

    基础教程推荐