Вопрос

I am developing a simple RESTful API using Laravel 4. I have set a Route that calls a function of my Controller that basically does this:

  • If information is in the database, pack it in a JSON object and return a response
  • Else try to download it (html/xml parsing), store it and finally pack the JSON response and send it.

I have noticed that the CPU load while doing a total of 1700 requests, only 2 at a time together, raises to 70-90%.

I am a complete php and laravel beginner and I've made the API following this tutorial, maybe I'm probably doing something wrong or it's just a proof of concept lacking of optimzations. How can I improve this code? (starting function is getGames) Do you think the root of all problems is Laravel or I should obtain the same result even changing framework/using raw PHP?

UPDATE1 I also set a file Cache, but the CPU load is still ~50%.

UPDATE2 I set the query rate at two each 500ms and the CPU load lowered at 12%, so I guess this code is missing queue handling or something like this.

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


}
?>
Это было полезно?

Решение

You're trying to retrieve data from others' servers. That is putting your CPU "on hold" until the data is fully retrieved. That's what is making your code be so "CPU expensive" (couldn't find other stuff that fits here =/ ), cause your script is waiting until the data is received and then release the script (CPU) work.

I strongly suggest that you make asynchronous calls. That would release your CPU to work on the code, while other part of your system is getting the information you need.

I hope that'll be some help! =D

UPDATE
To make examples, I'd have to re-factor your code (and I'm lazy as anything!). But, I can tell you for sure: If you put your request code, those who make calls to others site's XML, onto a queue you would gain a lot of free CPU time. Every request are redirected for a queue. Once they're ready, you treat them as you wish. Laravel has a beautiful way for dealing with queues.

Другие советы

what I would do first is to use a profiler to find out which parts would need an optimization. You can use for example this:

http://xdebug.org/docs/profiler

As well you didn't specify what kind of cpu is it, how many cores are you using? Is this a problem that your cpu is getting used that high?

you should use Laravel's Queue system along with beanstalkd for example and then monitor the queue (worker) with artisan queue:listen

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top