Laravel / PHP — правильный выбор для параллельного выполнения заданий?

#php #laravel

#php #laravel

Вопрос:

Мое текущее приложение имеет статус около 5000 сетевых узлов в базе данных. Каждый узел должен обновляться поминутно, чтобы предоставлять текущий статус. Процесс обновления выглядит следующим образом:

  1. Получить старую запись из базы данных, чтобы получить ее IP
  2. Отправьте RPC на узел
  3. Обновите запись базы данных возвращенными данными

Для этого я использую Laravel horizon, который предоставляет мне удобный планировщик заданий на основе redis. Каждую минуту cron получает все записи узла и отправляет каждую UpdateNode(id) задачу в планировщик.

С увеличением количества узлов мне также приходится постоянно увеличивать количество работников расписания. В настоящее время 60 потоков PHP отвечают только за обновление узлов, чтобы поддерживать выполнение цикла обновления.

Этот факт снова приводит к огромной проблеме с ресурсами (в настоящее время 80% оперативной памяти и 70% процессора на 40-долларовом DigitalOcean Droplet), что снова подводит меня к моим основным вопросам:

  1. Является ли Laravel (или гораздо больше PHP) по-прежнему подходящим кандидатом для этой работы?
  2. Возможно, я делаю что-то неправильно с точки зрения Laravel? Обычно получение записи в базе данных, отправка RPC и обновление записи новыми данными не должно быть большой проблемой.

// редактировать: Вот мой iostat : введите описание изображения здесь

// для всех, кого интересует вот проект:https://github.com/nknx-org/nknx-api Задание, о котором мы говорим, это https://github.com/nknx-org/nknx-api/blob/master/app/Jobs/UpdateNode.php

Комментарии:

1. опубликуйте, пожалуйста iostat

2. Способны ли узлы отправлять обновления, а не опрашиваться?

3. Без просмотра вашего кода или статистики использования ресурсов трудно дать вам ответ; в любом случае, вы можете исключить первый шаг, сохранив старый ip вместе с заданием для его обработки, что должно сократить время, необходимое для обработки заданий на долю.

4. @TravisBritz К сожалению, нет — было бы проще, да 🙂

5. Это сделал @FAEWZX — Если какой-то код полезен, я могу предоставить и его, но я подумал сначала задать общий вопрос об этой ситуации

Ответ №1:

Единственное, что вы могли бы сделать, это разделить работу по отправке заданий на еще одно задание, если запланированное задание является каким-либо узким местом.

Вместо того, чтобы ваша единственная запланированная задача отвечала за отправку всех 5000 UpdateNode заданий для рабочих, вы могли бы разделить записи на фрагменты идентификаторов. Итак, ваша запланированная задача разбила бы 5000 записей, например, на блоки по 250 идентификаторов и отправила бы 20 новых DispatchUpdateNodeJobs($ids) заданий, каждое с массивом из 250 идентификаторов.

Эти задания отправляются в вашу обычную систему очередей для получения вашими рабочими, и каждый рабочий, который получает DispatchUpdateNodeJobs задание, в свою очередь отправляет свои 250 UpdateNode заданий при запуске, а затем эти задания забираются рабочими, как они делают сейчас. Это может упростить масштабирование работы в зависимости от того, как настроена ваша система, а также потенциально быстрее поместить полные 5000 заданий в очередь для рабочих, если на создание 5000 заданий по расписанию у вас ушло много времени.

Однако, если вы говорите, что загрузка процессора высока из-за того, что все 60 ваших существующих работников находятся на одном сервере, то я думаю, вам просто нужно добавить еще один сервер, чтобы разделить работу. 60 PHP-процессов, каждый из которых использует 1% ЦП и ~ 25 МБ памяти, все равно будут использовать 60% ЦП и 1,5 ГБ памяти, избежать этого при таком количестве активных процессов невозможно.

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

В конечном счете, лучший вариант будет зависеть от того, каковы ваши текущие узкие места и как настроена система.

Комментарии:

1. На самом деле это хорошая стратегия. К сожалению, у каждого RPC-вызова максимальное время ожидания составляет 1 секунду, так что я могу выполнить только 60 заданий в пакетном режиме, но это помогло бы. Также возможно добавление другого сервера. Характеристики моего сервера: — 8 ГБ оперативной памяти — 4 VCPU Просто странно, что 60 процессов заполняют сервер настолько последовательно, я также добавил репозиторий в начальный пост! В базу данных добавляются индексы.

2. Я думаю, вы, возможно, неправильно поняли? Разбиение на фрагменты предназначено только для отправки отдельных UpdateNode задач; в отношении их выполнения ничего не меняется, поэтому время ожидания RPC будет таким же, как и раньше. Преимущество в том, что у вас нет ни одной запланированной команды, выполняющейся в течение длительного времени, отправляющей все 5000 заданий в очередь

3. О да, я неправильно понял концепцию — теперь я понимаю, что вы предлагаете не оптимизировать UpdateNode само задание — вместо этого диспетчеризация является своего рода узким местом

Ответ №2:

%user = 53% это одна задача = 1% * 4CPU = 4% это большой

Разделить вашу задачу

handle() — получать данные только в папку или redis

 file_put_contents("/path/to/response/nodes/".time()."_".$node->alias,$apiRequest->getBody());
  

Отправляйте все json в базу данных из папки каждую минуту в рамках одной задачи.