Laravel: как обновить MySQL с помощью Eloquent в дочернем процессе?

Я пишу команду Laravel, и она разветвляет некоторый дочерний процесс. Дочерний процесс обновит БД Eloquent.

Код:

<?php

namespace App\Console\Commands;

use App\Console\BaseCommand;
use App\Item;
use Illuminate\Console\Command;

class Test extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        Item::first();
        $children = [];
        for($i = 0; $i < 5; $i++) {
            $pid = pcntl_fork();
            if ($pid == -1) {
                die('pmap fork error');
            } else {
                if ($pid) {
                    $children[] = $pid;
                } else {
                    Item::first(); exit;
                }
            }
        }
        foreach ($children as $child) {
            pcntl_waitpid($child, $status);
        }
    }
}

Запустите мой код:

vagrant@homestead:~/ECAME$ php artisan test


  [Illuminate\Database\QueryException]
  Packets out of order. Expected 1 received 116. Packet size=6255201 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)



  [Illuminate\Database\QueryException]
  Packets out of order. Expected 1 received 100. Packet size=6238815 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)



  [Illuminate\Database\QueryException]
  Packets out of order. Expected 1 received 0. Packet size=2816 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)



  [Illuminate\Database\QueryException]
  Packets out of order. Expected 1 received 116. Packet size=6381412 (SQL: select * from `items` where `items`.`deleted_at` is null limit 1)



  [ErrorException]
  Packets out of order. Expected 1 received 100. Packet size=6238815



  [ErrorException]
  Packets out of order. Expected 1 received 116. Packet size=6381412



  [ErrorException]
  Packets out of order. Expected 1 received 116. Packet size=6255201



  [ErrorException]
  Packets out of order. Expected 1 received 0. Packet size=2816

В чем причина этого? И как обновить MySQL с помощью Eloquent в дочернем процессе?

PS:

Я думаю, что причина этой проблемы в том, что все дочерние процессы используют одно и то же соединение MySQL, которое разветвляется от родительского процесса.

Если я не вызываю Item::first() в родительском процессе перед вызовом fork(), он работает хорошо. (В моем реальном случае использования я не могу этого сделать ... Родительский процесс будет много делать с MySQL до дочернего процесса fork.)

Потому что в этом случае соединение MySQL не инициализируется в родительском процессе, поэтому каждый дочерний процесс инициализирует соединение самостоятельно.

Итак, если это так, как инициализировать новое соединение MySQL для каждого дочернего процесса после разветвления?

эта ссылка может вам помочь laracasts.com/discuss/channels/laravel/…

rkj 20.07.2018 10:13

Разветвленные процессы не могут совместно использовать (глобальные) ресурсы. Настоящий вопрос - почему вы разветвляетесь и является ли это реальным решением вашей проблемы? Вы должны устанавливать соединение MySQL в каждом ответвленном запросе.

N.B. 22.07.2018 11:55

Привет! Я согласен с предыдущим комментарием ... Я не хочу критиковать ваш подход к процессу разветвления, но действительно ли это только одно решение и действительно ли оно необходимо? ... Мне любопытно - каковы причины, которые подтолкнули вас к выбору это решение? Спасибо!

YanDatsiuk 28.07.2018 09:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
5
3
696
3

Ответы 3

Поскольку все дело в том, что соединение умирает, вы можете решить эту проблему, просто повторно подключившись к базе данных.

use Illuminate\Support\Facades\DB;

[...]

public function handle()
{
    User::first();

    $children = [];

    for ($i = 0; $i < 5; $i++) 
    {
        $pid = pcntl_fork();

        if ($pid == -1) 
        {
            die('pmap fork error');
        } 
        else 
        {
            if ($pid) 
            {
                $children[] = $pid;
            } 
            else
            {
                DB::connection()->reconnect(); // <----- add this
                User::first(); exit;
            }
        }
    }

    foreach ($children as $child) 
    {
        pcntl_waitpid($child, $status);
    }
}

Я тестировал это в Laravel 5.6, и он работает.

Тогда покажите нам более подходящий код для воспроизведения ошибки.

GiamPy 24.07.2018 09:50

А если вы определите второй ddbb с такими же параметрами в вашем файле database.php и запустите свой Item :: first на основе второго соединения?

# Primary database connection
            'mysql' => [
                'driver'    => 'mysql',
                'host'      => 'localhost',
                'database'  => 'myddbb',
                'username'  => 'root',
                'password'  => '',
                'charset'   => 'utf8',
                'collation' => 'utf8_unicode_ci',
                'prefix'    => '',
            ],
  # Secondary database connection
        'mysql_forConnectChildren' => [
            'driver'    => 'mysql',
            'host'      => 'localhost',
            'database'  => 'myddbb',
            'username'  => 'root',
            'password'  => '',
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ],

Позже

 $item = \DB::connection('mysql_forConnectChildren')->select('select * from Item')->get(1);

Я не тестировал, но думаю, может работать

После разветвления процесса в Laravel размещение следующего кода внутри дочернего элемента создало для меня стабильное клиентское соединение mysql в Laravel 4.2:

DB::disconnect();

// we need to clone the database connection of the parent and
// create a new connection as using the original causes protocol errors

$dbconn_child = 
    [
        'driver'    => DB::connection()->getConfig('driver'),
        'host'      => DB::connection()->getConfig('host'),
        'port'      => DB::connection()->getConfig('port'),
        'database'  => DB::connection()->getConfig('database'),
        'username'  => DB::connection()->getConfig('username'),
        'password'  => DB::connection()->getConfig('password'),
        'charset'   => DB::connection()->getConfig('charset'),
        'collation' => DB::connection()->getConfig('collation'),
        'prefix'    => DB::connection()->getConfig('prefix'),
        'modes'     => DB::connection()->getConfig('modes'),
        'options'   => DB::connection()->getConfig('options'),
    ];

Config::set('database.connections.DB_CONFIG_CHILD', $dbconn_child);

DB::setDefaultConnection('DB_CONFIG_CHILD');

DB::reconnect();

Это динамически создает новое соединение с теми же параметрами, что и исходное, а затем переключает дочерний процесс на новое соединение и подключается.

Простое отключение и повторное подключение соединения по умолчанию привело к набору ошибок протокола mysql, аналогичных результатам OP.

на самом деле, я обнаружил, что если параметры подключения точно такие же, похоже, что PDO все еще подключает вас к родительскому подключению. Я закончил тем, что изменил параметры параметров и режимов таким образом, чтобы это не повлияло на работу значимым образом, но было отличным от дочернего элемента, что не позволяло PDO выбирать существующее соединение из пула.

amatter84 05.06.2020 02:42

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