2015-07-14 30 views
7

Sono davvero alle prese con i supervisori Elixir e ho capito come chiamarli per poterli usare. Fondamentalmente, sto solo provando ad avviare un supervisore Task a cui posso inviare messaggi.Supervisori elisir - Come si chiama un'attività supervisionata

così ho il seguente:

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

con il punto di ingresso del progetto come:

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 

A questo punto, non so nemmeno sicuri che sto usando la cosa giusta (Task in particolare). Fondamentalmente, tutto ciò che voglio è generare un processo o un'attività o GenServer o qualsiasi altra cosa è giusta quando l'applicazione si avvia che posso inviare messaggi a cui faremo essenzialmente un System.cmd(cmd, opts). Voglio che questo compito o processo sia supervisionato. Quando invio un messaggio {:run, cmd, opts} come {:run, "mv", ["/file/to/move", "/move/to/here"]}, voglio che crei una nuova attività o processo per eseguire quel comando. Per il mio utilizzo, non ho nemmeno bisogno di riprendere la risposta dal compito, ho solo bisogno di eseguire. Qualsiasi guida su dove andare sarebbe utile. Ho letto la guida introduttiva, ma onestamente mi ha lasciato più confuso perché quando provo a fare ciò che è fatto non risulta mai come nell'applicazione.

Grazie per la vostra pazienza.

risposta

8

vorrei solo usare un GenServer, istituito come il seguente:

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 

È possibile quindi inviare il processo in esecuzione di un comando da eseguire in questo modo:

# 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"]) 

Fondamentalmente stiamo creando un processo "singleton" (solo un processo può essere registrato con un determinato nome e stiamo registrando il processo Run.Command con il nome del modulo, quindi tutte le chiamate consecutive a start_link mentre il processo è in esecuzione avranno esito negativo. questo semplifica la configurazione di un'API (lo run) che può eseguire in modo trasparente il comando nell'altro processo senza che il processo chiamante debba sapere nulla a riguardo. Ho usato call rispetto a cast qui, ma è un cambiamento banale se non ti interesserà mai il risultato e non vuoi che il processo chiamante blocchi.

Questo è uno schema migliore per qualcosa di long-running. Per le cose una tantum, Task è molto più semplice e facile da usare, ma preferisco usare GenServer per processi globali come questo personalmente.

+3

Questa è una soluzione perfetta. Il motivo per cui non è possibile assegnare un nome a un'attività è perché se si desidera inviarlo messaggi ... non si desidera più utilizzare un'attività. –

+0

Grazie per la spiegazione di questo dettaglio. Questo è stato molto utile per me @ bitwalker. Grazie per il vostro tempo, assistenza e pazienza. – kkirsche

+0

@bitwalker Quando si tenta di utilizzare questo ottengo un timeout GenServer.call. Non dovrebbe il supervisore impedire che questo tipo di cose accada? (uscita) chiuso in: GenServer.call (Run.Command, {: run, "ls", ["."]}, 5000) ** timeout (EXIT) (elixir) lib/gen_server.ex: 356 GenServer.call/3 – kkirsche