Время загрузки файла 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'])."&region=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.

Долгосрочные задачи: правильный путь

Тем не менее, ваш подход быстрый и грязный, и даже если я уверен, что это нормально для вашего случая, было бы лучше пойти по другому пути:

  1. не выполняйте обработку CSV сразу после загрузки
  2. вместо этого, загрузите файл, поместите его в очередь и уведомите клиента, чтобы проверить позже
  3. выполнить обработку CSV в фоновом режиме с работником очереди

Пожалуйста, проверьте документацию Laravel об очередях ( https://laravel.com/docs/5.5/queues).

При таком подходе ваш пользователь получит немедленную обратную связь, и у вас больше не будет проблем с тайм-аутом! (Ну, теоретически: фоновые задания из очередей тоже могут быть отсчитаны по времени, но это уже другая история.)

Я надеюсь, что это может помочь:)

Другие вопросы по тегам