php - Laravel REST API 和高 CPU 负载

标签 php mysql json laravel-4 cpu-usage

我正在使用 Laravel 4 开发一个简单的 RESTful API。 我设置了一个 Route 来调用我的 Controller 的函数,该函数基本上是这样做的:

  • 如果信息在数据库中,则将其打包到 JSON 对象中并返回响应
  • 否则尝试下载它(html/xml 解析),存储它,最后打包 JSON 响应并发送它。

我注意到,在总共执行 1700 个请求(一次只有 2 个请求)时,CPU 负载上升到 70-90%。

我是一个完整的 php 和 laravel 初学者,我已经按照 this tutorial 创建了 API ,也许我可能做错了什么,或者这只是缺乏优化的概念证明。如何改进此代码? (启动函数是getGames) 你认为所有问题的根源是 Laravel 还是我应该得到相同的结果,即使改变框架/使用原始 PHP?

UPDATE1我也设置了一个文件缓存,但是CPU负载还是~50%。

UPDATE2 我将查询速率设置为每 500 毫秒两次,CPU 负载降低了 12%,所以我猜这段代码缺少队列处理或类似的东西。

class GameController extends BaseController{
    private static $platforms=array(
        "Atari 2600",
        "Commodore 64",
        "Sega Dreamcast",
        "Sega Game Gear",
        "Nintendo Game Boy",
        "Nintendo Game Boy Color",
        "Nintendo Game Boy Advance",
        "Atari Lynx",
        "M.A.M.E.",
        "Sega Mega Drive",
        "Colecovision",
        "Nintendo 64",
        "Nintendo DS",
        "Nintendo Entertainment System (NES)",
        "Neo Geo Pocket",
        "Turbografx 16",
        "Sony PSP",
        "Sony PlayStation",
        "Sega Master System",
        "Super Nintendo (SNES)",
        "Nintendo Virtualboy",
        "Wonderswan");
    private function getDataTGDB($name,$platform){
        $url = 'http://thegamesdb.net/api/GetGame.php?';
        if(null==$name || null==$platform) return NULL;
        $url.='name='.urlencode($name);
        $xml = simplexml_load_file($url);
        $data=new Data;
        $data->query=$name;
        $resultPlatform = (string)$xml->Game->Platform;

        $data->platform=$platform;
        $data->save();
        foreach($xml->Game as $entry){
            $games = Game::where('gameid',(string)$entry->id)->get();
            if($games->count()==0){
                if(strcasecmp($platform , $entry->Platform)==0 || 
                (strcasecmp($platform ,"Sega Mega Drive")==0 && 
                ($entry->Platform=="Sega Genesis" || 
                $entry->Platform=="Sega 32X" || 
                $entry->Platform=="Sega CD"))){
                    $game = new Game;
                    $game->gameid = (string)$entry->id;
                    $game->title = (string)$entry->GameTitle;
                    $game->releasedate = (string)$entry->ReleaseDate;
                    $genres='';
                    if(NULL!=$entry->Genres->genre)
                    foreach($entry->Genres->genre as $genre){
                        $genres.=$genre.',';
                    }
                    $game->genres=$genres;
                    unset($genres);
                    $game->description = (string)$entry->Overview;
                    foreach($entry->Images->boxart as $boxart){
                        if($boxart["side"]=="front"){
                            $game->bigcoverurl = (string)$boxart;
                            $game->coverurl = (string) $boxart["thumb"];
                        } continue;
                    }
                    $game->save();
                    $data->games()->attach($game->id);
                } 
            }
            else foreach($games as $game){
                $data->games()->attach($game->id);
            }
        }
        unset($xml);
        unset($url);
        return $this->printJsonArray($data);
    }

