2014-11-17 10 views
6

Ich habe ein Mix-Projekt mit so einfach wie möglich ein Supervisor und GenServer. Als ich von iex nennen:So führen Sie Elixir Supervisor in escript

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

The: murmeln Aufruf eine Ausnahme auslöst, dann wird die GenServer neu gestartet und die zweite: echo Aufruf ok funktioniert.

Wenn ich den Code auf eine andere Weise ausführen, kann der Supervisor den GenServer nicht neu starten. Zum Beispiel habe ich eine eScript des Projekts mit dem Hauptmodul wie folgt erstellen:

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 

Der: murmeln Aufruf löst eine Ausnahme und die eScript endet ohne Supervisor die GenServer neu zu starten.

Ich habe den Code der Supervisor- und Server-Module nicht angegeben, weil sie funktionieren, wenn sie von iex aufgerufen werden, also schätze ich, dass sie hier nicht benötigt werden.

Habe ich ein konzeptionelles Missverständnis? Ist das nicht möglich oder mache ich etwas falsch?

Antwort

5

Das Problem liegt nicht in Ihrem Server und Supervisor, sondern in der Art und Weise Du rufst sie an. Wenn der Server beendet wird, während ein anderer Prozess auf eine Antwort auf GenServer.call wartet, wird der aufrufende Prozess ebenfalls beendet, so dass der letzte Aufruf nie stattfindet. Der Grund dafür ist, dass der Prozess möglicherweise nicht in einem ungültigen Zustand fortgesetzt werden kann, wenn ein synchroner Aufruf fehlgeschlagen ist (ist synchron im Gegensatz zu GenServer.cast). Wenn Sie tun dies nur der Supervisor zu testen, dann können Sie versuchen:

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 

Der Grund ist es in iex funktioniert ist, dass iex Fallen der Ausgang und ermöglicht es Ihnen, eine weitere Zeile eingeben.

+1

Ok, nach viel Rumlaufen und Yak Rasieren, das war mein Problem, zusammen mit ungenügend: timer.sleeps. Ich könnte mit einem Noobie-Modus tun, der sie nach allem hinzufügt. Mein Skript stürzt ab: mumble, wenn ich GenServer.start verwende, aber wenn ich GenServer.start_link verwende, meldet es den Fehler und fährt mit einem neugestarteten GenServer fort. Ich habe verstanden, dass es anders herum funktionieren sollte, aber ich werde es nachlesen. Vielen Dank. –

3

Das Escript-Verhalten ist korrekt. Du verpasst einfach wie dir die iex-Shell "hilft".

Was Sie in Ihrem Code tun, ist eine verknüpfte Prozess gestartet, und dann stürzt es ab. Und da es sich um einen verknüpften Prozess handelt, sollten alle verknüpften Prozesse heruntergefahren werden. Es könnte einige "Ausnahmen" geben, aber was passiert mit deinem E-Skript-Prozess.

Beide Shell und Przess Supervisor können solche "Ich starb, also sollten Sie" Nachricht behandeln. Sie tun es, indem sie die Art und Weise ändern, wie Prozesse (verknüpfter Prozess, nicht der Sterbende) solche Nachrichten verarbeiten. Es erlaubt ihnen, sie als normale Nachrichten zu empfangen (die Sie in receive Klausel erhalten könnten, wenn Sie möchten), anstatt spezielle, interne Nachrichten. Um dieses Verhalten zu ändern, verwenden sie Process.flag(:trap_exit, :true) (elixir doc zeigt auf eralng's one). Es erlaubt der Shell, nur den Tod der getöteten Prozesse auszudrucken, vielmehr stirbt sie jedes Mal, wenn Sie etwas Schlechtes tun.

So könnten Sie das gleiche tun. Ändern Sie dieses Flag, und wenn Sie Muster in receive für solche Nachrichten übereinstimmen. Aber ich glaube nicht, dass du das suchst. Da Ihr Prozess ein Singleton ist und der Supervisor alle Neustarts durchführt, haben Sie eigentlich keinen Grund, an erster Stelle zu verlinken. Es gibt keine Notwendigkeit für Updates zu Todesfällen und Neustarts, lassen Sie sich einfach Sorgen machen. Es ist genau wie Joe Armstrong sagte (könnte paraphrasieren)

Sie brauchen nicht zu wissen, wie Verkaufsautomaten zu beheben, um es zu verwenden.

So einfach, start, anstatt start_link.

Das heißt, Sie könnten in Betracht ziehen, einen Link mit dem Supervisor zu erstellen, der auch nach zu vielen Neustarts abstürzen könnte (er könnte aufgefordert werden, sich so zu verhalten). Und es erlaubt Ihnen, ihn (und überwachten Prozess) zu nehmen, wenn Sie sterben. Oder er könnte mit Ihrem Vorgesetzten oder Anwendungsleiter oder auf andere Weise verbunden werden. Es hängt von Ihrer Domain ab, und es gibt keine schlechte Entscheidung, Sie müssen nur überprüfen, was für Sie funktioniert.Es ist Design-Entscheidung, und Sie haben entweder zu experimentieren oder mehr darüber lesen:

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

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

http://learnyousomeerlang.com/supervisors

Verwandte Themen