Laravel REST API和高CPU负载

时间:2014-02-16 21:11:02

标签: php mysql json laravel-4 cpu-usage

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

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

我注意到CPU总共执行了1700个请求,一次只有2个,一共增加到70-90%。

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

UPDATE1 我还设置了文件Cache,但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());
        }
    }


}
?>

3 个答案:

答案 0 :(得分:3)

您正在尝试从其他服务器检索数据。这就是让你的CPU“保持”状态,直到完全检索到数据。这就是让您的代码变得如此“昂贵” (无法找到适合此处的其他东西= /) 的原因,导致您的脚本等待直到收到数据然后释放脚本(CPU)工作。

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

我希望这会有所帮助! = d

<强>更新
举个例子,我不得不重新考虑你的代码(而且我很懒惰!)。但是,我可以肯定地告诉你:如果你把你的请求代码,那些调用其他网站的XML的人放到队列中,你将获得大量的免费CPU时间。每个请求都被重定向到队列。一旦他们准备好了,你可以按照自己的意愿对待他们。 Laravel有一个处理队列的漂亮方式。

答案 1 :(得分:2)

我首先要做的是使用分析器来找出哪些部件需要优化。您可以使用例如:

http://xdebug.org/docs/profiler

你还没有指定它是什么类型的cpu,你使用了多少个核心?这是一个问题,你的CPU使用得那么高吗?

答案 2 :(得分:1)

你应该使用Laravel的Queue系统和beanstalkd,然后用工匠队列监视队列(worker):listen