Share
Embed
Transcript
19 slides
-
About Me @koga1020_ @koga1020 koga1020.com 👨💻 自己紹介 古賀 祥造(koga1020 )
福岡在住のバックエンドエンジニア(最近はマネジメント寄り) fukuoka.ex 管理人 💡 最近の興味関心 Elixir ・Phoenix を使ったWeb アプリケーション開発 マイクロサービスの実現。実装パターンの学習 ユーザーも開発者もハッピーなプロジェクト・プロダクトマネジメント 美味しいご飯・美味しいお酒 🍶 マイホームで快適に過ごすこと 🏠 キャンプ・アウトドア用品 ⛺ -
PubSub パターン 出版(Publish)- 購読(Subscribe) パターン Publisher は入力チャネルを介してメッセージを送信 Subscriber のためにメッセージを入力チャネルから出力チャネルにメッセージをコピー Subscriber
は関心のあるメッセージを受け取る [1] 1. https://docs.microsoft.com/ja-jp/azure/architecture/patterns/publisher-subscriber -
PubSub パターンの利点 Publisher とSubscriber を切り離して管理、疎結合にできる Publisher は、メッセージを送信するのみ それをどこの誰がSubscribe しているかは関心がない Publisher
の処理を小さくできる(応答性の向上) 遅延処理やスケジュールされた処理も可能 詳しくは クラウド設計パターン - パブリッシャーとサブスクライバーのパターン 参照 -
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 -
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 -
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 -
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 ] -
実行してみる 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 -
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]}} ...> ) -
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 ` ` -
パターンの勘所 あるドメインイベントが発生したときに、そのイベントのデータを利用する コンシューマー(=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