Время загрузки файла Laravel истекло - добавление чанка в updateOrCreate?
Я пытаюсь загрузить CSV-файл пользователя и передать данные в базу данных, но из-за размера строки / CSV время ожидания истекает. Данные должны быть проверены, чтобы увидеть, есть ли они уже в базе данных и обновить или создать.
Я применил блок в CSV для чтения данных, но не знал, возможно ли добавить блок в раздел загрузки в базу данных?
Вот моя функция
public function import(Request $request) {
if($request->file('imported-file')) {
$path = $request->file('imported-file')->getRealPath();
$data = Excel::filter('chunk')->load($path)->chunk(200, function($results) {
foreach($results as $row) {
if(!empty($row['postcode'])) {
$url = "https://maps.googleapis.com/maps/api/geocode/xml?address=".urlencode($row['postcode'])."®ion=uk&key=";
$tmp = file_get_contents($url);
$xml = simplexml_load_string($tmp);
if((string)$xml->status == 'OK' && isset($xml->result[0])) {
$lat = 0;
$lng = 0;
if(isset($xml->result[0]->geometry->location->lat)) {
$lat = (string)$xml->result[0]->geometry->location->lat;
}
if(isset($xml->result[0]->geometry->location->lng)) {
$lng = (string)$xml->result[0]->geometry->location->lng;
}
}
Import::updateOrCreate(
[
'sitecode' => $row['sitecode']
],
[
'sitecode' => $row['sitecode'],
'sitename' => $row['sitename'],
'address_1' => $row['address_1'],
'address_2' => $row['address_2'],
'address_town' => $row['address_town'],
'address_postcode' => $row['postcode'],
'charity' => $row['charity'],
'latitude' => $lat,
'longitude' => $lng,
'approved' => 1
]
);
} else {
// Postcode not valid!!!
}
} // endforeach
Session::flash('sucess', 'Import was sucessful.');
return redirect()->route('locations');
});
} else {
Session::flash('error', 'Please select a file to upload!');
return back();
}
}
1 ответ
Ваша проблема связана с конфигурацией вашего сервера, и вы должны понимать, что многие вещи могут пойти не так, когда вы выполняете долгосрочные задачи в режиме реального времени.
Если вы используете установку Nginx/PHP-FPM, вы должны посмотреть файл конфигурации Nginx, PHP и PHP-FPM.
Конфигурация PHP
Давайте начнем с PHP. Открой /etc/php/<phpversion>/fpm/php.ini
файл и поиск max_execution_time
, Вы должны найти что-то вроде
max_execution_time = 30
Это означает, что каждый запрос может длиться не более 30 секунд. Если вам нужно больше времени, увеличьте это число, например
max_execution_time = 300
на 5 минут.
Тогда давайте рассмотрим конфигурацию PHP-FPM. Откройте конфигурацию вашего пула, такую как /etc/php/<phpversion>/fpm/pool.d/www.conf
и искать request_terminate_timeout
, В моей конфигурации он отключен:
; Default Value: 0
;request_terminate_timeout = 0
Значение по умолчанию - 0 (отключено), но если оно включено, вы должны увеличить число, например
request_terminate_timeout = 400
за 400 секунд до того, как PHP-FPM убьет дочерний процесс. Если вы даете число, используйте что-то выше, чем max_execution_time
в противном случае ваш процесс будет остановлен PHP-FPM, игнорируя максимальное время выполнения.
Конфигурация Nginx
Наконец, посмотрите на конфигурацию Nginx в /etc/nginx/sites-available/yoursite.conf
, Там вы должны найти раздел, который настраивает связь между Nginx и PHP-FPM. Там вы найдете fastcgi_read_timeout
Это максимальное время, которое Nginx будет ждать, пока PHP-FPM вернет некоторые данные:
location ~ \.php$ {
# ...
fastcgi_read_timeout 300;
# ...
}
Если через 300 секунд PHP-FPM ничего не вернул, Nginx прервет соединение. В вашем случае вы отправляете данные обратно на веб-сервер после длительного процесса, поэтому это может занять не более 300 секунд. Вы должны изменить это число на что-то совместимое с числами, которые вы указали в конфигурациях PHP.
Подводить итоги
Если вы считаете, что ваша обработка может занять до 30 минут, используйте числа, подобные этим:
в
/etc/php/<phpversion>/fpm/php.ini
:max_execution_time = 1800 ; 1800 secs = 30 minutes
в
/etc/php/<phpversion>/fpm/pool.d/www.conf
:request_terminate_timeout = 0 ; no timeout, or greater than 1800
в
/etc/nginx/sites-available/yoursite.conf
:fastcgi_read_timeout = 2000; # 2000 secs, or at least greater than the previous twos
С этой комбинацией max_execution_time
будет править среди других, и вы будете знать, что у вашего процесса есть 30 минут рабочего времени, потому что тайм-ауты PHP-FPM и Nginx должны произойти после PHP.
Не забывайте на стороне клиента
Если вы используете загружаемую библиотеку AJAX, пожалуйста, проверьте ее конфигурацию, потому что это может наложить другой тайм-аут на полный запрос загрузки AJAX.
Например, dropzonejs по умолчанию использует тайм-аут 30 секунд. Ваш сервер может работать целую вечность, но по истечении этого короткого промежутка времени ваша библиотека javascript прервет соединение.
Обычно вы можете изменить это значение. С помощью dropzone вы можете установить время ожидания 2100 секунд с помощью
var myDropzone = new Dropzone("#uploader", {
// ...
timeout: "2100",
// ...
});
Опять же, используйте более высокое значение, чем значение в Nginx.
Долгосрочные задачи: правильный путь
Тем не менее, ваш подход быстрый и грязный, и даже если я уверен, что это нормально для вашего случая, было бы лучше пойти по другому пути:
- не выполняйте обработку CSV сразу после загрузки
- вместо этого, загрузите файл, поместите его в очередь и уведомите клиента, чтобы проверить позже
- выполнить обработку CSV в фоновом режиме с работником очереди
Пожалуйста, проверьте документацию Laravel об очередях ( https://laravel.com/docs/5.5/queues).
При таком подходе ваш пользователь получит немедленную обратную связь, и у вас больше не будет проблем с тайм-аутом! (Ну, теоретически: фоновые задания из очередей тоже могут быть отсчитаны по времени, но это уже другая история.)
Я надеюсь, что это может помочь:)