RedisのCluster構成でPubSubを試す


## Redisをクラスタ構成で動かす

[構築メモ](http://qiita.com/toshiyukihina/private/3fc090912ba32f77ac0a)にて。

## 環境

* Ubuntu 14.04

## 実験

`redis-cli`でいろいろ実験してみる。なお、`redis-cli`に、`-c`オプションをつけないと、値の設定や取得でエラーになる。(`-c`はクラスタモード。)
`-c`をつけずに値の設定や取得を行うと、以下のようなエラーになる。

```bash
127.0.0.1:7000> set key1 100
(error) MOVED 9189 127.0.0.1:7001
127.0.0.1:7000> get key1
(error) MOVED 9189 127.0.0.1:7001
```

### GET/SET

`key1`に値`100`を設定してみた。
`key1`は、ハッシュスロット9189に保存されたようだ。Redisが自動的に振り分けて`7001`のノードにリダイレクトしている。

```bash
127.0.0.1:7000> set key1 100
-> Redirected to slot [9189] located at 127.0.0.1:7001
OK
127.0.0.1:7001> get key1
"100"
```

### MGET

今度は、`MGET`で複数の値を取得してみる。
まずは新たに値`key2:200`を追加する。すると、今度は`7000`のノードに保存した。

```bash
127.0.0.1:7001> set key2 200
-> Redirected to slot [4998] located at 127.0.0.1:7000
OK
127.0.0.1:7000> get key2
"200"
```

この時点で、`key1`は`7001`のノードに、`key2`のノードは`7000`のノードに保存されている。この状態で、`MGET`を実行して正常に取得できるか？
実行してみると、スロットを跨いで取得することは出来ない。

```bash
127.0.0.1:7001> mget key1 key2
(error) CROSSSLOT Keys in request don't hash to the same slot
```

では、どのようにすればMGETすることが出来るようになるか？ここで、`ハッシュタグ`の出番。`ハッシュタグ`とは、キーに`{foo}`のようにブラケットで囲んだ文字列を付与することにより、同じハッシュスロットに割り当てる機能である。

では実際にやってみるとどうなるか？以下は、`{tag}`というハッシュタグを、`key1`/`key2`に付与して`MGET`してみる。
正常に動作しているようである。

```bash
127.0.0.1:7001> set {tag}key1 1000
OK
127.0.0.1:7001> get {tag}key1
"1000"
127.0.0.1:7001> set {tag}key2 2000
OK
127.0.0.1:7001> get {tag}key2
"2000"
127.0.0.1:7001> mget {tag}key1 {tag}key2
1) "1000"
2) "2000"
```

では、ハッシュタグ`{tag}`を付与した値が保存されたノードは`7001`であるが、`7000`のノードで、`{tag}`を付与した値をSETするとどうなるか？
`7001`のノードにリダイレクトした後、SETしてくれている。

```
127.0.0.1:7000> set {tag}key3 3000
-> Redirected to slot [8338] located at 127.0.0.1:7001
OK
```

このように、ハッシュタグを付与した値は、同じノードに対してSETしてくれることがわかった。
`MGET`などのように、複数のキーに対して何かを行うようなコマンドを使う場合は、予めハッシュタグを考慮して設計する必要がありそうだ。

### Pub/Sub

#### 同じノードでPub/Sub

まずは簡単に、同一ノードに接続したPublisherとSubscriberで正常に動作するか確認してみる。以下のコマンドを2つターミナルを立ち上げ実行する。

* Subscriber

```bash
127.0.0.1:7000> SUBSCRIBE 'channel:7000'
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:7000"
3) (integer) 1
```

* Publisher

```bash
127.0.0.1:7000> PUBLISH 'channel:7000' 'Hello from 7000'
(integer) 1
```

結果、Subscriberに正常に通知された。

```bash
127.0.0.1:7000> SUBSCRIBE 'channel:7000'
	… (snip) …
1) "message"
2) "channel:7000"
3) "Hello from 7000"
```

#### 異なるノードでPub/Sub

今度は、Publisherを異なるノードに接続し、Publishしてみる。

```bash
127.0.0.1:7001> PUBLISH 'channel:7000' 'Hello from 7001'
(integer) 0
127.0.0.1:7001>
```

結果、Subscriberに正常に通知された。

```bash
127.0.0.1:7000> SUBSCRIBE 'channel:7000'
	… (snip) …
1) "message"
2) "channel:7000"
3) "Hello from 7001"
```

#### PubをSlaveに接続してPub/Sub

今度は、Publisherをスレーブに接続してPublishしてみる。

```bash
127.0.0.1:7004> PUBLISH 'channel:7000' 'Hello from 7004'
(integer) 0
```

結果、Subscriberに正常に通知された。

```bash
127.0.0.1:7000> SUBSCRIBE 'channel:7000'
	… (snip) …
1) "message"
2) "channel:7000"
3) "Hello from 7004"
```

#### アクティブなチャネル一覧の取得

`PUBSUB CHANNELS [pattern]`で、アクティブなチャネル一覧を取得できるが、クラスタ環境で取得可能か確かめる。
Subscriberは、`7000`でチャネル`channel:7000`をSubscribeしている状態である。

```bash
127.0.0.1:7000> SUBSCRIBE 'channel:7000'
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:7000"
3) (integer) 1
```

まずは同じノードから。正常に取得できた。

```bash
127.0.0.1:7000> PUBSUB CHANNELS 'channel:7000'
1) "channel:7000"
```

次に、異なるノード`7001`から。取得できない。
どうやら、`PUBSUB CHANNELS`は、PubとSubが異なるノードの場合、期待通りに動作しないようだ。

```bash
127.0.0.1:7001> PUBSUB CHANNELS 'channel:7000'
(empty list or set)
```

ちなみに、ハッシュタグをつけても無駄である。

#### アクティブなチャネル数の取得

`PUBSUB NUMSUB [channel]`で、アクティブなチャネル数を取得することが出来るが、クラスタ環境で正常に動作するか試す。`PUBSUB CHANNELS`の場合と同様、`7000`のノードでSUBしている状態で試した。

まずは同じノードから。正常に取得できた。

```bash
127.0.0.1:7000> PUBSUB NUMSUB 'channel:7000'
1) "channel:7000"
2) (integer) 1
```

次に、異なるノードから。取得できない。

```bash
127.0.0.1:7001> PUBSUB NUMSUB 'channel:7000'
1) "channel:7000"
2) (integer) 0
```

ちなみに、ハッシュタグをつけても無駄である。