【发布时间】:2014-03-16 00:24:46
【问题描述】:
我正在使用 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());
}
}
}
?>
【问题讨论】:
-
... 我使用 Linode 平台,曾经遇到同样的问题。只需重新启动机器(虚拟服务器机器),一切都恢复正常。我认为一些“幽灵代码”可能在我不知道的情况下在后台运行!你应该试一试(如果你可以的话)而且我也在制作一个 RESTful API!
-
@DennisBraga 不幸的是我无法重新启动,但是当我不使用 API 时,CPU 使用率为 0%,所以这是我的代码。我想我应该实现一些东西来防止请求泛滥或队列系统,这样只有 2-3 个请求一起完成,而其他客户端必须排队等待。
-
查看了您的代码。当您从其他服务器检索数据时,您正在拨打被“搁置”的电话。认为有你的问题。尝试解决这部分问题,我认为你会没事的。
-
P.S.:尝试阅读 Lavarel 代码指南。 codding 会有一些不错的提示。
-
@DennisBraga 问题是我应该设置可以同时处理的最大请求数,因为即使 API 返回所有缓存数据,CPU 负载也很高。我通过在每个请求之间添加 500 毫秒的暂停来测试它,并且 CPU 负载下降到 5-10%。解决方案是队列系统。例如,我可以创建一个“锁”并调用
usleep,直到锁再次打开。
标签: php mysql json laravel-4 cpu-usage