$30 off During Our Annual Pro Sale. View Details »

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. Phoenix.PubSub
    の紹介と活用を
    考える
    2021.10.26 fukuoka.ex#47
    :Elixir
    お茶会〜Phoenix
    を学ぼう編

    View Slide

  2. About Me
    @koga1020_

    @koga1020

    koga1020.com
    👨‍💻
    自己紹介
    古賀 祥造(koga1020

    福岡在住のバックエンドエンジニア(最近はマネジメント寄り)
    fukuoka.ex
    管理人
    💡 最近の興味関心
    Elixir
    ・Phoenix
    を使ったWeb
    アプリケーション開発
    マイクロサービスの実現。実装パターンの学習
    ユーザーも開発者もハッピーなプロジェクト・プロダクトマネジメント
    美味しいご飯・美味しいお酒 🍶
    マイホームで快適に過ごすこと 🏠
    キャンプ・アウトドア用品 ⛺

    View Slide

  3. アジェンダ
    PubSub
    パターンについて
    Phoenix.PubSub
    の導入
    PubSub
    を動かしてみる
    ユースケースを考える

    View Slide

  4. PubSub
    パターンについて

    View Slide

  5. PubSub
    パターン
    出版(Publish)-
    購読(Subscribe)
    パターン
    Publisher
    は入力チャネルを介してメッセージを送信
    Subscriber
    のためにメッセージを入力チャネルから出力チャネルにメッセージをコピー
    Subscriber
    は関心のあるメッセージを受け取る
    [1]
    1. https://docs.microsoft.com/ja-jp/azure/architecture/patterns/publisher-subscriber

    View Slide

  6. PubSub
    パターンの利点
    Publisher
    とSubscriber
    を切り離して管理、疎結合にできる
    Publisher
    は、メッセージを送信するのみ
    それをどこの誰がSubscribe
    しているかは関心がない
    Publisher
    の処理を小さくできる(応答性の向上)
    遅延処理やスケジュールされた処理も可能
    詳しくは クラウド設計パターン -
    パブリッシャーとサブスクライバーのパターン 参照

    View Slide

  7. Phoenix.PubSub
    を使ってPubSub
    を実装してみる

    View Slide

  8. 実装イメージ(再掲)
    真ん中のブローカーがPhoenix.PubSub
    が担うイメージ
    1. https://docs.microsoft.com/ja-jp/azure/architecture/patterns/publisher-subscriber
    ↩︎
    [1]

    View Slide

  9. 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

    View Slide

  10. 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

    View Slide

  11. 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

    View Slide

  12. 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

    ]

    View Slide

  13. 実行してみる
    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

    View Slide

  14. どんな使い方が考えられる?

    View Slide

  15. 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]}}

    ...> )

    View Slide

  16. 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
    ` `

    View Slide

  17. パターンの勘所
    あるドメインイベントが発生したときに、そのイベントのデータを利用する

    コンシューマー(=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

    View Slide

  18. PubSub
    パターンが不適切な場合
    https://docs.microsoft.com/ja-jp/azure/architecture/patterns/publisher-subscriber
    より
    作成側アプリケーションの大きく異なる情報を必要とするコンシューマーが、数人だけしかいない場合。
    アプリケーションが、ほぼリアルタイムの、コンシューマーとの対話を必要とする場合。

    View Slide

  19. まとめ
    Phoenix.PubSub
    を使ったPubSub
    パターンの実装について紹介
    プロジェクトを作成しただけでこの環境が整っているのは非常に便利
    設計の勘所は必要そうだが、うまく使えると大きな武器になるかも?

    View Slide