我需要在数据库中更新~5-6k $items
。每个项目都需要HTTP请求才能从页面获取数据。在HTTP GET请求中,我获得了大量的数组(~500-2500),我只需要插入那些不在数据库中的行。在我的流浪苏格兰威士忌盒上,我目前的剧本(每2-4分钟一件)需要花费很多时间。
简化示例:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use GuzzleHttp\Client;
use App\Item;
use App\ItemHistory;
use Carbon\Carbon;
use DB;
class UpdateController extends Controller
{
public function getStart() {
// Don't cancel the script
ignore_user_abort(true);
set_time_limit(0);
$client = new Client();
$items = Item::where('updated_at', '<=', Carbon::now()->subDay())->get();
foreach($items as $item) {
$response = $client->request('GET', 'API_URL');
// get the body
$body = $response->getBody()->getContents();
$hugeArray = $body['history']; // can be from 100 to 5 000 lines and I use regex to get the "history" array from the body
$arrayCollection = collect($hugeArray);
foreach($arrayCollection->take(-100) as $row) { // I take the last 100 since each row = 1 hour, so I get items in the last 100 hours
$date = new \DateTime($row['created_at']);
if( ! ItemHistory::whereItemId($item->id)->whereSoldAt($date)->count()) { // Checking if it already exists
// I insert the new rows..
$history = new ItemHistory;
// ....
$history->save();
}
}
}
}
}
我实际上抓取数据并使用正则表达式来查找正文响应中的数组。
难道我做错了什么?它需要很长时间,直到它移动到下一个$item
。
答案 0 :(得分:1)
我可以提供简化的答案 - 同步执行,对象水合和批量数据库查询。
考虑以下示例:
$requests = function () use ($items) {
foreach ($items as $item) {
yield new GuzzleHttp\Psr7\Request($method, $uri);
}
};
$client = new GuzzleHttp\Client();
foreach ($requests() as $request) {
$client->sendAsync($request)
->then(
function(Psr7\Http\Message\ResponseInterface) {
// process the response into array;
return $arrayFromResponse;
})
->then(
function ($unfilteredArray) {
// filter the array as necessary
return $filteredArray;
})
->then(
function($filteredArray) {
// create the array for bulk insert / update
return $sqlArray;
})
->then(
function($sqlArray) {
// perform bulk db operations.
}
);
}
同步Http查询 - 上面的示例突出显示了Guzzle的一些异步功能,同时突破了处理步骤。您上面链接的代码是同步的。执行请求,等待响应,处理响应,rince&amp;重复。异步Http请求将确保在处理其他信息时下载数据。我应该注意,您的结果会有所不同,具体取决于您的特定用例,可能会增加资源使用量。
对象水合 - 也就是当你执行查询时你的ORM正在做什么,它返回一个对象实例(而不是一个数组),是耗时和内存密集的。 @orcamius(Doctrine的开发人员之一)在subject上撰写了一篇相当技术性的文章。虽然这不是Eloquent特定的,但它确实提供了对所有ORM的幕后操作的深入了解。代码段会执行其中的许多操作(参考$itemHistory
,$history
,Item::where
)。
批量数据库操作 - 众所周知的事实是数据库操作很慢。当与物体水合相结合时,这个时间进一步增加。使用1000x记录和1000x插入执行单个插入更好。为此,必须将代码从使用ORM修改为直接使用DB表。如docs
DB::table('itemHistory')->insert($arrayOfValues)
执行批量插入
醇>
更新:虽然未显示,但then()
的方法签名为then(callable $fulfilled, callable $onError)
。如果出现问题,您可以执行类似
// promise returned from a request
$p->then(
function (Psr\Http\Message\ResponseInterface $response) use ($p)
if ($response->getResponseCode() >= 400) {
$p->cancel();
}
//perform processing
return $someArray;
},
function (RequestException $e) {
echo $e->getMessage() . "\n";
echo $e->getRequest()->getMethod();
})
->then(
function($someArray) use ($p) {
// filter or other processing
});
有关Guzzle's Promises的更多信息可以在Github Repo
中找到