我一直在为游戏创建会员列表,并从其得分中获取一些数据。首先,我得到一个名称列表,然后将其插入数据库,然后将其提供给cURL以从hiscores获取统计信息,然后将其更新至数据库。
问题似乎是当我发出cURL请求时,在主机显示503错误之前(我可能是由于执行时间最长),我设法总共更新了约30个名称。但是,我必须能够进行更多更新。我会说最低为100。
我已尝试优化代码,以使其运行更快并取得一些成功。似乎有30个人是我一次查询中最多可以更新的人。
代码本身有什么问题,为什么要花这么长时间?以下是代码的cURL部分,它可能不是您所见过的最漂亮的部分。我认为cURL能够一次处理更多数据,并且在没有数据库正常工作的情况下,我也有类似的解决方案。原因可能是https吗?以前不需要,但现在需要。
<?php
$ch = curl_init();
if(isset($_POST['submit'])){ //check if form was submitted
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//get users
$stmt = $conn->prepare("SELECT m.name, m.id, m.group_id, p.field_1, g.prefix, g.suffix FROM members m INNER JOIN pfields_content p ON m.id = p.id INNER JOIN groups g ON g.g_id = m.group_id WHERE
m.group_id = 1
");
$stmt->execute();
$result = $stmt->get_result();
while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
// add new member ID to database
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$stmt = $conn->prepare("INSERT IGNORE INTO `table` (`member_id`, `name`, `dname`) VALUES ('".$row['member_id']."', '".$row['name']."', '".$row['field_1']."')");
$stmt->execute();
// dname
if($row['field_1'] != '' || $row['field_1'] != NULL) {
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_URL, "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=".$row['field_1']);
curl_setopt($ch, CURLOPT_HEADER, 0);
// grab HTML
$data = curl_exec($ch);
$array = array();
$array = explode(',', $data);
//formula
if (!empty($array[15]) && (is_numeric($array[15]))) {
$level = ((round($array[13]/2, 0, PHP_ROUND_HALF_DOWN)+$array[9]+$array[7])/4) + (($array[3]+$array[5])*0.325);
$level = number_format($level, 2);
// if valid name, update
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$stmt = $conn->prepare("UPDATE table SET
member_id = '".$row['id']."',
name = '".$row['name']."',
cb = '".$level."' WHERE member_id = ".$row['id']."");
$stmt->execute();
$conn->close();
}}}}
答案 0 :(得分:0)
好的,有几件事值得一提:
1)为什么您只能做那么多?这是最可能的罪魁祸首:
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_URL, "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=".$row['field_1']);
curl_setopt($ch, CURLOPT_HEADER, 0);
// grab HTML
$data = curl_exec($ch);
您正在为每个呼叫进行外部curl呼叫,这意味着您受其他站点的支配,并且需要花费多长时间来解决该呼叫。您可以在curl调用周围添加一些回声,以查看每个调用进行了多少时间。但是,可悲的是,由于您依赖于外部过程,因此您可能无法从代码中获得更多的速度。这可能是因为https,或者仅仅是他们的系统过载。就像我在上面说过的,如果您真的想知道每个对象需要花费多长时间,请在其周围添加一些回声,例如:
echo "About to curl runescape " . date("H:i:s");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)");
curl_setopt($ch, CURLOPT_URL, "https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws?player=".$row['field_1']);
curl_setopt($ch, CURLOPT_HEADER, 0);
// grab HTML
$data = curl_exec($ch);
echo "Done with call to runescape " . date("H:i:s");
其余的代码似乎在速度上不是问题。但是:
2)您的连接有点混乱。您打开一个连接,并进行查询。然后一会儿开始,您打开第二个连接并进行查询。然后,如果满足正确的条件,则打开第三个连接并进行一些工作,然后将其关闭。原始的2个连接永远不会关闭,而第二个连接实际上在您的循环中被打开了多次。为什么不重用原始的$ conn而不是每次都打开一个新连接?
3)最后,如果您的php文件需要运行60秒以上,请在顶部添加以下内容:
set_time_limit(0);
以上内容应该有效地让脚本运行所需的时间。但是,与在浏览器上长时间运行的脚本相比,在CLI上作为cronjob进行运行要好得多,就像上面的代码一样。
答案 1 :(得分:0)
其他人似乎做得还不错,弄清楚为什么代码这么慢(您正在执行一堆cURL请求,每个请求都花时间),以及代码的其他一些问题(您的缩进很混乱)向上;对不起,我没有更深入地挖掘。)
答案在某种程度上取决于您的需求:您是否需要将处理后的数据发送回原始请求者,还是将其保存到数据库中?
执行数据库查询以及执行所有
同时执行所有cURL请求 。 我实际上并不认为这可以在PHP中完成(请参见下面的curl_multi)。在其他一些语言中,这很容易。最强力的方法是为每个cURL请求分离一个异步系统进程,并将PHP置于睡眠/检查循环中,直到看到所有子进程均已将其结果写入数据库为止。
当您开始使用异步工具时,您将遇到更多的麻烦,而且还不清楚您是否正在以最佳方式解决问题。就是说,如果您走这条路,我认为您需要的第一个功能是exec。例如,这将产生一个独立的异步进程,该进程将永远向虚空大喊(实际上不这样做):
exec('yes > /dev/null &')
最后,我自己的议程:对您来说,这是将您的某些执行移出PHP的绝佳机会!尽管您可能只需要使用curl_multi就能获得所需的一切,甚至还有bypassing cURL and building your own HTTP requests的某些选项,但我建议使用更适合手头任务的工具。
答案 2 :(得分:0)
我研究了您的代码,并试图以一种更好地利用数据库连接和curl请求的方式对其进行重组。由于curl请求的目标URL是通过HTTPS进行的,因此我修改了curl选项以包括证书信息以及可能需要或可能不需要的其他一些修改-我无法完全测试此代码,因此可能会出错!
prepared statement
,因为它不使用任何用户提供的数据,因此很安全。prepared statements
时,只需创建一次(这样就不会循环),并且将占位符绑定到变量(如果语句创建成功)。在那个阶段,变量实际上不需要存在(至少使用mysqli
时-PDO中有所不同)prepared statements
,请先在sql中嵌入变量(在这种情况下不是用户输入),否则不要破坏数据库-使用占位符作为参数!我希望以下内容能对您有所帮助...我能够在5秒内使用随机名称而不使用任何数据库调用来进行测试〜6个用户
<?php
try{
$start=time();
$cacert='c:/wwwroot/cacert.pem'; # <-------edit as appropriate
$baseurl='https://secure.runescape.com/m=hiscore_oldschool/index_lite.ws';
if( isset( $_POST['submit'], $servername, $username, $password, $dbname ) ){
/* should only need the one curl connection */
$curl=curl_init();
curl_setopt( $curl, CURLOPT_RETURNTRANSFER, true );
curl_setopt( $curl, CURLOPT_BINARYTRANSFER, true );
curl_setopt( $curl, CURLOPT_FOLLOWLOCATION, true );
curl_setopt( $curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)" );
curl_setopt( $curl, CURLOPT_HEADER, false );
curl_setopt( $curl, CURLINFO_HEADER_OUT, false );
curl_setopt( $curl, CURLOPT_SSL_VERIFYPEER, true );
curl_setopt( $curl, CURLOPT_SSL_VERIFYHOST, 2 );
curl_setopt( $curl, CURLOPT_CAINFO, $cacert );
curl_setopt( $curl, CURLOPT_MAXREDIRS, 10 );
curl_setopt( $curl, CURLOPT_ENCODING, '' );
/* only need the one db connection */
$conn = new mysqli( $servername, $username, $password, $dbname );
/* initial db query does not need to be a prepared statement as there are no user supplied parameters */
$sql='select m.`name`, m.`id`, m.`group_id`, p.`field_1`, g.`prefix`, g.`suffix`
from members m
inner join pfields_content p on m.`id` = p.`id`
inner join groups g on g.`g_id` = m.`group_id`
where m.`group_id` = 1';
$res=$conn->query( $sql );
if( $res ){
/* create the prepared statement for inserts ONCE, outside the loop */
$sql='insert ignore into `table` ( `member_id`, `name`, `dname` ) values ( ?,?,? )';
$stmt=$conn->prepare( $sql );
if( $stmt ){
/* bind the placeholders to variables - the variables do not need to exist YET in mysqli */
$stmt->bind_param('iss', $id, $name, $field_1 );
/* placeholder arrays for bits of the recordset */
$data=array();
$urls=array();
/*
collect all the relevant player names into an array
and store info for use in INSERT query
*/
while( $rs=$res->fetch_object() ){
if( !empty( $rs->field_1 ) ) {
$urls[ $rs->field_1 ]=(object)array(
'name' => $rs->name,
'id' => $rs->id
);
}
$data[]=array(
'name' => $rs->name,
'id' => $rs->id, /* original code references `member_id` which does not exist in the recordset */
'field_1' => $rs->field_1
);
}
/* now loop through $data to do the inserts */
foreach( $data as $obj ){
/* create/dimension the variables for the prepared statement parameters */
$name=$obj->name;
$id=$obj->id;
$field_1=$obj->field_1;
/* run the insert cmd */
$stmt->execute();
}
/* we should now be finished with the initial prepared statement */
$stmt->free_result();
$stmt->close();
/*
now for the curl calls... no idea how many there will be but this should be known
by sizeof( $urls )
Dependant upon the number you might opt to perform the curl calls in chunks or use
`curl_multi_init` ~ more complicated but perhaps could help.
Also need to define a new sql statement ~ which sort of does not make sense as it was
~ do not need to update the `member_id`!
*/
$sql='update `table` set `name`=?, `cb`=? where `member_id`=?';
$stmt=$conn->prepare( $sql );
if( $stmt ){
$stmt->bind_param( 'ssi', $name, $level, $id );
foreach( $urls as $player => $obj ){
$url = $baseurl . '?player=' . $player;
/* set the url for curl */
curl_setopt( $curl, CURLOPT_URL, $url );
/* execute the curl request... */
$results=curl_exec( $curl );
$info=(object)curl_getinfo( $curl );
$errors=curl_error( $curl );
if( $info->http_code==200 ){
/* curl request was successful */
$array=explode( ',', $results );
if( !empty( $array[15] ) && is_numeric( $array[15] ) ) {
$level = ((round($array[13]/2, 0, PHP_ROUND_HALF_DOWN)+$array[9]+$array[7])/4) + (($array[3]+$array[5])*0.325);
$level = number_format($level, 2);
/* update db ~ use $obj from urls array + level defined above */
$name=$obj->name;
$id=$obj->id;
$stmt->execute();
}
} else {
throw new Exception( sprintf('curl request to %s failed with status %s', $url, $info->http_code ) );
}
}// end loop
$stmt->free_result();
$stmt->close();
curl_close( $curl );
printf( 'Finished...Operation took %ss',( time() - $start ) );
}else{
throw new Exception( 'Failed to prepare sql statement for UPDATE' );
}
}else{
throw new Exception( 'Failed to prepare sql statement for INSERT' );
}
}else{
throw new Exception( 'Initial query returned no results' );
}
}
}catch( Exception $e ){
exit( $e->getMessage() );
}
?>