2016-11-17 5 views
1

Во-первых, я очень новичок в Elixir, так что это может быть ошибочный вопросСохранять родительский процесс

У меня есть функция, которая запускает два процесса.

Первый процесс использует библиотеку erlang: fs для просмотра каталога изменений файла, а затем отправляет сообщение во второй процесс.

Второй процесс ожидает сообщения о том, что файл в каталоге был изменен, и когда он получает сообщение, запускает функцию, которая регенерирует отчет HTML (это счет-фактура).

Это выглядит следующим образом:

def run_report_daemon(line_item_dir) do 

    if Process.whereis(:html_daemon) == nil do 
     spawn(HTMLInvoiceDaemon, :run_daemon, [line_item_dir]) 
     |> Process.register(:html_daemon) 
    end 

    if Process.whereis(:file_watcher) == nil do 
     :fs.start_link(:file_watcher, Path.absname(line_item_dir)) 
    end 
    Process.sleep(1000) 
    run_report_daemon(line_item_dir) 
end 

Этот «работает», но то, что меня беспокоит, это «сон» и рекурсивный вызов.

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

Блоки if необходимы, поскольку в противном случае он будет повторно запускать и регистрировать процессы.

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

Я подозреваю, что моя проблема связана с фундаментальным непониманием «правильного способа сделать что-то» в Эликсире.

(примечание: я, вероятно, может сделать все это без порождения процессов, как это, но это обучение упражнения)

+0

Где именно вы отправляете сообщения на 'HTMLInvoiceDaemon'? Можете ли вы опубликовать еще какой-нибудь код? – Dogbert

+0

[: fs library] (https://github.com/synrc/fs) включает функцию, которая позволяет другому процессу подписываться на получение сообщений из процесса: fs. В HTMLInvoiceDaemon я подписываюсь на сообщения от: file_watcher, а затем выполняю эти сообщения. – Veen

+0

Действительно ли 'line_item_dir' или вы знаете это во время начала? Кроме того, сколько из них есть? Я попытался ответить, но потом понял, что вход там будет трудно получить, если неизвестно, когда приложение запустится. –

ответ

1

я бы, вероятно, сделать что-то вроде этого

Обратите внимание, что если любой из этих аварии, супервизор перезапустит его, и мы закончим в правильном состоянии, где есть один процесс FSWatch и один процесс InvoiceDaemon, а процесс InvoiceDaemon будет прослушивать процесс FSWatch.

Стратегия rest_for_one, которая перезапустит процесс InvoiceDaemon, если процесс FSWatch погибнет. Это предотвратит присоединение InvoiceDaemon к FSWatch, который больше не существует. Если сбой InvoiceDaemon, нет необходимости перезапускать процесс FSWatch, потому что когда возвращается InvoiceDaemon, он будет повторно подписаться, а затем запуститься вечно.

defmodule Thing do 
    use Supervisor 
    # Wraps the :fs.start_link 
    # thing in a process and goes to sleep 
    defmodule FSWatch do 
    def start_link(target_dir) do 
     watcher = spawn_link(fn -> 
     IO.puts "Watching #{target_dir}" 
     :fs.start_link(:file_watcher, target_dir) 
     :timer.sleep(:infinity) 
     end) 

     {:ok, watcher} 
    end 
    end 

    # Subscribe to the file watcher and then listen 
    # forever 
    defmodule InvoiceDaemon do 
    def start_link do 
     daemon = spawn_link(fn -> 
     :fs.subscribe(:file_watcher, "/path") 
     run() 
     end) 

     {:ok, daemon} 
    end 

    defp run do 
     receive do 
     {_, {:fs, file_event}, path} -> 
      IO.puts "Got a file event #{inspect path}" 
      run() 
     e -> 
      IO.puts "unexpected event #{inspect e}" 
      run() 
     end 
    end 
    end 

    def start_link do 
    target_dir = "/foo/bar" 

    children = [ 
     worker(FSWatch, [target_dir]), 
     worker(InvoiceDaemon, []) 
    ] 
    IO.inspect self 
    Supervisor.start_link(children, strategy: :rest_for_one) 
    end 
end 

Thing.start_link 
:timer.sleep(:infinity) 
0

Если вы не собираетесь использовать супервизор, который является правильным образом, вы можете использовать ссылку/монитор для этих процессов.

Вместо сна/рекурсивного вызова вы связываете/контролируете эти процессы. Затем вы ждете сообщения вниз, которое отправляется вам, когда любой из них умирает.

Хорошо, что вы блокируете прием, поэтому не потребляете ресурсы.

Фактически, это база супервизора, и я рекомендовал бы это, если возможно.

Больше чтения http://elixir-lang.org/docs/stable/elixir/Process.html

Посмотрите в spawn_link и spawn_monitor функций.

+0

Я бы предоставил вам больше примеров, которые вы выбрали именно так. Я не мог, потому что сейчас на мобильный. – vfsoraki

+0

Спасибо! Это в основном решение, которое я придумал прошлой ночью: подождите в блоке приема и поймайте сообщение о выходе и перед выполнением рекурсивного вызова. – Veen

Смежные вопросы