記錄佇列序列化模型導致的記憶體溢位的解決方案

ab0029發表於2019-10-29

直接進入主題,當一個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方法,直接放回一個空陣列既。或者把加入佇列需要序列化的模型的關係去掉

相關文章