dptsivmg82908 2014-10-09 03:34
浏览 184

Yii Php执行异步后台请求

Hi i'm trying to execute a LONG RUNNING request (action) in background.

function actionRequest($id){

//execute very long process here in background but continue redirect

Yii::app()->user->setFlash('success', "Currently processing your request you may check it from time to time.");
$this->redirect(array('index', 'id'=>$id));
}

What i'm trying to achieve is to NOT have the user waiting for the request to be processed since it generally takes 5-10min, and the request usually goes to a timeout, and even if I set the timeout longer, waiting for 5-10 min. isn't a good user experience.

So I want to return to the page immediately notifying the user that his/her request is being processed, while he can still browse, and do other stuff in the application, he/she can then go back to the page and see that his/her request was processed.

I've looked into Yii extensions backjob, It works, the redirect is executed immediately (somehow a background request), but when doing other things, like navigating in the site, it doesn't load, and it seems that the request is still there, and i cannot continue using the application until the request is finished.

A similar extension runactions promises the same thing, but I could not even get it to work, it says it 'touches a url', like a fire and forget job but doesn't work.

I've also tried to look into message queuing services like Gearman, RabbitMQ, but is really highly technical, I couldn't even install Gearman in my windows machine so "farming" services won't work for me. Some answers to background processing includes CRON and AJAX but that doesn't sound too good, plus a lot of issues.

Is there any other workaround to having asynchronous background processing? I've really sought hard for this, and i'm really not looking for advanced/sophisticated solutions like "farming out work to several machines" and the likes. Thank You very much!

  • 写回答

1条回答 默认 最新

  • drbi19093 2014-10-10 15:56
    关注

    If you want to be able to run asynchronous jobs via Yii, you may not have a choice but to dabble with some AJAX in order to retrieve the status of the job asynchronously. Here are high-level guidelines that worked for me. Hopefully this will assist you in some way!


    Setting up a console action

    To run background jobs, you will need to use Yii's console component. Under /protected/commands, create a copy of your web controller that has your actionRequest() (e.g. /protected/commands/BulkCommand.php).

    This should allow you to go in your /protected folder and run yiic bulk request.

    Keep in mind that if you have not created a console application before, you will need to set up its configuration similar to how you've done it for the web application. A straight copy of /protected/config/main.php into /protected/config/console.php should do 90% of the job.


    Customizing an extension for running asynchronous console jobs

    What has worked for me is using a combination of two extensions: CConsole and TConsoleRunner. TConsoleRunner uses popen to run shell scripts, which worked for me on Windows and Ubuntu. I simply merged its run() code into CConsole as follows:

    public function popen($shell, $redirectOutput = '')
    {
        $shell = $this->resolveCommandLine($shell, false, $redirectOutput);
        $ret = self::RETURN_CODE_SUCCESS;
        if (!$this->displayCommands) {
            ob_start();
        }
    
        if ($this->isWindows()) {
            pclose(popen('start /b '.$shell, 'r'));
        }
        else {
            pclose(popen($shell.' > /dev/null &', 'r'));
        }
    
        if (!$this->displayCommands) {
            ob_end_clean();
        }
        return $ret;
    }
    
    protected function isWindows()
    {
        if(PHP_OS == 'WINNT' || PHP_OS == 'WIN32')
            return true;
        else 
            return false;
    }
    

    Afterwards, I changed CConsole's runCommand() to the following:

    public function runCommand($command, $args, $async = false, &$outputLines = null, $executor = 'popen')
        {
            ...
            switch ($executor) {
                ...
                case 'popen':
                    return $this->popen($shell);
                ...
            }
        }
    


    Running the asynchronous job

    With the above set up, you can now use the following snippet of code to call yiic bulk request we created earlier.

    $console = new CConsole();
    $console->runCommand('bulk request', array(
        '--arg1="argument"',
        '--arg2="argument"',
        '--arg3="argument"',
    ));
    

    You would insert this in your original actionRequest().


    Checking up on the status

    Unfortunately, I'm not sure what kind of work your bulk request is doing. For myself, I was gathering a whole bunch of files and putting them in a folder. I knew going in how many files I expected, so I could easily create a controller action that verifies how many files have been created so far and give a % of the status as a simple division.

    评论

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