PhoenixでRedisを使わないPubSubでスケール その1


PhoenixでRedisを使用してPubSubを行う、記事はいくつかありますが、
PhoenixはRedisを使用せずとも標準でPG2を使用したPubSubの機構があります。
今回はそれを使用し、PubSubを試します。

PG2の説明とかいいから早くPhoenix見せろよという方は[こちら](http://qiita.com/tamai/items/41b14b636341f3839f01)

## Phoenixとは

Elixir製のWAF.
そこそこ速い。

Rails5で導入されるActionCableのような、websocketの機能がある。

## PubSubとは

websocketで接続しているクライアントに対して、pushを送る機能のこと。

Phoenixで言うと、
channelにjoin(subscribe)しているクライアントに対して、
push(publish)を行う機能。

## PG2モジュールとは
ProcessGroup2の略。
PGは非推奨。

名前の通り、プロセスのグループを生成してプロセスのリストを持つことが出来る。
特徴として、ErlangVMのNode間でリストをグルーバルにリストが共有される。

## 用途

Node間でプロセスを共有できると言うことは、
クラスターを構成した場合に、クラスター間で別NodeのプロセスのメソッドをRPCで呼べるということ。

PhoenixではwebsocketのPubSubとして使用している。
同じchannelにjoin(subscribe)しているクライアントに対して、
別ノードに接続しているクライアントでも、publishが出来るようになっている。

## まずはPG2モジュールを使ってみる

まず、Node実行時に`name`を指定する必要がある。
実行すると、name@HostでNodeが起動する。

```
vagrant-ubuntu-precise-32% iex --sname bar
Erlang/OTP 18 [erts-7.2] [source] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(bar@vagrant-ubuntu-precise-32)1>
```

同様に別ターミナルでNodeを実行する。

```
vagrant-ubuntu-precise-32% iex --sname foo
Erlang/OTP 18 [erts-7.2] [source] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(foo@vagrant-ubuntu-precise-32)1>
```

これで、2つのNodeが実行中になったがまだお互いのNodeはお互いが見えていない。
この状態で、PG2を使ってみる。

```
iex(bar@vagrant-ubuntu-precise-32)2> :pg2.create(:process_group)
:ok
iex(bar@vagrant-ubuntu-precise-32)5> :pg2.get_members(:process_group)
[]
```

```
iex(foo@vagrant-ubuntu-precise-32)2> :pg2.get_members(:process_group)
{:error, {:no_such_group, :process_group}}
```

fooからbarが見えていない為、エラーになる。

Node間の接続をする必要がある。

```
iex(bar@vagrant-ubuntu-precise-32)6> :net_kernel.connect_node(:"foo@vagrant-ubuntu-precise-32")
true
```

```
iex(foo@vagrant-ubuntu-precise-32)3> :pg2.get_members(:process_group)
[]
```

これでお互いのNodeが接続され、pg2が共有されました。

ですが、実際に運用する上でいちいち起動したらこれらのコマンドを実行するなんて嫌ですよね。

設定ファイルを用意することで、自動的にNodeの接続を行うことが出来ます。

iexコマンドを実行するディレクトリに以下のファイルを作成します。

```sys.config
[
  {
    kernel,
    [
      {sync_nodes_optional, ['foo@127.0.0.1', 'bar@127.0.0.1']},
      {sync_nodes_timeout, 10000},
      {start_pg2, true}
    ]
  }
].
```

以下のように、実行時にerlのパラメータとしてsys.configを指定して実行する。

```
vagrant-ubuntu-precise-32% iex --name bar@127.0.0.1 --erl "-config sys.config"
Erlang/OTP 18 [erts-7.2] [source] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(bar@127.0.0.1)1>
```

```
vagrant-ubuntu-precise-32% iex --name foo@127.0.0.1 --erl "-config sys.config"
Erlang/OTP 18 [erts-7.2] [source] [async-threads:10] [hipe] [kernel-poll:false]

Interactive Elixir (1.2.0) - press Ctrl+C to exit (type h() ENTER for help)
iex(foo@127.0.0.1)1>
```

PG2を使ってみます。

```
iex(foo@127.0.0.1)1> :pg2.create(:process_group)
:ok
```

```
iex(bar@127.0.0.1)1> :pg2.get_members(:process_group)
[]
```

うまくいってます。

プロセスの共有を試します。

```
iex(bar@127.0.0.1)3> pid = spawn fn -> Enum.map(Stream.cycle([1, 2, 3]), fn(x) -> :timer.sleep(1000) end) end
#PID<0.76.0>
iex(bar@127.0.0.1)4> :pg2.join(:process_group,pid)
:ok
```

```
iex(foo@127.0.0.1)2> :pg2.get_members(:process_group)
[#PID<3090.76.0>]
```

うまくいってます。
barで生成して、process_groupに追加したPIDがfooからも見えています。

PIDの見え方が違いますが、以下のようになっています。

`#PID<ノードNo. プロセスNo. プロセスNoが最大を超えた場合のインクリメント>`


次回はPhoenixを使って実際にPG2のPubSubを使います。

[その2](http://qiita.com/tamai/items/41b14b636341f3839f01)