2017-05-29 16 views
1

Elixir GenServerの例をいくつか見てきましたが、主に値の配列(ショッピングカートなど)やカウンタの増分を扱っています。したがって、単純なデータ型の処理方法を示します。Phoenix/Elixir Genserverでモデルを渡す方法

特定のModelレコードを更新しているとき、どのようにPhoenixアプリケーションで状態を渡すことができますか?

Iを提供することができる例はこれです:

  • STEP1:私は(新しいS3オブジェクトが追加されたどのようなデータを含む)AWS SNS通知を受信=>だけNotification
  • STEP2をモデル化するためのメッセージを格納しますI Notification内のメッセージを解析して、s3オブジェクトfilenameを読み取ります。

    :私はこのようにそれを行うだろうS3オブジェクト(例えばoriginal_name)のメタデータを取得し、Railsのにルビーから来る

それを格納します。そして、新しいDocumentモデル

  • STEP3にこれを保存します
    • コントローラNotification次いで
    • 第2工程のためのバックグラウンドジョブ(Sidekiq)をスケジュールするバックグラウンドジョブは、メタデータPullDocumentMetadata.perform_later(「文書」、document.idを引っ張ってドキュメントとスケジュール別のジョブを作成し、作成し)

    例:

    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 
    

    は今、私はにしようとしていた段階コールによってGenserver(ステップを使用してフェニックスと同様のものを複製する)

    (作成Notificationはフェニックスで行われる最初のステップコントローラと私は2つのgenserver呼び出しに他の2つの手順を分離したい:

    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) 
        end 
    end 
    
    defmodule ProcessNotification do 
        def handle_cast(:process_to_document, {Notification, notification_id}) do 
        notification = Repo.get(Notification, notification_id) 
        filename = not_important_how_i_parse_body(notification) 
    
        doc = %{}Document |> Document.changeset(%{filename: filename}) |> Repo.insert! 
    
        {:noreply, {Document, document.id}} 
        end 
    
        def handle_cast(:pull_metadata, {Document, document_id}) do 
        document = Repo.get(Document, document_id) 
        original_name = not_important_how_i_pull_the_metadata(document) 
    
        doc = %{}Document |> Document.changeset(%{original_name: original_name}) |> Repo.update! 
    
        {:noreply, {Document, document.id}} 
        end  
    end 
    
    今ここに

    は私の質問です:

    • 私はそれが{Document, id}だ、最初はそれが{Notification, id}た(Genserverの状態を変更しています。 Genserverがいつも同じタイプを期待しているような気がしますか?だから、いつも `{Notification、id}を返さなければならないし、関連からドキュメントを引っ張るべきでしょうか?それともこれは大丈夫ですか?
    • `pid = GenServer.start_link(ProcessNotification、notification)でGenServerを初期化すると、GenserverはStructの状態を保持できます。オブジェクトをマーシャリングするか、この反物質ですか?
    • 実際にキャストからキャストするにはどうすればいいですか。process_to_documentからキャストするとpull_metadataとなります。それとも私はこのようなコントローラでは、これらのスケジュールを設定する必要があります

    例:

    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 
    

    私が間違っている私がやっているかなり確信しているが、私はこれは良いはずどのように任意のアイデアを感謝しています。

  • 答えて

    2

    キャストフォームは簡単です。

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

    しかし、コントローラからGenサーバーを起動しているように見えるので、なぜそうする必要があるのか​​わかりません。私はそれが正しいアプローチだとは思わない。ここで必要と思われるのは、すべての作業を行うためのプロセスを生成することだけです。ここで

    あなたはエラー処理と再試行を処理する場合は、タスクを開始するためにスーパーバイザーを使用することができ

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

    タスクモジュールを使用することができます。

    作成するプロセスの数が気になる場合は、ワーカープールを参照してください。

    ところで、GenServerの状態はかなりシンプルです。初期状態は、init/1から返されるものです。次の状態はあなたがhandle_cast/2または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 
    

    から復帰何ちょうど別handle_xxxを呼び出すことができます前に、完了するまで実行する必要handle_xxx各ことを覚えています。したがって、GenServer.callを同じGenServerに呼び出すことはできません。プロセスがデッドロックされるからです。

    しかし、GenServer.castは非同期なので問題ありません。

    もう1つの方法は、send self(), {:event, data}をGenServerハンドラの中から実行することです。これにより、GenServer上のhandle_info/2ハンドラが実行されます。

    +0

    Re: 'しかし、GenServer.castは非同期なので問題ありません。これは、非同期で別の非同期をキャストして、別の非同期をキャストできるかどうか心配していることです。 '{Notification、123}'から '{Document、234} 'までのステップは、監督者が正しい実行順序を保証するか?どのような 'handle_cast'メソッドも一度だけ呼び出されるべきであると私に聞きます。そしてその実行の中でgenサーバは' handle_info'を使うべきです。 (それは私に 'handle_cast - > handle_info - > handle_info'と同じように感じます。公開メソッドと同様に、プライベートメソッドの束を呼び出す) – equivalent8

    +0

    ...アドバイスありがとう、本当に役立ちます。私はそれが間違っているのを見ていることがわかります:)私はGenServerの章をもう一度見直さなければなりません。 – equivalent8

    関連する問題