    private function getArcadeHits($name){
        $url = "http://www.arcadehits.net/index.php?p=roms&jeu=";
        $url .=urlencode($name);

        $html = file_get_html($url);

        $data = new Data;
        $data->query=$name;
        $data->platform='M.A.M.E.';
        $data->save();
        $games = Game::where('title',$name)->get();
        if($games->count()==0){
            $game=new Game;
            $game->gameid = -1;
            $title = $html->find('h4',0)->plaintext;
            if("Derniers jeux commentés"==$title)
            { 
                unset($game);
                return Response::json(array('status'=>'404'),200);
            }
            else{
                $game->title=$title;
                $game->description="(No description.)";
                $game->releasedate=$html->find('a[href*=yearz]',0)->plaintext;
                $game->genres = $html->find('a[href*=genre]',0)->plaintext;
                $minithumb = $html->find('img.minithumb',0);
                $game->coverurl = $minithumb->src;
                $game->bigcoverurl = str_replace("/thumb/","/jpeg/",$minithumb->src);
                $game->save();
                $data->games()->attach($game->id);
            }
        }

        unset($html);
        unset($url);
        return $this->printJsonArray($data);
    }

    private function printJsonArray($data){
        $games = $data->games()->get();
        $array_games = array();
        foreach($games as $game){
            $array_games[]=array(
                'GameTitle'=>$game->title,
                'ReleaseDate'=>$game->releasedate,
                'Genres'=>$game->genres,
                'Overview'=>$game->description,
                'CoverURL'=>$game->coverurl,
                'BigCoverURL'=>$game->bigcoverurl
            );
        }
        $result = Response::json(array(
            'status'=>'200',
            'Game'=>$array_games
            ),200);
        $key = $data->query.$data->platform;
        if(!Cache::has($key))
            Cache::put($key,$result,1440);
        return $result;
    }

    private static $baseImgUrl = "";
    public function getGames($apikey,$title,$platform){
            $key = $title.$platform;
            if(Cache::has($key)) return Cache::get($key);
        if(!in_array($platform,GameController::$platforms)) return Response::json(array("status"=>"403","message"=>"non valid platform"));
        $datas = Data::where('query',$title)
                ->where('platform',$platform)
                ->get();
        //If this query has already been done we return data,otherwise according to $platform
        //we call the proper parser.
        if($datas->count()==0){
            if("M.A.M.E."==$platform){
                return $this->getArcadeHits($title);
            }
            else{
                return $this->getDataTGDB($title,$platform);
            }
        } else{
            else return $this->printJsonArray($datas->first());
        }
    }


}
?>

最佳答案

您正在尝试从其他人的服务器检索数据。那就是让你的 CPU “暂停”,直到数据被完全检索。这就是使您的代码如此“CPU 昂贵”的原因 (找不到适合这里的其他东西 =/),因为您的脚本正在等待直到收到数据然后释放脚本(CPU)工作。

强烈建议您进行异步调用。这将释放您的 CPU 来处理代码,而您系统的其他部分正在获取您需要的信息。

希望对您有所帮助! =D

更新
为了举例说明,我必须重构您的代码(而且我很懒惰!)。但是,我可以肯定地告诉你:如果你把你的请求代码,那些调用其他站点的 XML 的代码,放到一个队列中,你将获得大量的空闲 CPU 时间。每个请求都被重定向到一个队列。一旦他们准备好了,你就可以随心所欲地对待他们。 Laravel 有一种处理队列的漂亮方法。

关于php - Laravel REST API 和高 CPU 负载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21816682/

相关文章:

php - 正则表达式 (preg_match)

php - 如何将 HTML 转换为 BBCode

mysql - 有没有办法删除mysql中列数据末尾的空格

mysql - 使用 JSON 值左连接表 ON 行?

javascript - 尝试让 package.json knex 命令起作用

php - 注意:尝试获取非对象错误的属性

php - CodeIgniter 中的图像处理

php - 将条件多次插入到 1 个表中

python - Python读取多个同名但不同扩展名的文件

python - 如何使用python过滤文件中的json输出?