技術メモ

プログラミングとか電子工作とか

Phoenix入門 (第10章 Channels その1)

f:id:ysmn_deus:20190219130922p:plain

どうも、靖宗です。 実は個人的に一番知りたかった項目で、Channelに関してです。
この章も少々長くなりそうなので2記事に分割予定です。

Channels

PhoenixのChannelは何百万のクライアントを接続するリアルタイムコミュニケーション機能です。ある意味Phoenixのコア技術と勝手に感じております(Elixir自体がたくさんのリクエストを捌くイメージなので)。
想定される使い方としては

  • チャットルームとメッセージを送ったりするAPIの提供
  • 速報などのニュース(地震速報とか)
  • 地図上でのトラッキング(電車とかトラックとか)
  • マルチユーザのゲームに於けるイベント
  • センサーのモニタリングや照明のコントロール
  • CSSJavaScriptが変更されたときの通知(開発時など)

などが挙げられております。

チャンネルにクライアントが接続して、トピックにサブスクライブすると、Phoenixサーバーが他のクライアントに配信する、という仕組みだそうです。
公式にAAみたいなのがはっついてました。たぶんスマホとかでみると崩れる。

                                                                  +----------------+
                                                     +--Topic X-->| Mobile Client  |
                                                     |            +----------------+
                              +-------------------+  |
+----------------+            |                   |  |            +----------------+
| Browser Client |--Topic X-->| Phoenix Server(s) |--+--Topic X-->| Desktop Client |
+----------------+            |                   |  |            +----------------+
                              +-------------------+  |
                                                     |            +----------------+
                                                     +--Topic X-->|   IoT Client   |
                                                                  +----------------+

言うは易く行うは難しな気はしますが、ソースコードがあればできないことはない。はず・・・(´・ω:;.:...

ちなみに、様々なクライアントを想定しているだけあって様々なライブラリが整備されているようです。

hexdocs.pm

The Moving Parts

Overview

おそらく簡単に使えるんでしょうが、実際の挙動はやや複雑です(とはいっても概念だけなのでそこまで難しく無さそう。)

クライアントがPhoenix Serverと通信を開始する際に1クライアント1トピック(チャットルームの様なイメージ?)につき通信用のプロセスを生成します。この際に通信で使われる%Phoenix.Socket(いわゆる%Plug.Connのようなもの?)が初期化されるそうです。詳しくはたぶん後で出てくるのでそのときに確認しましょう。

f:id:ysmn_deus:20190314121243p:plain

一度通信が成立すると、クライアントとトピックで一意に決まるプロセス(図で言う「Sending Client, Topic 1」)へのルーティングが確立し、クライアントからの通信がきちんとチャンネルのサーバーへ繋がるようになります。
通信が確立した後にブロードキャストのメッセージを発行すると、一度Local PubSubというプロセス?を経て同じトピックに繋がっているサーバーへ配信されるようです。

f:id:ysmn_deus:20190314121334p:plain

Local PubSubに送られたメッセージは、同一トピックに接続しているプロセス全部に送信され、無事メッセージが各クライアントに届きます。

f:id:ysmn_deus:20190314121516p:plain

同一ノードはこのような仕組みになっていますが、別のノードはPubSub間で通信があった後に各クライアントプロセスへ配信という仕組みになっているそうです。

f:id:ysmn_deus:20190314121636p:plain

Endpoint

Endpointの章で若干触りましたが、Endpointにsocketの設定を書く箇所があります。

defmodule HelloPhoenixWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :hello_phoenix

  socket "/socket", HelloPhoenixWeb.UserSocket,
    websocket: true,
    longpoll: false
...

URIの設定やwebscoketを利用するかロングポーリングを使用するかの設定です。

Socket Handlers

チャンネルの接続がセットアップされる度に、上記で言うHelloPhoenixWeb.UserSocketがソケットハンドラとして呼び出されるようです。
おそらく初期化関数や認証などはHelloPhoenixWeb.UserSocketに書くのでしょう。

Channel Routes

チャンネルのルーティングもHelloPhoenixWeb.UserSocketに書くそうです。Phoenixプロジェクトを生成して最初から書かれているコメントアウトされている箇所にも書かれています。

defmodule HelloPhoenixWeb.UserSocket do
  use Phoenix.Socket

  ## Channels
  # channel "room:*", HelloPhoenixWeb.RoomChannel
...

channel "room:*", HelloPhoenixWeb.RoomChannelroom:lobbyroom:123といったトピックへのルートを生成できるようになるようです。

Channels

上記で言うHelloPhoenixWeb.RoomChannelでしょうか。
ソケット通信でないHTTPリクエストで言うコントローラに該当するようなモジュールのようです。ただし、コントローラは基本的に受け身ですが、Channelは双方向という違いはあります。
Channelはjoin/3terminate/2handle_in/3handle_out/3という4つの関数を実装して作って行くようです。

Topics

トピックはChannel Routesでワイルドカード指定した箇所が該当します。
基本的にroom:123の要にレコードIDを付与していく形で使う事が多いそうですが、名前を付けるのかID割り振っていくのかはケースバイケースだと思います。

Messages

Phoenix.Socket.Messageというモジュールが存在し、これを利用してトピックにメッセージなどを発行するのでしょう。
Channelの通信で使うメッセージの構造を定義しているようです。

  • topic - トピック名。"messages"とか"messages:123"とか。
  • event - イベント名。"phx_join"とか。よくわかんないので保留。
  • payload - メッセージのペイロード。たぶんメッセージ本体を意味している。"おはよう!"とか。
  • ref - 一意に決まる文字列?よくわかんない。

サンプルとかもあるのでよく分からない箇所は後回しにしましょう。

PubSub

Overviewのところで出てやつ。図からも分かるとおり内部実装で稼働してる箇所なので直接使う事はほぼないらしい。ただし設定することはあるかもとのこと。

PubSubはPhoenix.PubSubというモジュールで構成されていて、こいつはGenServerとかいろんなものの組み合わせだそうで。
ノード間通信も面倒みてくれるのですが、ノード間の通信はデフォルトではPhoenix.PubSub.PG2というおそらく内部実装がErlangのpg2でできてるモジュールを利用しているそうです。
なんらかの原因(古いElixirやErlangのバージョンを利用しないといけないケース?)でpg2などが利用できない場合はRedisを利用する設定もあるそうです。詳しくはドキュメントを参照とのこと。

hexdocs.pm

もしRedisの方が早い環境などあったらチューニングでRedisを選択する、なんてことも想定しといたほうが良いんでしょうか?
ただ、デフォルトでPG2を指定されているなら基本的にはPG2をチョイスする方が無難な気はします。

Client Libraries

Phoenixを利用するライブラリが公式+サードパーティーから提供されています。フルスクラッチも勉強にはなると思いますが基本的にはこの辺を利用することになるでしょう。

Official

hexdocs.pm

ギットハブ

github.com

3rd Party

次回は実際にサンプルを動かしていく箇所です。たぶん。