2015-07-14 8 views
7

Naprawdę walczę z opiekunami Elixir i zastanawiam się, jak je nazwać, aby móc z nich korzystać. Zasadniczo próbuję uruchomić nadzorowaną wersję Task, do której mogę wysyłać wiadomości.Elixir Supervisors - Jak nazwać nadzorowane zadanie

Więc mam następujące:

defmodule Run.Command do 
    def start_link do 
    Task.start_link(fn -> 
     receive do 
     {:run, cmd} -> System.cmd(cmd, []) 
     end 
    end) 
    end 
end 

z punktu wejścia projekt jako:

defmodule Run do 
    use Application 

    # See http://elixir-lang.org/docs/stable/elixir/Application.html 
    # for more information on OTP Applications 
    def start(_type, _args) do 
    import Supervisor.Spec, warn: false 

    children = [ 
     # Define workers and child supervisors to be supervised 
     worker(Run.Command, []) 
    ] 

    # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html 
    # for other strategies and supported options 
    opts = [strategy: :one_for_one, name: Run.Command] 
    Supervisor.start_link(children, opts) 
    end 
end 

W tym momencie, nawet nie czuć się pewnie, że używam uszne (Task w szczególności). Zasadniczo wszystko, czego chcę, to odrodzenie procesu lub zadania lub GenServer lub cokolwiek jest w porządku, gdy aplikacja się rozpocznie, że mogę wysyłać wiadomości, które w istocie robią System.cmd(cmd, opts). Chcę, aby to zadanie lub proces był nadzorowany. Kiedy wysyłam wiadomość o numerze {:run, cmd, opts}, taką jak {:run, "mv", ["/file/to/move", "/move/to/here"]}, chcę, aby uruchomiła nowe zadanie lub proces, aby wykonać to polecenie. Do mojego użytku, nawet nie potrzebuję odzyskać odpowiedzi z zadania, po prostu potrzebuję go do wykonania. Pomocne będą wskazówki dotyczące tego, gdzie się udać. Przeczytałem poradnik dla początkujących, ale szczerze mówiąc, bardziej mnie to zdziwiło, ponieważ kiedy próbuję robić to, co się robi, nigdy się tak nie dzieje, jak w aplikacji.

Dziękuję za cierpliwość.

Odpowiedz

8

Chciałbym po prostu użyć GenServer, skonfigurować tak:

defmodule Run do 
    use Application 

    def start(_, _) do 
    import Supervisor.Spec, warn: false 

    children = [worker(Run.Command, [])] 
    Supervisor.start_link(children, strategy: :one_for_one) 
    end 
end 

defmodule Run.Command do 
    use GenServer 

    def start_link do 
    GenServer.start_link(__MODULE__, [], name: __MODULE__) 
    end 

    def run(cmd, opts) when is_list(opts), do: GenServer.call(__MODULE__, {:run, cmd, opts}) 
    def run(cmd, _), do: GenServer.call(__MODULE__, {:run, cmd, []}) 

    def handle_call({:run, cmd, opts}, _from, state) do 
    {:reply, System.cmd(cmd, opts), state} 
    end 
    def handle_call(request, from, state), do: super(request, from, state) 
end 

Można następnie wysłać uruchomionego procesu polecenie, aby wykonać tak:

# If you want the result 
{contents, _} = Run.Command.run("cat", ["path/to/some/file"]) 
# If not, just ignore it 
Run.Command.run("cp", ["path/to/source", "path/to/destination"]) 

Zasadniczo tworzymy proces "singleton" (tylko jeden proces może zostać zarejestrowany pod podaną nazwą, a my rejestrujemy proces Run.Command z nazwą modułu, więc wszelkie kolejne wywołania do start_link podczas wykonywania procesu zakończą się niepowodzeniem. dzięki temu można łatwo skonfigurować interfejs API (run), która może wykonywać polecenia w sposób przezroczysty w drugim procesie, bez konieczności wywoływania procesu wywołującego. Używałem tutaj call w porównaniu do cast, ale jest to prosta zmiana, jeśli nigdy nie dbasz o wynik i nie chcesz, aby proces wywołujący blokował.

To jest lepszy wzór na coś od dawna działającego. W przypadku zdarzeń jednorazowych jest dużo prostsze i łatwiejsze w użyciu, ale wolę używać GenServer do takich globalnych procesów, jak ten osobiście.

+3

To idealne rozwiązanie. Powodem, dla którego nie możesz nazwać zadania, jest to, że jeśli chcesz wysłać mu wiadomości ... nie chcesz już używać zadania. –

+0

Dziękuję za wyjaśnienie tego w taki sposób. To było bardzo pomocne dla mnie. Dziękuję za poświęcony czas, pomoc i cierpliwość. – kkirsche

+0

@bitwalker Podczas próby użycia tego otrzymuję limit czasu GenServer.call. Czy nadzorca nie powinien zapobiegać temu, aby coś takiego się wydarzyło? (exit) zakończone w: GenServer.call (Run.Command, {: run, "ls", ["."]}, 5000) ** (EXIT) limit czasu (eliksir) lib/gen_server.ex: 356 : GenServer.call/3 – kkirsche