Как сохранить соединение mysql в родительском процессе после pcntl_fork?

Как вы все знаете, когда вы разветвляете, дочерний элемент получает копию всего, включая файловые и сетевые дескрипторы — man fork.

В PHP, когда вы используете pcntl_fork, все ваши соединения, созданные с помощью mysql_connect, копируются, и это некоторая проблема - php docs и SO вопрос. Здравый смысл в этой ситуации говорит закрыть родительское соединение, создать новое и позволить дочернему использовать старое. Но что, если указанному родителю нужно создавать много детей каждые несколько секунд? В этом случае вы в конечном итоге создадите множество новых подключений — по одному на каждый набор ответвлений.

Что это означает в коде:

while (42) {

  $db = mysql_connect($host, $user, $pass);

  // do some stuff with $db
  // ...

  foreach ($jobs as $job) {
        if (($pid = pcntl_fork()) == -1) {
            continue;
        } else if ($pid) {
            continue;
        }
    fork_for_job($job);
  }

  mysql_close($db);
  wait_children();
  sleep(5);
}

function fork_for_job($job) {

  // do something. 
  // does not use the global $db 
  // ...

  exit(0);
}

Ну, я не хочу этого делать - слишком много соединений с базой данных. В идеале я хотел бы иметь возможность добиться поведения, подобного этому:

$db = mysql_connect($host, $user, $pass);

while (42) {

  // do some stuff with $db
  // ...

  foreach ($jobs as $job) {
        if (($pid = pcntl_fork()) == -1) {
            continue;
        } else if ($pid) {
            continue;
        }
    fork_for_job($job);
  }

  wait_children();
  sleep(5);
}

function fork_for_job($job) {

  // do something
  // does not use the global $db 
  // ...

  exit(0);
}

Как вы думаете, это возможно?

Некоторые другие вещи:

  • Это скрипт php-cli
  • Я пробовал использовать mysql_pconnect в первом примере, но, насколько я могу судить, разницы нет — сервер mysql получает столько же новых подключений. Может потому что это cli и pconnect не работает как в mod_php. Как заметил Марк - pconnect в php-cli не имеет смысла.

person doycho    schedule 20.04.2011    source источник
comment
постоянные соединения сохраняются только в том случае, если есть что-то, что поддерживает соединение открытым. в случае mod_php PHP остается активным внутри веб-сервера и может удерживать открытое соединение. В интерфейсе командной строки после завершения сценария ничего не остается, поэтому соединение будет закрыто в любом случае.   -  person Marc B    schedule 20.04.2011
comment
Вы неправильно настроили разветвление, вы исчерпали пул соединений MySQL. Возможно, может помочь проверка следующего примера и соответствующее изменение: > stackoverflow.com/questions/5573214/   -  person Michael J.V.    schedule 20.04.2011


Ответы (3)


Единственное, что вы можете попробовать, это позволить своим детям подождать, пока каждый другой ребенок не закончит свою работу. Таким образом, вы можете использовать одно и то же соединение с базой данных (при условии отсутствия проблем с синхронизацией). Но, конечно, у вас будет много процессов, что тоже не очень хорошо (по моему опыту, PHP использует довольно много памяти). Если наличие нескольких процессов, обращающихся к одному и тому же соединению с базой данных, не является проблемой, вы можете попытаться создать «группы» процессов, которые совместно используют соединение. Таким образом, вам не нужно ждать, пока завершится каждое задание (вы можете убраться, когда вся группа закончит), и у вас также не будет много связей.

Вы должны спросить себя, действительно ли вам нужно подключение к базе данных для ваших рабочих процессов. Почему бы не позволить родителю получить данные и записать ваши результаты в файл?

Если вам нужно подключение, вам следует рассмотреть возможность использования другого языка для работы. PHPs cli сам по себе не является «типичным» вариантом использования (он был добавлен в 4.3), а многопроцессорность — это скорее хак, чем поддерживаемая функция.

person svens    schedule 20.04.2011
comment
Проблема не в том, что я хочу использовать одно и то же соединение в дочерних элементах, чтобы избавить их от соединения самостоятельно, а скорее в том, что я не хочу создавать новое соединение для родителя после каждой вилки, потому что все равно никто его не использует, и я в конечном итоге повторное подключение каждого прохода основного цикла без уважительной причины. Возможно, примеры были ошибочными. Я их немного отредактировал. - person doycho; 21.04.2011
comment
Вы все еще можете использовать тот же подход; создавать группы (получать работу для нескольких дочерних элементов, затем разветвляться и отбрасывать соединение). Нет правильного решения вашей проблемы, вы можете только уменьшить накладные расходы (используя стандартный PHP). Если вам не нужно подключение к базе данных для ваших дочерних элементов, вам следует подумать о запуске независимых дочерних процессов (т. е. о запуске новых процессов вместо разветвления), в зависимости от ваших входных данных, вы можете передать их в качестве аргумента или (в худшем случае) в файл. Но это может быть даже медленнее, чем открывать новое соединение с базой данных каждые несколько дочерних элементов. - person svens; 29.04.2011
comment
Что ж, спасибо тебе. Насколько я понимаю, это ответ на мой вопрос :) - person doycho; 04.05.2011

Если потомок достаточно быстро вызывает exec() или _exit(), все в порядке. Проблема в том, что ребенок остается и удерживает копии ваших файловых дескрипторов.

Вы также можете использовать posix_spawn, если у PHP есть для этого API. Это может хорошо сработать.

person MarkR    schedule 20.04.2011

Мой совет (из личного опыта по той же проблеме) - закрыть соединение до pcntl_fork(), а затем открыть новые соединения в родительском и/или дочернем процессе по мере необходимости.

Если вы открываете новое соединение в родительском процессе, вам необходимо заблокировать сигнал SIGCHLD (используя pcntl_sigprocmask(SIG_BLOCK, array(SIGCHLD)). В дочерних процессах особой заботы не требуется (кроме случаев, когда они также запускают своих собственных дочерних элементов, становясь таким образом родителями).

SIGCHLD — это сигнал, который получает родительский процесс, когда завершается один из его дочерних процессов.

Во время связи с сервером клиентская библиотека MySQL использует nanosleep() приостановить выполнение программы на некоторое время. Функции sleep() возвращаются по истечении времени, но они также возвращаются до времени, если процесс получает сигнал, пока он приостановлен.

Когда nanosleep() возвращается из-за сигнала (т. е. до того, как прошло достаточно времени), библиотека MySQL сбивается с толку и сообщает об ошибке «Сервер MySQL ушел», и соединение больше нельзя использовать. Это ложная тревога, сервер MySQL все еще ждет запросов, но клиентский код обманут сигналом, пришедшим в неподходящий момент.

Если вы заинтересованы в получении сигнала SIGCHLD, вы можете заблокировать его перед выполнением запроса MySQL, а затем разблокировать его снова (чтобы избежать его получения во время связи с сервером MySQL.

Также прочитайте этот ответ и это ответ Я писал по похожим вопросам (это та же информация, но с более подробной информацией и объяснением.)

person axiac    schedule 19.08.2017