Эрланг: какое дерево контроля мне следует закончить написанием планировщика задач? - PullRequest
1 голос
/ 23 марта 2011

В основном в образовательных целях я пытаюсь написать задачу (задача - планировщик open_port ({spawn_executable, Command})).

Я в конечном итоге с деревом, как

        supervisor
        |        |
scheduler        receiver
gen_event        gen_event
                     |
                supervisor
                     |
                dispatcher
                gen_server
                     |
                supervisor
                |    |   |
             task1  ... taskN

Другими словами:

  1. главный супервизор запускает планировщик и получатель и следит за тем, чтобы они были живы
  2. Приемник начинает среднего супервизора
  3. средний супервизор запускает диспетчер и следит за тем, чтобы он был жив
  4. Диспетчер запускает нижнего супервизора
  5. нижний супервизор запускает задачи по запросу и следит за тем, чтобы они были перезапущены в случае ошибки

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

  7. когда встречается временная метка, она уведомляет какого-либо event_manager
  8. Получатель затем уведомляется тем же менеджером событий и передает сообщение диспетчеру через среднего супервизора.
  9. Диспетчер имеет некоторую бизнес-логику, поэтому он не без сохранения состояния, например, некоторые задачи не могут быть выполнены одновременно
  10. при выполнении всех условий диспетчер передает задание нижнему супервизору, который следит за выполнением задания до тех пор, пока не будет получен нормальный выход или не пропущено какое-либо пороговое значение
  11. нижний супервизор возвращает обратно сообщение, которое затем передается наверх некоторому менеджеру событий
  12. и планировщик в конечном итоге получает это сообщение, удаляя задачу из своей очереди или повторно ставя ее в очередь или что-то еще

Вопросы:

  1. Правильно ли я использую поведение?
  2. Разве структура не слишком сложна? (Однако в будущем система станет распределенной.)
  3. Есть ли способ объединить получателя + среднего супервизора и диспетчера + нижнего супервизора в двух модулях вместо четырех, реализующих 4 поведения одновременно?
  4. Или есть ли способ объединить приемник + диспетчер + нижний супервизор в одном модуле, устраняя необходимость в среднем супервизоре, одновременно реализуя поведение gen_event + gen_server + супервизор?
  5. Ошибаюсь ли я, думая о поведении как об интерфейсах или о множественном наследовании в ОО-языках? (Это заставляет меня задавать вопросы 3 и 4.)

Заранее спасибо.

P. С. ИМО, с одной стороны, структура слишком сложна; с другой стороны, такая структура позволяет мне распределять любой из ее блоков (например, много планировщиков для одного получателя, один планировщик для многих получателей, много планировщиков для многих получателей, много диспетчеров для каждого получателя и даже много нижних супервизоров для каждого диспетчера - каждый слой со своей политикой надзора). Где находится баланс между сложностью и расширяемостью?

1 Ответ

1 голос
/ 24 марта 2011

Я бы посоветовал упростить ваш дизайн, например:

        supervisor
        |        |
 dispatcher      |
 +scheduler      |
                 |
            supervisor
            |    |   |
         task1  ... taskN

Нет особой выгоды от того, что отдельный планировщик отправляет события диспетчеру, который запускает задачи и т. Д. Даже в светеdistribution.

Диспетчер-планировщик, вероятно, может быть довольно просто выполнен с помощью модуля таймера и может быть gen_server.Таймер может отправлять сообщения, которые вы можете обработать в обратном вызове handle_info, или вызывать функции API вашего gen_server.

Вы также можете использовать функцию тайм-аута, чтобы разбудить gen_server после следующего интервала, который будет еще прощене нужно беспокоиться об отмене таймеров при добавлении новой «задачи».

Диспетчер / планировщик затем вызывает supervisor:start_child для добавления рабочих задач.

Распределение можно легко добавить:Диспетчер / планировщик может находиться на отдельном узле, чем диспетчер второго уровня.Функция запуска задач может распространяться дальше и, возможно, с помощью модуля pool для балансировки нагрузки.

Чтобы ответить на ваши пять вопросов:

  1. Я подозреваю васиспользуют gen_event там, где он не нужен, но поскольку сами модули не нужны, его легко исправить, удалив их.gen_event - если вы хотите иметь возможность зарегистрировать много обработчиков на одном источнике событий, вы используете его 1: 1.Деревья наблюдения обычно строятся так, что супервизоры являются прямыми дочерними элементами других супервизоров.

  2. Да, это слишком сложно, выглядит так, как если бы вы делали это на ОО-языках с меньшей выразительной силой.И просто для подготовки к возможному распространению его не нужно.Рефакторинг на функциональном языке, таком как Erlang, намного проще, чем вы думаете.Так что начните с простой и разделенной функциональности, если увидите необходимость.

3 + 4.Смотрите мое совершенно другое предложение.

  1. Это не так, как OO.Поведения в OTP - это только модули обратного вызова, скрывающие механику процесса в универсальном модуле.

Даже с простой структурой, которую я предложил, есть большая гибкость (предоставленная вам Эрлангом), потому что если вы хотите иметьнесколько планировщиков, вы можете просто использовать rpc для вызова супервизора.Вы можете использовать pool для автоматического распределения нагрузки по распределению задач.А диспетчерская часть может быть легко отделена от планировщика (тогда и под супервизором верхнего уровня), и вы можете иметь более общее состояние, отделенное от планировщика.

...