laravel Export 导出数据,报504 Gateway Time-out

我查询了数据库里面就只有6000多条数据,我用的是web方式去请求下载的。
执行结果却是
image.png

这是我控制器方法:
image.png

这个是我的Export类:

<?php

namespace App\Exports;

use App\Helper\Utils;
use Maatwebsite\Excel\Concerns\Exportable;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithTitle;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithEvents;
use Maatwebsite\Excel\Events\AfterSheet;

class CopyRightExport implements WithHeadings, WithMapping, WithTitle, ShouldAutoSize, WithEvents, FromQuery
{
    use Exportable;

    protected $months = [];//2.0月份
    protected $months3 = [];//3.0月份

    public function __construct()
    {
        $startMonth = "2018-8";//2.0开始月份
        $startMonth3 = "2019-8";//3.0开始月份
        //获取当前月份
        $thisMonth = date("Y-m");
        $months = [$startMonth];
        $months3 = [$startMonth3];
        while (true) {
            if ($startMonth == $thisMonth) {
                break;
            }
            $nextMonth = Utils::getMonth(0, $startMonth);
            $months[] = $nextMonth;
            $startMonth = $nextMonth;
        }
        while (true) {
            if ($startMonth3 == $thisMonth) {
                break;
            }
            $nextMonth3 = Utils::getMonth(0, $startMonth3);
            $months3[] = $nextMonth3;
            $startMonth3 = $nextMonth3;
        }
        $this->months = $months;
        $this->months3 = $months3;
    }

    public function query()
    {
        // 查询版权方
        return Novel::where("platform_id", "!=", Novel::MY_PLATFORM)
            ->with(['platform'])
            ->select(['id', 'name', 'author', 'platform_id']);
    }


    public function map($novel): array
    {
        $money = [];
        foreach ($this->months as $month) {
            //起止时间
            $days = date('t', strtotime($month));
            //计算起始时间
            $startTime = $month . "-1 00:00:00";
            $endTime = $month . "-" . $days . " 23:59:59";

            $money[] = ChannelIncomeLog::where('platform_id', $novel->platform_id)
                ->where("novel_id", $novel->id)
                ->whereBetween('created_at', [$startTime, $endTime])
                ->sum("money");
        }
        
        foreach ($this->months3 as $month3){
            //起止时间
            $days = date('t', strtotime($month3));
            //计算起始时间
            $startTime3 = $month3 . "-1 00:00:00";
            $endTime3 = $month3 . "-" . $days . " 23:59:59";

            $money[] = ChannelIncomeXFLog::where('platform_id', $novel->platform_id)
                ->where("novel_id", $novel->id)
                ->whereBetween('created_at', [$startTime3, $endTime3])
                ->sum("money");
        }

        $novel->data = $money;
        $arr1 = [
            $novel->id,
            $novel->name,
            $novel->author,
            $novel->platform->name ?? "未知",
        ];
        return array_merge($arr1, $novel->data);
    }

    public function headings(): array
    {

        $startMonth = "2018-8";
        //获取当前月份
        $thisMonth = date("Y-m");
        $month = [];
        $month[] = $startMonth . " 收入(2.0)";
        while (true) {
            if ($startMonth == $thisMonth) {
                break;
            }
            $nextMonth = Utils::getMonth(0, $startMonth);
            $month[] = $nextMonth . " 收入(2.0)";
            $startMonth = $nextMonth;
        }
        foreach ($this->months3 as $month3){
            $month[] = $month3 . " 收入(3.0)";
        }
        $arr = [
            "书本id",
            "书本名称",
            "书本作者",
            "版权方"
        ];
        return array_merge($arr, $month);
    }

    public function title(): string
    {
        return 'novel';
    }

    public function sheets(): array
    {
        return [
            (new self()),
        ];
    }

    public function registerEvents(): array
    {
        return [
            AfterSheet::class => function (AfterSheet $event) {
                $event->sheet->getColumnDimension('A')->setAutoSize(false)->setWidth(15);
                $event->sheet->getColumnDimension('B')->setAutoSize(false)->setWidth(25);
                $event->sheet->getColumnDimension('C')->setAutoSize(false)->setWidth(25);
                $event->sheet->getColumnDimension('D')->setAutoSize(false)->setWidth(20);
            }
        ];
    }
}

虽然原数据只有6000多条,但是每条原数据会去查询数据库和原数据相关的数据,所以是不是这里导致的啊?

这个问题是什么原因造成的啊?
有什么优化方案吗?

阅读 600
评论 更新于 2019-10-31
    4 个回答
    pikalu
    • 256

    关于导出excel,结合自身经验,有以下一些优化的思路:

    1. SQL语句,避免慢查询,合理的建立索引,查询指定的字段,sql优化这块在此就不展开了
    2. 查询的结果集为大对象时转数组处理,框架中一般有方法可以转,如Laravel中有toArray(),Yii2中有asArray()
    3. 大数组进行数据切割处理,PHP函数有array_chunk()、array_slice()。
    4. 对于大型的字符串和对象,使用引用传递&。
    5. 用过的变量及时unset。
    6. 导出的文件格式由excel改为csv
    7. ini_set(‘memory_limit’,’’),设置程序可以使用的内存(不建议这样做)。
    8. 异步导出,比如异步脚本跑好数据,发送至邮箱

    关于这方面写过一遍博客,地址:https://segmentfault.com/a/11...
    博客原文地址:https://tsmliyun.github.io/ph...

    评论 赞赏 2019-11-06

      首先呢,一眼就看出来了。这个问题是Nginx报的504,也就是Nginx超时了。

      然后,为什么Nginx会超时呢,因为你的Web服务响应太慢了。

      那么解决方式呢?

      1. 优化SQL、代码,将处理时间缩短。当然,这是有限度的。
      2. 将Nginx的超时时间加长。当然这也是有限度的,总不能无限加长吧。
      3. 最好的办法,将这个东西做成异步的。
      如何做成异步的呢?

      将【导出】作为一个触发事件,并不一定会立即触发任务。
      可以把它想象成生成了一个【导出订单】,用户可以查看到该订单和它的的状态,比如【处理中(订单已创建,正在处理)】-->【已完成(订单处理完成,已经生成文件,可以下载了)】。

      后台专门跑个任务来处理未完成的订单,处理完成之后修改订单状态,并通知用户可以下载了。

      举例。比如阿里云电子发票下载

      clipboard.png

      评论 赞赏 2019-10-31
        贺兰
        • 3
        • 新人请关照

        如果不要求性能的话,把nginx的超时时间设的长一点就行了,如果要求高的话,那就异步生成,提供下载链接

        评论 赞赏 2019-11-05

          没仔细看代码,但就页面发聩来说,是超时了。
          你可以先不导出数据,执行你上面获取数据的代码,看是否是因为查询数据很久还是拼接数据很久再做具体优化。
          其次如果上面代码没办法优化,那你可以考虑用队列的方式来实现你的数据导出。

          评论 赞赏 2019-11-05
            撰写回答

            登录后参与交流、获取后续更新提醒