Laravel 文件上传超时 - 向 updateOrCreate 添加块?

Laravel file upload timing out - adding chunk to 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();

    }

}

您的问题与您的服务器配置有关,您必须明白,当您实时执行长时间 运行ning 任务时,很多事情都可能出错。

如果您使用的是 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 配置

最后看看/etc/nginx/sites-available/yoursite.conf中的Nginx配置。在那里你应该找到配置 Nginx 和 PHP-FPM 之间的通信的部分。在那里你找到 fastcgi_read_timeout,这是 Nginx 等待 PHP-FPM 到 return 一些数据的最长时间:

location ~ \.php$ {
    # ...
    fastcgi_read_timeout 300;
    # ...
}

如果 300 秒后 PHP-FPM 没有 returned 任何东西,Nginx 将终止连接。在您的情况下,您在 long-运行ning 过程后将数据发送回网络服务器,因此不会超过 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 分钟的 运行ning 时间,因为 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)。

通过这种方法,您的用户将立即得到反馈,您将不会再遇到超时问题! (好吧,理论上:来自队列的后台作业也可以超时,但那是另一回事了。)

希望对您有所帮助:)