API LARAL REST и высокая загрузка процессора

Я разрабатываю простой RESTful API, используя Laravel 4. Я установил Route, который вызывает функцию моего Controller, которая в основном делает это:

  • Если информация находится в базе данных, упакуйте ее в объект JSON и верните ответ
  • Попробуйте загрузить его (html/xml parsing), сохраните его и, наконец, упакуйте ответ JSON и отправьте его.

Я заметил, что загрузка ЦП при выполнении в общей сложности 1700 запросов, всего по 2 за один раз, поднимается до 70-90%.

Я полный новичок php и laravel, и я сделал API после этот учебник, возможно, я, вероятно, что-то делаю неправильно или это просто доказательство концепции отсутствия оптимизаций. Как я могу улучшить этот код? (начальная функция getGames) Считаете ли вы, что корнем всех проблем является Laravel, или я должен получить тот же результат, даже изменив структуру/используя raw PHP?

UPDATE1 Я также установил кэш файлов, но загрузка процессора по-прежнему составляет ~ 50%.

UPDATE2. Я установил скорость запроса на два каждые 500 мс, а загрузка ЦП снизилась на 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());
        }
    }


}
?>

Ответ 1

Вы пытаетесь получить данные с других серверов. Это приводит к удержанию вашего процессора в режиме ожидания, пока данные не будут полностью восстановлены. То, что делает ваш код настолько "дорогостоящим процессором" (не удалось найти другой материал, который подходит здесь =/), заставьте ваш script ждать, пока данные не будут получены, а затем отпустите script (CPU).

Я сильно предлагаю вам делать асинхронные вызовы. Это освободит ваш процессор для работы с кодом, в то время как другая часть вашей системы получит необходимую вам информацию.

Надеюсь, это поможет!= D

UPDATE
Чтобы привести примеры, мне пришлось бы перефакторировать ваш код (и я ленивый, как ничто!). Но, я могу сказать вам точно: если вы поместите свой код запроса, который совершает вызовы на другие сайты XML, в очередь вы получите много свободного времени процессора. Каждый запрос перенаправляется для очереди. Как только они будут готовы, вы относитесь к ним по своему усмотрению. Laravel имеет прекрасный способ для работы с очередями.

Ответ 2

то, что я хотел бы сделать первым, - использовать профилировщик, чтобы выяснить, какие части нуждаются в оптимизации. Вы можете использовать, например, следующее:

http://xdebug.org/docs/profiler

Также вы не указали, что это за процессор, сколько ядер вы используете? Это проблема, связанная с тем, что ваш процессор сильно используется?

Ответ 3

вам следует использовать систему Laravel Queue вместе с beanstalkd, а затем следить за очередью (рабочим) с помощью ремесленной очереди: listen