Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Phoenix.PubSubの紹介と活用を考える

shozo koga
October 26, 2021

 Phoenix.PubSubの紹介と活用を考える

2021/10/26 fukuoka.ex#47:Elixirお茶会 での登壇資料です

https://fukuokaex.connpass.com/event/227466/

shozo koga

October 26, 2021
Tweet

More Decks by shozo koga

Other Decks in Programming

Transcript

  1. About Me @koga1020_ @koga1020 koga1020.com 👨‍💻 自己紹介 古賀 祥造(koga1020 )

    福岡在住のバックエンドエンジニア(最近はマネジメント寄り) fukuoka.ex 管理人 💡 最近の興味関心 Elixir ・Phoenix を使ったWeb アプリケーション開発 マイクロサービスの実現。実装パターンの学習 ユーザーも開発者もハッピーなプロジェクト・プロダクトマネジメント 美味しいご飯・美味しいお酒 🍶 マイホームで快適に過ごすこと 🏠 キャンプ・アウトドア用品 ⛺
  2. PubSub パターンの利点 Publisher とSubscriber を切り離して管理、疎結合にできる Publisher は、メッセージを送信するのみ それをどこの誰がSubscribe しているかは関心がない Publisher

    の処理を小さくできる(応答性の向上) 遅延処理やスケジュールされた処理も可能 詳しくは クラウド設計パターン - パブリッシャーとサブスクライバーのパターン 参照
  3. Phoenix.PubSub の導入 project を作成した段階でsupervision tree に追加されている # Start the PubSub

    system {Phoenix.PubSub, name: Sample.PubSub}, # lib/sample/application.ex @impl true def start(_type, _args) do children = [ # Start the Ecto repository Sample.Repo, # Start the Telemetry supervisor SampleWeb.Telemetry, # Start the Endpoint (http/https) SampleWeb.Endpoint # Start a worker by calling: Sample.Worker.start_link(arg) # {Sample.Worker, arg} ] # See https://hexdocs.pm/elixir/Supervisor.html # for other strategies and supported options opts = [strategy: :one_for_one, name: Sample.Supervisor] Supervisor.start_link(children, opts) end
  4. Subscriber を追加する "user_event" をsubsribe するSubsriber1 を作成 IO.puts "get message: #{inspect(message)}

    at #{__MODULE__}" # lib/sample/subscriber1.ex defmodule Sample.Subscriber1 do use GenServer def init(_) do Phoenix.PubSub.subscribe(Sample.PubSub, "user_event") {:ok, nil} end def start_link(opts) do GenServer.start_link(__MODULE__, opts) end def handle_info(message, state) do {:noreply, state} end end
  5. Subscriber を追加する product_event をsubsribe するSubscriber2 を作成 Phoenix.PubSub.subscribe(Sample.PubSub, "product_event") # lib/sample/subscriber2.ex

    defmodule Sample.Subscriber2 do use GenServer def init(_) do {:ok, nil} end def start_link(opts) do GenServer.start_link(__MODULE__, opts) end def handle_info(message, state) do IO.puts "get message: #{inspect(message)} at #{__MODULE__}" {:noreply, state} end end
  6. application tree に追加する def start(_type, _args) do children = [

    # Start the Ecto repository Sample.Repo, # Start the Telemetry supervisor SampleWeb.Telemetry, # Start the PubSub system {Phoenix.PubSub, name: Sample.PubSub}, # Start the Endpoint (http/https) - SampleWeb.Endpoint + SampleWeb.Endpoint, # Start a worker by calling: Sample.Worker.start_link(arg) # {Sample.Worker, arg} + Sample.Subscriber1, + Sample.Subscriber2 ]
  7. 実行してみる user_event にはSubscriber1 が、product_event にはSubscriber2 がそれぞれ応答していることがわかる $ iex -S mix

    iex(2)> Phoenix.PubSub.broadcast(Sample.PubSub, "product_event", %{id: 456}) :ok get message: %{id: 456} at Elixir.Sample.Subscriber2 iex(1)> Phoenix.PubSub.broadcast(Sample.PubSub, "user_event", %{id: 123}) :ok get message: %{id: 123} at Elixir.Sample.Subscriber1
  8. Webhook e.g. GitHub をイメージしてみる branch が削除されたら、X に通知 issue にコメントがついたら、X とY

    に通知 …etc こんな感じで実装できそう? # Subsriber iex> PubSub.subscribe(Sample.PubSub, "repo_event:koga1020") # Publisher iex> PubSub.broadcast( ...> Sample.PubSub, ...> "repo_event:koga1020", ...> {:branch, :deleted, %{repo: "koga1020/sample", branch: "feature1", deleted_at: ~U[2021-10-24 14:06:23.666118Z]}} ...> )
  9. CallBack のように使う ActiveRecord でいう after_update っぽく使う 「ユーザーデータが更新された」というイベントを配信する 保存後に追加で何を行うかはPublisher は関与しない def

    update_user(%User{} = user, attrs) do user |> User.changeset(attrs) |> Repo.update() |> tap( {:ok, user} -> Phoenix.PubSub.broadcast(Sample.PubSub, "user:#{user.id}", {:user_update}, user) error -> error end) end ` `
  10. パターンの勘所 あるドメインイベントが発生したときに、そのイベントのデータを利用する コンシューマー(=Subscriber) が多い場合に有用 Channels のユースケース=PubSub のユースケースと解釈しても良さげ hexdocs の例: Chat

    rooms and APIs for messaging apps Breaking news, like "a goal was scored" or "an earthquake is coming" Tracking trains, trucks, or race participants on a map Events in multiplayer games Monitoring sensors and controlling lights Notifying a browser that a page’s CSS or JavaScript has changed (this is handy in development) [1] 1. https://hexdocs.pm/phoenix/channels.html