sangoの有料プラン使ってみました


http://sango.shiguredo.jp/

時雨堂のMQTTブローカーサービスsango。有料プランのスタンダードプランのサービスインは10/30だったのに、最近まで仕事で死んでたので、ざっくり使ってみたところだけレポートです。追記、、、したいけど時間作らないと。

## Changelog (majorなもの)

+ 2014/11/10 更新 : mosquitto-1.3.5のクライアントでTLS接続すると--insecureのオプションが無いとエラーになる件は、sangoがワイルドカードのサーバー証明書を送信しているのをmosquitto側が正しく認識できていない（ホスト名との完全一致のみ許していた）せいでした。ワイルドカードの処理をcURLなどを参考に追加したところ、sangoと通信できることがわかりました。
+ 2014/11/7 10:25 更新 : 50KBを越えるメッセージの扱いについて情報をいただきましたので、更新しました。
+ 2014/11/6 14:00 追記 : mosquittoのクライアントでの--insecure指定時のエラーは、サーバー側の証明書のタイプにmosquitto側が対応していないようでした。現状では--insecureを付けてテストを継続しています。
+ 2014/11/6 追記：TLSのエラーの件でサポートしていただいてます。フォロー追記予定です。なお、GoやPythonで今すぐTLS試したい、という方は下記を参考になさって下さい。
  - Go言語 : http://qiita.com/r_rudi/items/994adea7c1551b9d3748
  - Python : http://qiita.com/voluntas/items/76f013acdc42fb492b0c


# 無料プランと何が違うか

公式にはこちらをご参照ください：https://sango.shiguredo.jp/#plan
けっこういろいろ違います。

で、今回自分が注目したのは以下の点でした。

+ クラスタ組んである
+ TLSが使える
	+ Secure Websocketが使える
+ 容量拡大
	+ 120,000メッセージまで送れる(無料プランだと50,000まで)
	+ メッセージサイズが50KBまで使える(MQTTのゲートウェイで集約してから投げるとか？）



## クラスタ組んである

プラン登録すると、Broker AとBの２台のエンドポイントが見られるようになる。

で、AとBそれぞれにsubscriberのclientを接続しておいて、ブローカーAにメッセージを投げると、AとBの両方のsubscriberにメッセージが届く。ブローカーBに投げても、AとBの両方に届く。

これで冗長系を組める、、と思うのだけど、障害検知をどうするか、とか、通常はround robinで負荷分散する意味があるか、とか。いろいろあるが、今後の検討課題。

## TLS使える

すでにGolangやPythondでの接続例はあるので、mosquittoでやってみました。

サーバ証明書のタイプによっては--insecureの指定は不可欠のようです。ソース追ってます。

### client側の準備

参照：http://mosquitto.org/man/mosquitto-tls-7.html


+ clientの側も証明書作る。（これはサーバー側で認証する場合。sangoでは不要っぽいです）
	- まず自前CA作る
	- 秘密鍵作る
	- CSR作る（subjectは自前CAと変えておくのがいいらしい)
	- CSRを自前CAの証明書で署名してclientの証明書の出来上がり

#### 作例

```{sh}
$ openssl req -new -x509 -days 3650 -extensions v3_ca -keyout ca.key -out ca.crt
$ openssl genrsa -des3 -out client.key 2048
$ openssl req -out client.csr -key client.key -new
$ openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 3650
```

### sango側の証明書
サービスを契約すると、ダッシュボードからダウンロードできます。ECC(楕円曲線暗号)使ってあって感動（何がw

### ではつないでみます

subscriberはまずnon-TLSで

```
$ mosquitto_sub -h サーバー名 -p ポート番号  -t "トピックのルート/#" -u "ユーザー名" -P "パスワード"
```

TLS使ってpublishします。最初は-d (debug)オプション付き（追記：--certと--keyは無くても繋がりました）

```
$ mosquitto_pub -h サーバー名 -p ポート番号 -t "トピックのルート/tlstest" -u "ユーザー名" -P "パスワード" --cafile sangoの証明書 --cert クライアント側の証明書  --key クライアント側の鍵ファイル --insecure -d -m "メッセージの文字列"

```

これで、TLSでpublishしてnon-TLSでsubscriber側がメッセージを受け取れることがわかりました。

今度はsubscriber側もTLSで繫いでみます（追記：この場合も、--certと--keyは無くても繋がりました）

```
$ mosquitto_sub -h サーバー名 -p ポート番号 -t "トピックのルート/tlstest" -u "ユーザー名" -P "パスワード" --cafile sangoの証明書 --cert クライアント側の証明書  --key クライアント側の鍵ファイル --insecure -d

```

TLSでpubしてTLSでsub、もできました。
ただ、今のところ、```--insecure```はずすと

```
error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed
```
でちゃうのが悔しい。せめてもっと詳しいエラー出せるようにソース追っかけてます。

#### (2014/11/10追記) ワイルドカードサーバー証明書の処理を追加したらうまくいきました

mosquitto-1.3.5のソースをとってきて、ワイルドカードサーバー証明書の処理のパッチをしたところ、--insecureのオプション無しで通信できることを確認できました。
パッチのデバッグの過程で、sangoがワイルドカードのサーバー証明書を送ってきていることも確認できました。（これはtcpdumpでも確認できていました）

まだ、パッチは他所からもらってきたコードの寄せ集めなので、公開は問題がありますが、もしご自分でやりたい場合には、mosquitto-1.3.5のソースのうち、```lib/tls-mosq.c```の```_mosquitto_verify_certificate_hostname()```関数内の

```
        if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){
                name[sizeof(name) - 1] = '\0';
                if (!strcasecmp(name, hostname)) return 1;
        }
```
のstrcasecmp関数をワイルドカードのnameが来ても大丈夫な関数に変更すればよいかと思います。（他の箇所では完全一致が必要なので、strcasecmpを変更することはおすすめしません）

## 容量拡大
これまで6KBまでのメッセージが送ることができた。Githubのrepositoryへのcommitイベントを通知してもらうとき、データの量が４KBくらいだったので、ちょうど良かった。

### 50KBを120,000回送れるとなるとどうなるか？

１ヶ月は3600x24x30=2592000秒なので、120Kメッセージということは21秒に１メッセージ。
これがサンプリング間隔の目安になると思った。

５分間隔のサンプリングレートでも良いデータというものはけっこうある、というか、サーバー監視なんかではそもそもの観測値（loadaverageとか）が５分間の平均値だったりする。

50KBという容量は、１台のサーバーというより、１つのサービスを構成するサーバー群の観測データを１つにまとめておくことも可能なんじゃないかと思う。

すると、結果として１５サービスくらいの監視の通信インフラとして機能させることができる。

これが月500円だとすると、まずまず。（実際にはfluentdやkibanaを動かすサーバーの費用もかかる）かな。
インフラ提供サービスを運営するとなれば、サーバー立ち上げるライセンス買うことになると思うけど、このラインより安くあげるのは、営業費用も考えるとなかなか大変。


# 気になっていたこと（TODOsから変更)

## 50KBを越えるとどういうエラーになるのか？

データを集約するときに、データ量の見積もりを誤ったらどうなるか？結構心配なのでテストしてみたい。、、と思っていましたが、コメントを頂いた通り、エラーではなく接続断になるようです。　送信する前にチェック大事ですね。