记录队列序列化模型导致的内存溢出的解决方案

直接进入主题,当一个Job需要加入队列的时候,经常会用到一个框架自带的序列化TraitsIlluminate\Queue\SerializesModels,例如以下代码:

<?php
namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Models\Order;

class CloseCheckOutOrder implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
    protected $order;

    public function __construct(Order $order)
    {
        $this->order = $order;
    }

    public function handle()
    {
        // TODO XXX
    }
}

默认情况下执行dispatch(new CloseCheckOutOrder($order))是不会报错的

只有当关联了relation时,且关联的子项也关联了父项,这时候就会有问题了,例如以下代码:

$order = Order::with(['order_goods'])->first();
foreach ( $order->order_goods as $order_goods) {
    // 这里如果业务需求,假装把订单商品关联下父订单
    $order_goods->order()->associate($order);
}
// 然后我们这里调用Job的话就会内存溢出
dispatch(new CloseCheckOutOrder($order));

查看了下源码,最终定位到了Illuminate\Database\Eloquent\Model类下面的方法getQueueableRelations

/**
 * Get the queueable relationships for the entity.
 *
 * @return array
 */
public function getQueueableRelations()
{
    $relations = [];

    foreach ($this->getRelations() as $key => $relation) {
        if (! method_exists($this, $key)) {
            continue;
        }

        $relations[] = $key;

        if ($relation instanceof QueueableCollection) {
            // 就是这里出的问题,子关联项如果也关联了父级的话,会一直循环下去
            foreach ($relation->getQueueableRelations() as $collectionValue) {
                $relations[] = $key.'.'.$collectionValue;
            }
        }

        if ($relation instanceof QueueableEntity) {
             // 就是这里出的问题,子关联项如果也关联了父级的话,会一直循环下去
            foreach ($relation->getQueueableRelations() as $entityKey => $entityValue) {
                $relations[] = $key.'.'.$entityValue;
            }
        }
    }

    return array_unique($relations);
}

不难看出上面的代码,变成无线循环了,所以最终导致内存溢出

简单粗暴的方法就是不设置序列化的关系,在Order模型里重写getQueueableRelations方法,直接放回一个空数组既。或者把加入队列需要序列化的模型的关系去掉

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!