2014-11-17 8 views
6

Ho un progetto di mixaggio con il più semplice possibile Supervisore e GenServer. Quando chiamo da iex:Come eseguire Elixir Supervisor in script

EchoCmd.Supervisor.start_link([:Hello]) 
GenServer.call(:echoserver, :echo) 
GenServer.call(:echoserver, :mumble) 
GenServer.call(:echoserver, :echo) 

L': chiamata Mumble solleva un'eccezione, quindi la GenServer viene riavviato e la seconda: echo chiamata funziona bene.

Se eseguo il codice in qualsiasi altro modo, Supervisor non riesce a riavviare il GenServer. Per esempio, io creo un EScript del progetto con il modulo principale come segue:

defmodule EchoCmd.Echo do 
    def main(args) do 
     EchoCmd.Supervisor.start_link([:Hello]) 
     GenServer.call(:echoserver, :echo) 
     GenServer.call(:echoserver, :mumble) 
     GenServer.call(:echoserver, :echo) 
    end 
end 

L': chiamata Mumble solleva un'eccezione e l'Escript termina senza il Supervisore riavviare il GenServer.

Non ho incluso il codice dei moduli Supervisor e Server perché funzionano correttamente quando vengono chiamati da iex, quindi suppongo che non siano necessari qui.

Ho un equivoco concettuale? Non è possibile o sto facendo qualcosa di sbagliato?

risposta

5

Il problema non risiede sul server e supervisore, ma nel modo li stai chiamando Se il server si chiude mentre un altro processo è in attesa di risposta a GenServer.call, anche il processo chiamante termina, quindi l'ultima chiamata non viene mai eseguita. La ragione di ciò è che il processo non potrebbe continuare in uno stato non valido se una chiamata sincrona non è riuscita (GenServer.call è sincrono invece di GenServer.cast). Se stai facendo questo solo per testare il supervisore, allora si può provare:

defmodule EchoCmd.Echo do 
    def main(args) do 
     EchoCmd.Supervisor.start_link([:Hello]) 
     GenServer.cast(:echoserver, :echo) 
     GenServer.cast(:echoserver, :mumble) 
     GenServer.cast(:echoserver, :echo) 
    end 
end 

Il motivo per cui lavora in iex è che iex trappole l'uscita e consente di digitare in un'altra linea.

+1

Ok, dopo tanto borbottamento e rasatura yak, questo era il mio problema, insieme a insufficiente: timer.sleeps. Potrei fare con una modalità noobie che li aggiunge dopo tutto. La mia copia si blocca da: mumble se uso GenServer.start, ma se utilizzo GenServer.start_link segnala l'errore e prosegue con un GenServer riavviato. Ho capito che dovrebbe funzionare al contrario, ma leggerò. Grazie. –

3

Il comportamento dell'iscrizione è corretto. Ti manca solo come il guscio iex ti "aiuta".

Quello che stai facendo nel tuo codice sta avviando un processo collegato, e che lo si blocca. E poiché si tratta di un processo collegato, quando scende, suppone di far cadere tutti i processi collegati. Potrebbero esserci alcune "eccezioni", ma ciò che sta accadendo alla tua procedura di script.

Sia il supervisore di shell che di prcess possono gestire tale messaggio "Sono morto, quindi dovresti". Lo fanno cambiando il modo in cui il processo (processo collegato, non quello morente) elabora tali messaggi. Permette loro di riceverli come normali messaggi (che potresti ricevere nella clausola receive se desideri) piuttosto che speciali interni. Per modificare questa impostazione, utilizzare Process.flag(:trap_exit, :true) (elixir doc che punta a eralng's one). Permette alla shell di stampare solo la morte dei processi uccisi, piuttosto che morire ogni volta che fai qualcosa di male.

Quindi potresti fare la stessa cosa. Cambia questo flag, e se non vuoi corrispondere allo schema in receive su tali messaggi. Ma non penso che sia quello che stai cercando. Dal momento che il tuo processo è singleton e il supervisore fa tutto il riavvio, in realtà non hai alcun motivo per collegarti in primo luogo. Non c'è bisogno di aggiornamenti su morti e riavvii, lascia che sia il supervisore a preoccuparsi di ciò. È proprio come ha detto Joe Armstrong (potrebbe parafrasare)

Non è necessario sapere come risolvere il distributore automatico per usarlo.

Quindi, solo start, anziché start_link.

Detto questo, si potrebbe prendere in considerazione la creazione di un collegamento con il supervisore, che potrebbe anche morire dopo un numero eccessivo di riavvii (potrebbe essere detto di comportarsi in questo modo). E ti permette di prenderlo (e supervisionato) quando muori. Oppure potrebbe essere collegato al tuo supervisore o supervisore dell'applicazione o in qualche altro modo. Dipende dal tuo dominio e non c'è una decisione sbagliata, devi solo controllare cosa funziona per te.E 'decisione di progettazione, e si sia necessario sperimentare o leggere più su di esso:

http://elixir-lang.org/getting_started/mix_otp/5.html

http://www.erlang.org/doc/design_principles/des_princ.html

http://learnyousomeerlang.com/supervisors