2017-05-29 4 views
1

Ich habe mehrere Beispiele von Elixir GenServer gesehen, aber sie beschäftigen sich hauptsächlich mit Array von Werten (z. B. Einkaufswagen) oder Zählerinkremente. Daher zeigen sie, wie man mit einfachen Datentypen umgeht.Wie man das Modell in Phoenix/Elixir Genserver übergibt

Ich frage mich, wie ich in einer Phoenix-Anwendung Status übergeben kann, wenn ich bestimmte Model-Datensätze aktualisiere.

Das Beispiel, das ich zur Verfügung stellen kann, ist dies:

  • Schritt 1: Ich erhalte AWS SNS-Benachrichtigung (Daten enthält, welche neues s3 Objekt wurde hinzugefügt) => speichert nur die Nachricht Notification
  • step2 zu modellieren: I parse die Nachricht innerhalb Notification, um s3 Objekt filename zu lesen. Dann speichern Sie diese neue Document Modell
  • step3: Ich habe die Metadaten des s3 Objekt holen (zB original_name) und speichern Sie es

Von Ruby on Rails Ich würde es als dies tun:

  • -Controller erzeugt Notification dann Zeitplan Hintergrundjob (Sidekiq) für den 2. Schritt
  • Hintergrund Job schafft Dokument und plant einen anderen Job Metadaten PullDocumentMetadata.perform_later zu ziehen ("Dokument", document.id
  • )

Beispiel:

class NotificationController 
    def create 
    # ... 
    notification = Notification.create(body: message_body) 
    ProcessNotification.perform_later("Notification", notification.id) 
    # ... 
    end 
end 

class ProcessNotification 
    # ... 
    def process(resource_class, resource_id) 
    notification = resource_class.constantize.find(resource_id) 
    document = Document.new(filename: parse_filename(notification.body)) 
    document.save 
    PullMetadata.perform_later("Document", document.id) 
    end 
    # ... 
end 

class PullMetadata 
    # ... 
    def process(resource_class, resource_id) 
    document = resource_class.constantize.find(resource_id) 
    document.original_name = fetch_original_name(document.filename) 
    document.save 
    end 
    # ... 
end 

Nun war ich zu versuchen, etwas Ähnliches replizieren mit Phoenix mit Genserver (Schritt für Schritt Anruf)

Der erste Schritt (Erstellen Notification wird von Phoenix getan Controller und ich möchte andere zwei Schritte zu 2 Genserver Anrufe isolieren:

hier sind nun meine Fragen:

  • ich den Zustand der Genserver bin zu ändern (zunächst war es {Notification, id}, dann ist es {Document, id}. Es fühlt sich für mich so an, als würde der Genserver die ganze Zeit vielleicht den gleichen Typ erwarten? Also sollte ich immer die `{Notification, id} zurückgeben und das Dokument aus einer Assoziation ziehen? Oder ist das ok, wie es ist?
  • Wie gut würde Genserver den Zustand eines Struct halten, wenn ich GenServer mit 'pid = GenServer.start_link (ProcessNotification, notification) init ... würde also Marshal das Objekt, oder ist das Antimaterie?
  • Wie kann ich tatsächlich aus einem Guss, so aus process_to_document würde ich pull_metadata werfen. Oder soll ich diese in der Steuerung wie folgt planen:

Beispiel:

defmodule NotificationController do 
    # ... 
    def create(conn, params) do 
    notification = # ... store body to %{}Notification 
    # ... 
    pid = GenServer.start_link(ProcessNotification, {Notification, notification.id}) 
    GenServer.cast(pid, :process_to_document) 
    GenServer.cast(pid, :pull_metadata) 

    end 
end 

Ich bin mir ziemlich sicher, was ich falsch mache, also schätze ich eine Ahnung, wie dies sollte besser sein.

Antwort

2

Cast Form eine Besetzung ist einfach.

GenServer.cast self(), {:another_event, some_data} 

Aber ich bin nicht einmal sicher, warum Sie müssten, das zu tun, da Sie scheinen den Gen-Server von Ihrem Controller zu starten. Ich denke nicht, dass das der richtige Ansatz ist. Alles, was Sie hier brauchen, ist, einen Prozess zu erzeugen, der die ganze Arbeit erledigt.

Hier können Sie Aufgabe verwenden Modul

Task.start fn -> 
    # do my heady lifting here 
end 

Wenn Sie die Fehlerbehandlung und Wiederholungen zu handhaben möchten, dann können Sie einen Supervisor verwenden, um die Aufgabe zu starten.

Wenn Sie sich Gedanken über die Anzahl der Prozesse machen, die Sie erstellen, dann sehen Sie sich Worker-Pools an.

BTW, GenServer Zustand ist ziemlich einfach. Der Anfangszustand ist das, was Sie von init/1 zurückgeben. Der nächste Zustand ist, was Sie von der handle_cast Rückkehr/2 oder handle_call/3

defmodule MyGenServer do 
    use GenServer 

    def start_link(args) do 
    init_args = # do something with args 
    GenServer.start_link(__MODULE__, init_args) 
    end 

    def init(init_args) do 
    initial_state = # perhaps manipulate init_args 
    {:ok, initial_state} 
    end 

    def handle_cast(event, current_state) do 
    new_state = # manipulate current_state 
    GenServer.cast self(), {:another_event, some_data} 
    {:noreply, new_state} 
    end 

    def handle_call(event, sender, current_date) do 
    new_state = # manipulate current_state 
    {:reply, return_value, new_state} 
    end 
end 

Denken Sie daran, dass jeder handle_xxx vollständig ablaufen muss, bevor ein anderer handle_xxx aufgerufen werden kann. Daher können Sie GenServer.call nicht zum selben GenServer aufrufen, da dies den Prozess blockiert.

Aber GenServer.cast ist async, so ist es kein Problem.

Die andere Option besteht darin, innerhalb der GenServer-Handler einen send self(), {:event, data} zu tun. Dadurch wird der Handler handle_info/2 auf dem GenServer ausgeführt.

+0

Re: 'Aber GenServer.cast ist async, so ist es kein Problem.' das ist die Sache, die ich besorgt bin, wenn ein Async kann ein anderes async und teoretical werfen, die ein anderes async, Wenn ich den Wert von GenServer in einer von denen ändern Schritte von '{Notification, 123}' nach '{Document, 234}' stellt der Supervisor die korrekte Ausführungsreihenfolge sicher? ... Es klingt für mich, dass jede 'handle_cast'-Methode nur einmal aufgerufen werden sollte, und innerhalb dieser Ausführung sollte gen server nur' handle_info' verwenden. (Es fühlt sich für mich 'handle_cast -> handle_info -> handle_info' Ähnlich wie öffentliche Methode, Aufruf von privaten Methoden) – equivalent8

+0

... und vielen Dank für den Rat, wirklich hilfreich. Ich kann sehen, dass ich es falsch sehe :) Ich werde die Kapitel über GenServer noch einmal besuchen müssen. – equivalent8

Verwandte Themen