« 2013年12月 | メイン | 2014年2月 »

2014年1月

2014年1月22日 (水)

git submoduleのすすめ

またしてもWebSocketのデモアプリを作ることになりました。

それほど凝ったものを作るつもりはないので、さきに作った汎用のRedisService(のプロト)を再利用すれば簡単にできる見込みです。(^^v

が、ここでどうしたものかと考えるのはどのように再利用するか?です。

新しいプロジェクトにソースをコピーしても良いんですが、それはあまりに芸がない。。。

ちゃんとやるならプラグイン化するのが良いんでしょうが、まだプロトでしかない(=コードの改修がガンガン発生する)段階でプラグイン化しても、外側のアプリを作りつつプラグインを修正しては入れ直し。。。とやるのはかなり面倒です。。。。(--

そんな折、Heroku MeetupのLTネタを探してDevCenterをあさっていたらふとこんな文書が目に留まりました。

https://devcenter.heroku.com/articles/git-submodules

Herokuではgit submoduleが使えるらしい。。。

git submoduleって???(初耳)


★ git submoduleとは?

ググりましょう。(^^;
日本語のサイトだけでも十分な情報が得られます。

要するにあるgitプロジェクトの任意のディレクトリに別のgitプロジェクトをまるごと取り込む仕組みです。

コマンドの使い方の説明は他のサイトに譲るとしてここでは最初の課題(RedisServiceをどのように再利用するか?)に対して、どの程度有効であるかを検証します。


★ とりあえずsubmoduleプロジェクトを作ってみる

今回作成するのはPlay2で使用する(広義の)プラグインです。
submoduleとして取り込む場合はappディレクトリに任意のディレクトリを作成してそこに取り込むことになります。

なので、submoduleのプロジェクトはそれにあわせたディレクトリ構成になっていなければならない訳ですが、幸いにもScalaにはpackageとディレクトリ構成が同じでなければならないという制限はありません。

またREADME.md等コンパイルと無関係なファイルがapp以下にあってもPlayは単純にそれを無視するだけです。

これを踏まえて、

パッケージ名: flect.redis
ファイル構成:
- README.md
- src/flect/redis/RedisService.scala


という構成でsubmoduleプロジェクトを作成しました。

https://github.com/shunjikonishi/play-redis-submodule

新規のPlayプロジェクトでこれをsubmoduleとして使用する方法はREADMEに記載した通りです。
app/redisディレクトリの下にsrcというディレクトリが現れるのはあまり一般的なディレクトリ構成ではありませんが、Playでは実行に問題ありません。
(もちろんsubmodule側でこれとは異なるディレクトリ構成を採用してもOKです。)


★ 何が嬉しいの?

ずばり外側のアプリを作りながら内側のライブラリを改修できることです。

submoduleのディレクトリはそのディレクトリに入ってしまえば通常のgitのディレクトリと変わりありません。
なので、普段と同じようにそこで修正、commit、pushなどを行うことができます。

外側のgitではsubmoduleのコミットIDしか管理していないので内側のsubmoduleを更新した場合は、外側でそのsubmoduleのadd/commitが必要になります。

ネット上ではここでsubmoduleと外側のgitが連動しないのがわかりにくい、という意見もいくつか目にしましたがこれはむしろ好都合。。。というよりも完全に意図的にこういう仕様にしていると思います。

これはさらに別のプロジェクトで同じsubmoduleを使うケースを考えてみるとわかります。

別のプロジェクトに同じsubmoduleを追加して、そこでもsubmodule側になんらかの修正を加えたくなったとします。
この場合修正はどこで行うべきでしょうか?

答えはもちろんそのプロジェクトのディレクトリの中で修正してしまえば良いのです。gitのリポジトリはどこが正ということはないので、どこで修正したって構わない訳です。

ここでの修正は既存の別プロジェクトには影響を与えませんし、必要ならばそっちでもpullすればOKです。


★ どこで使えそう?

今回はPlay/Scalaで使いましたが、考え方自体はシンプルなので他のフレームワークでも応用可能です。(Javaみたいにパッケージ名の制約があるものはちょっと悩ましいですけど。。。)

ですが、本命はなんといってもJavaScriptですね。既存のライブラリに手を加えて使うこともちょいちょいありますが、それがそのままgitリポジトリであるならプルリクしようかという気にもなるしバージョンアップも楽です。
書いてて気がついたけど、プルリクするような汎用的な修正じゃない場合でもバージョンアップと同時にマージできちゃいますね。素晴らしすぎる。(^^v

他にも社内ユース専用のちょっとした小物ライブラリなんかもこっちの管理方法の方がマッチすることが多いんじゃないでしょうか。

正直SBTにはかなりうんざりしているので、これは新しい時代の依存性管理の仕方としてアリなんじゃないかとさえ思います。

2014年1月17日 (金)

Heroku Meetup #11

昨夜はHeroku Meetup #11。

新年会と銘打っただけのことはあってご飯もお酒もいつもより増量されてて凄い楽しかったです。

日本酒持ってうろうろしている女子が複数いたのには笑った。(^^;

自分のやったLTの資料はこちら

http://www.slideshare.net/shunjikonishi/heroku-tips1

小ネタ集です。(^^;

LTは予定通り途中で切られたわけだが、他の人見てると最後までやりきる人も多くて、最初から全部喋る気のなかった自分をちょっと反省した。(--

また、小ネタがたまったらLTにもトライしようと思います。

2014年1月14日 (火)

HerokuでスケーラブルWebSocket

前回の続きです。
前回Play+Redisでスケーラブルに動くWebSocketアプリケーションが完成したので今回はそれを実際にHeroku上で動かしてみます。

といってもCLIで「heroku labs:enable websockets」を叩く以外は特に変わったことをする必要はありません。
この辺は以前にも書いたのでそちらも参照してください。

http://blog.flect.co.jp/labo/2013/12/herokuwebsocket-aad6.html

実際のところはデプロイすれば特に何の問題もなく動きます


★HerokuでWebSocketアプリを動かす場合の留意点

Herokuの特性と照らしてWebSocketアプリを動かす時に気をつけなければならない点がいくつかあります。
ざっと思いつくところは以下です。

  1. 通信が無い状態が30秒続くと接続が切れる
  2. マルチDyno対応
  3. デイリー再起動がある


1番目の30秒ルールへの対応はクライアントまたはサーバから定期的にPing的なメッセージを送信することで回避できます。

2番目のマルチDyno対応は通常のスケールアウトと何ら変わるところがないのでRedisを使うことでクリアです。

やっかいなのは最後、再起動対応です。
HerokuのDynoManagerはWebSocket接続がある場合でも容赦なく再起動をかけてくると思います。(試してませんが。。。)
これはHerokuの内部仕様なのでユーザ側では制御不能です。

以下、これに対してどういう対策が可能かを考えてみます。


★接続遮断時のクライアントの動作

Dynoの再起動をローカルでシュミレートする方法は単純に起動中のPlayframeworkをCTRL+Dで止めるだけです。

この時にクライアント側のJavaScriptがどのような動作になるかをまずは検証します。検証の方法は単純にWebSocketの onopen/onerror/oncloseイベントにconsole.logを仕込むだけです。

検証に使用したブラウザはChrome, Firefox, IE10です。
サーバ強制終了時に発生するイベントとその順序は以下です。

  • Chrome: onclose
  • Firefox: onclose
  • IE10: onerror, onclose


IEのみonerrorが発生していますが、これは多分IEの問題はまたPlayのWebSocket実装の問題です。強制終了に限らずサーバ側からWebSocketを切断(EnumeratorでInput.EOFを送信)した場合、常に発生するので。

ざっと検索した感じ(Playの話ではないですが)この辺りが関連トピックかなと思いますが詳しくは調査していません。

onerrorイベントは発生していますが、例外でスクリプトが停止する等の実害はないのでここでは気にせず話を進めることにします。

重要なのは強制終了による切断であってもクライアント側からは単にサーバからWebSocketを切断されただけに見えるという点です。(PlayはShutdownHookでクリーンアップを行っているのでそれも関係あるかもしれませんが)


★oncloseで再接続してみる

安易ですがoncloseイベントが発生した場合に再接続してみることにします。

ローカルのテストでは手動で再起動を行っている関係で、切断直後はまだサーバが起動していません(※1)。当然接続はエラーになりますが、その場合以下の順序でイベントが発生しました。

  • Chrome: onerror, onclose
  • Firefox: onerror, onclose
  • IE10: onerror, onclose


すべてのブラウザでonerrorとoncloseが発生しています。
onopenとoncloseって必ず対で発生するとは限らないんですね。。。イマイチこれが実装依存なのか正規の仕様なのかがW3Cの仕様見てもわからないんですが、とりあえずoncloseを拾って時間をおいて数回再接続を試みるような実装はできそうです。(※2)


※1 HerokuでマルチDynoで動かしている場合は再接続は別のDynoにつながるのでエラーなしで再接続できます。

※2 console.logは見てませんがiOS、Android(のChrome)でもリトライでの再接続ができているので、やはり接続失敗時にoncloseは発生しています。


★Window#onunloadでのoncloseイベントのクリア

ここまでの内容を実装して動かしてみるとローカル、Herokuともにいい感じに再接続ができています。(Herokuでの再接続のテストはCLIで「heroku ps:restart web.2」のようにDyno単位での再起動を行います。)

あともう一つ注意が必要なのはWebSocket#oncloseイベントはブラウザでページを閉じた場合などにも発生するのでその場合には再接続を行ってはいけないという点です。

ここではWindow#onunloadイベントでoncloseをクリアしていますが、ページ遷移以外のWebSocket切断があるアプリでは他にも制御が必要になるでしょう。

最終的なクライアント側のコードはこうなりました。(タグをつけようか迷いましたがmasterの最新です。)

https://github.com/shunjikonishi/websocketchat-redis/blob/master/app/views/chatRoom.scala.html

★まとめ

Dyno再起動に対応した再接続はがんばればできそうです。
しかし、ここをがんばる位なら素直にHeroku以外のプラットフォームを選んだ方が良いように思います。(^^;;;

しかししかし、EC2を使うとしてその場合にサーバの異常終了による予期しない切断の可能性を考慮しなくて良いかどうかは疑問です。
個人的な感覚としてはWebアプリはエラーが発生したとしてもリロードで回復するなら許せるんですが、かなり高い確率でそれもなんとかせぇと言われそうな気がしますね。。。(--

□□□□


まぁ必要に迫られたら考えますが、ふとこんな言い回しを思いつきました。

パフォーマンスもアベイラビリティも金で買ってください。
パフォーマンスは性能正比例で性能2倍になれば価格も2倍ですが、アベイラビリティは1%向上する毎に価格は10倍です!


昔友人が言っていた言葉の焼き直しですが、あながち間違ってないと思いますね。(^^;

2014年1月10日 (金)

PlayとRedisでスケーラブルWebSocket(実装編)

前回の続きです。

実際にPlayとRedisでWebSocketアプリケーションを作成したコードサンプルを示します。


★題材

今回の調査の途中でドンピシャのサンプルを見つけています。

http://www.ryantanner.org/2013/03/using-play-iteratees-and-enumerators.html

PlayにバンドルされているサンプルのチャットをRedis対応したサンプルアプリです。
接続情報を環境変数から取ってるふりして実はlocalhost固定だったとか、チャットルームのメンバリストをローカル管理してるから厳密にはスケーラブルになってないとか、手を入れ始めるといろいろ気にはなったんですが(^^;、WebSocket/Redisのコードサンプルとしては非常に有益で必要十分な内容でした。
記してここに感謝します。


今回の作業のゴールはこれを改良して汎用的に使えるWebSocket/Redisのベースクラスを作成することです。

https://github.com/shunjikonishi/websocketchat-redis

ただし現時点ではそれを切り出して単体のjar/pluginにすることは考えていません。現実にはWebSocket/Redisを使って何かを作る予定は今のところないしPlay、Redis共に開発サイクルが早いので、実際に必要になった時にはAPIが変わっている可能性も高いからです。

ここでは実際に必要になった時のプロトタイプとなり、その考え方や注意点を示せれば十分と思っています。


そんなわけでRedisService.scalaとChatRoom.scalaはかなり丁寧に書きましたが、それ以外(JavaScriptとか)は割と適当です。(^^;

JavaScript版のWebSocketラッパーもそのうち作りたいとは思うんですけどね。。。


★PlayでのWebSocketアプリの作り方

http://www.playframework.com/documentation/2.2.x/ScalaWebSockets

Controllerでリクエストハンドラを作る時にActionの代わりにWebSocketを使って受信データをハンドルするIterateeと送信を実行するEnumeratorを返すようにすればOKです。

以上、終わり(^^;

。。。なんですがちょっとだけIterateeとEnumeratorについても触れておきます。
これらを解説するサイトは日本語のものだけでも、かなり多数ありますが抽象度の高い概念なのでなかなか理解するのは難しいです。というか抽象度の高い議論は途中からついていけません。。。(^^;;;

そうした概念的な話はさておき、WebSocketの文脈に限定して話をするなら、

- Iteratee
クライアントからデータを受信した時と接続が切れた時に何をするかを定義するもの

- Enumerator
クライアントに発信するデータを供給するもの


とだけ理解しておけば十分です。


これ、よく考えるとアプリで必要な機能以外は何ひとつ作らなくて良いインターフェースになってるんですよね。

今までPlay2を触ってきた中でこのWebSocketのAPIが一番いけてると思いました。(^^;


★RedisService

https://github.com/shunjikonishi/websocketchat-redis/blob/master/app/models/RedisService.scala

プロトなので関連するクラスを全部1ファイルにまとめています。

RedisServiceクラスの機能は

- コネクションプール
- WebSocketで使用するPubSubChannelの作成

の二つだけです。

- コネクションプール
基本的にRedisを使う場合はコネクションプールを併用した方が良いです。RedisClientは基本的にソケット繋ぎっぱなので、接続のコストをカットできます。

ただし、RedisClientをSubscriberとして使用する場合は注意が必要です。Subscribe時のRedisClientは別スレッドでSocketの入力を監視しているのでプールに戻すタイミングが難しくなります。

今回はやや強引な方法でSubscriberもプールに戻してますが、シビアにスレッドセーフに気を使う必要があるので、素直にSubscriberとして使用する場合は新しいインスタンスを作成して使い捨てた方が良いと思います。(使い捨てる場合もunsubscribe時にdisconnectすることを忘れてはいけません。)

このプロトでは試行錯誤の過程を残す意味でプールに戻すコードを残していますが、もしこれを本当にライブラリ化するのであればSubscriberでは常に新しいインスタンスを使用するようにします。(そうするとborrowClient/returnClientメソッドが不要になるのでAPI的にもすっきりします。)


- PubSubChannel
WebSocketにひもづけるIteratee/Enumeratorをラップするクラスです。
コンストラクタの必須引数は購読するチャネル名のみです。
インスタンスを作成してメンバ変数のinとoutをWebSocketに返せばそれだけで単純なEchoアプリが作れます

オプションとして

  • send: クライアントから受信した文字列をRedisに送信する前に加工する関数
  • receive: Redisから受信した文字列をクライアントに送信する前に加工する関数
  • disconnect: クライアント切断時にRedisに送信する文字列を返す関数


などを指定できます。
exception/subscribe/unsubscribeなどのSubscriber関連のイベントハンドリング関数も一応指定できるようになってますが多分使うことはないです。

実装に関して言うと

  • Publisher: Actorになっていて都度プールから取ってきたクライアントで送信
  • Subscriber: ひとつのクライアントでredis-scalaの内部スレッドにおまかせ


となっています。
WebSocketではinとoutは一対ですが、RedisのPub/SubはWebSocketだけで使うものとは限らないので本来的にはは多対多であり、Publish自体はどこから行っても構わない訳です。

こう考えるとPublisherのActorはシングルトンでも構わない気がしますが、Publishのコストは購読者数に比例する(らしい)のでチャネル毎にわけています。

あとこのクラスではsubscribeするチャネルを一つに限定しています。Redisの機能を考えるとマルチチャネル購読を使いたいケースというのは十分に考えられるわけですが、そうすると

  • -> in/outにチャネル名も入れる必要がでてくる
  • -> 入出力の型をStringからTuple(String, String)に変更
  • -> in/outを直接WebSocketに接続できるというメリットが失われる


ので止めました。
まぁマルチチャネル対応版をサブクラスなりTraitに切りだすなりして対応するのはそんなに難しくないので必要に迫られた時に考えたいと思います。


★ChatRoom

https://github.com/shunjikonishi/websocketchat-redis/blob/master/app/models/ChatRoom.scala

ChatRoomに関して具体的なアプリの内容に関する説明はここではしません。
ここではWebSocket/Redisアプリを作る際のポイントとなる部分についてのみ説明します。


- クラスで状態管理をしてはならない
元々のサンプルではチャットルームのメンバ一覧をクラスのvar変数で管理していますが、複数サーバある場合に状態を共有できないのでアウトです。
状態管理の変数は常にRedis上に置く必要があります。

RedisにはListやSetを扱う機能があるのでとても重宝します。(^^;

- WebSocketとPubSubChannelは1対1とは限らない
WebSocket/Redisアプリを作る一番簡単な方法は先に作成したPubSubChannel(のin/out)を直接WebSocketにひもづけることです。

状態管理をすべてクライアント側で行うアプリであれば、サーバ側には一切ロジックを記述する必要がありません。(このサンプルはそうなっていませんが、チャットはそういうやり方でも作成できます。)

ですが、それだと接続毎にRedisのコネクションを消費することになるので無駄が多いわけです。

チャットの場合同じ部屋にいるユーザが購読するRedisチャネルは皆同じなのでホストで一つチャネルを開いてそれを共有できれば大幅にコネクションを節約できます。

実際、PubSubChannelのoutは複数のWebSocketで共有することができます

ですがinはダメです。何故ならこのIterateeではクライアント切断時にチャネルをクローズしているからです。つまり1人のユーザが退出したら全ユーザのWebSocketが切れます

しかし、前述の通りPublishはどこから行っても構わないのでPubSubChannelのinをそのまま使う必要はなく自前のIterateeを使うことができます

この時問題となるのはRedisの接続管理です。

- いつunsubscribeするか?
今でしょ!なわけないですよ。(^^;

論理的にはWebSocket接続数を数えておいて、0になったらunsubscribeすれば良いのですが、どこでcountUp/Downするかも悩ましかったりするわけです。考慮漏れがあると即リソースリークになるので。。。(--

で。。。結論なんですがこれは変に考えすぎず、クライアントからの切断にのみ反応するようにするべきと思います。具体的にはIteratee#Doneの中。

WebSocketの切断が発生するのは多くの場合ブラウザの画面遷移やクライアントの電源断(タブレット等ののスリープ含む)です。

これらはいつ発生するかはわからないので変に状態管理するよりも確実にそれだけを捕まえるのが良いと思います。

直観ですが、WebSocketアプリではサーバーサイドからの切断もあんまりやらない方が良い気がします。

- どこで接続管理を行うか?
これは絶対にActorを建ててそこでのみ行う必要があります。
IterateeはWebリクエストのワーカースレッド内で動くので、スレッドセーフにするためにはcountUp/Downを伴う操作は同じActorの中からしか実行してはいけません。

なのでIteratee#Done内で実行するDisconnet操作もActorにDisconnectメッセージを投げるだけとなっています。

□□□□
ここまでたどり着くのに相当試行錯誤しており、AkkaのActorにも慣れてないのでイマイチ実装に確信を持ててないんですが、概ね抑えるべきところは抑えたつもりです。

何かおかしなところがあれば是非ご連絡を!



★Playの残念なお知らせ

さて最後に今回気がついたPlayframeworkの衝撃の事実を。。。。(--

Playで修正/リコンパイルを繰り返している時には、修正前に使用されていたインスタンスは回収されません。。。マジでかーーーー!!


前回書いた通り、Redisの最大接続数=10の状態で作業しているとほんの数回修正を行っただけで接続エラー。。。。

その度にPlayの再起動を繰り返すのはなかなか苦痛でした。(最初はPlayのせいとは思わず自分かredis-scalaのリソースリークを疑っていたのでなおさら。。。(--)

Global#onStopでcloseすれば大丈夫かと思ったんですが、ここのロジックが実行されるのはリコンパイルが行われた後っぽい。。。意味ねーーーー!!!

シングルトン(object)とか、どうなってるのかと思うけどこれも単なるクラスのインスタンスなのでポインタ変わって新しいインスタンスが作られるだけなのかなと思ったり。

唯一有効だったのはActorのpostStopにcloseを仕込んで置くことだけど、これも常に有効かどうかは疑わしい。。。(上の推測通りシングルトンのアドレスが変わるのであればアウトだと思う。)

修正前のインスタンスが残るのは仕方ない気がするけど、せめてonStopはリコンパイル前に走って欲しい。。。(--

□□□□

以上、こんだけ書いておけば将来ホントにライブラリ化する日が来たとしても、ちょっとこれ作った奴出てこい!とか思わずに済むでしょう。。。多分。。。(^^;


次回、「HerokuでスケーラブルWebSocket」(かもしれない)

2014年1月 9日 (木)

PlayとRedisでスケーラブルWebSocket

本日2本目。

年末から突発的に1人でWebSocketブームです。(^^v
概要レベルの知識はあったんですが、実際に触ってみると思った以上に使える技術で応用範囲も広そうな気がします。
これは楽しい!


さて、スケールアウトするWebSocketアプリを作ろうと思ったらどうやらRedisが必修らしいので、Redisにも手を出してみることにしました。

この記事はそのまとめです。

 

★Redisとは

Memcacheの亜種。というのがこれまでの理解だったんですが、まぁだいたいあってます。基本的にはいわゆるKey-Valueストアです。

ですが実際にはMemcacheよりも遥かに高機能です。
ちゃんとドキュメントを読んだわけではないですが、ざっくりとMemcacheよりも優れている点を上げてみると

  • List, Set, Mapなどの集合を値として扱える
  • キーの一覧が取得できるなど管理系のコマンドがある
  • Pub/Sub機能がある
  • 永続化できる


などがあると思います。
今回、WebSocketで使うのは主にPub/Sub機能ですがListなどの集合の値をアトミックな操作で追加/削除できる機能はスケーラブルなアプリを作る上ではいかにも欲しそうな機能です。

通信プロトコルは多分独自で、ざっとクライアントのソースを眺めた感じではとてもシンプルそうです。可能な限り素のソケット通信に近いプロトコルを使用することで速度を稼いでいるんでしょうね。


★Pub/Subとは

Publisher/Subscriberの略です。日本語だと発信/購読という単語がしっくりきますかね。
任意のキー(チャネルという)を指定して購読を登録しておくと、そのチャネルに対して発信されたメッセージがすべての購読者にプッシュで配信されます。

ひとつのSubscriberで複数のチャネル(またはパターン)を登録することが可能(「hoge」、「fuga」、「room1.*」みたいな感じで指定できる)で、配信されるメッセージにはマッチしたチャネル名も含まれます。
なので、購読者はチャネルで分岐して処理を振り分けることができるわけです。まさにWebSocketのためにあるような機能ですね。(^^v


さて、プッシュをどうやって実現しているかというとその方法は極めて単純で単にソケットを繋ぎっぱにしてるだけです。
なので購読を行っているクライアント(ソケット)はメッセージがこない限りブロックします。(ここではNon Blocking I/Oは考慮せず単純なソケット通信を前提に話しています。)
まぁ、この辺たいていはライブラリがうまいことやってくれるので利用者はあまり気にする必要はないんですが、この事実からPub/Subを使う時の重要な注意点が導き出せます。
それは以下のような点です。

  • 購読中のクライアントは購読登録/解除のメソッド以外を実行してはならない
  • もちろん発信もアウト
  • 購読解除を行わない限りそのコネクションは占有され続ける


などなど。
初めて実装を行う際にはなんとなく同じクライアントを使いまわして購読と発信をやってしまいそうになりますが普通にアウトです。

またサーバーサイドでコネクション数の上限が決まっているので購読解除のし忘れはそのままリソースリークです。(もっとも購読に限らずRedisのクライアントはソケット繋ぎっぱなしにしていることが多いと思います。なので、利用の際にはJDBCのConnectionと同じようにこまめにcloseするかコネクションプールを使用する必要があります。)


★Java/ScalaのRedisクライアント事情

調査を始めた当初はJedisとかSedisとかいう単語がいきなり目に飛び込んできてかなり混乱しました。GitHub時代の弊害で雑多なライブラリが乱立しておりどれが良いんだかさっぱりわからないんですが、以下にいくつか調べたものをあげておきます。

- Jedis
https://github.com/xetorthio/jedis

JavaのRedisクライアント実装です。
多分これがデファクトスタンダード。

JavaのRedisクライアントだからJedisですか。そうですか。。。(--

- Sedis
https://github.com/pk11/sedis

JedisのScalaラッパー。
といっても全部のAPIがラップされているわけではないので、ないものを使おうと思ったら結局JedisのAPIを直接たたくことになる。(Pub/Subとかはない)

ScalaのRedisクライアントだからSedisですか。そうですか。。。(--
なんとなく名前に騙されそうになりますが、多分これを使うくらいなら最初からJedisを直で使う方が良いです。

- scala-redis
https://github.com/debasishg/scala-redis

最初からScalaで実装されたクライアント。
多分これがScalaのデファクト。

元々は他の人が始めたプロジェクトをこの人がフォークしてガンガン作りこんでいつの間にかこっちが本流になったらしい。。。

う~ん、GitHub時代。。。(--
なんか本家と元祖が並んでるたこ焼き屋みたいで何を信じれば良いのか疑心暗鬼にかられますけど、Redisの公式ページのクライアント一覧でもこれに星がついているしMavenセントラルにも登録されているので多分これが本家(?)です。

どうでもいいけどこのクライアント一覧。

 

お前のクライアントをこのリストに載せて欲しいのか?だったらredis-doc repositoryをフォークしてプルリクだしな!

 

とか書いてあるよ。。。
凄いな、GitHub時代。(^^;

- scala-reids-nb
https://github.com/debasishg/scala-redis-nb

Akkaを使用したNonBlocking I/OのRedisクライアント。
作者は上のscala-redisと同じ人。

RedisはNonBlocking I/Oと相性良さそうだけど、残念ながらPub/SubのAPIはない。
多分まだ枯れてないので動向はチェックしつつも様子見な感じ?

- play-plugins/redis
https://github.com/typesafehub/play-plugins/tree/master/redis

Play2系のRedisPlugin。CacheインターフェースのBackendをRedisに置き換えてくれる。
内部ではSedisを使用している


- 結局どれを使えば良いの?
素直にJavaならJedis。Scalaならscala-redisが良いと思います。

Playから使おうと思ったら多分最初に見つかるのはplay-plugins/redisなのでそれをそのまま使いたくなるけど、個人的にはscala-redisの方がお勧めです。(Jedisを直に使うならそれでも良いと思う。つまりSedisの評価が低い。)

あと、scala-redisはソースがとても読みやすいところも良いです。
実のところRedisのドキュメントはほとんど見てないんだけど、これのソース見たおかげでRedisの使い方がなんとなくわかったというのもあります。

読みやすいのはあんまりScalaっぽくないコードのせいかもしれないですけどね。(^^;
個人的には自分型アノテーションの使い方とかこれ見て初めてちゃんとわかった気がするのでそれも良かったです。
いきなりPlay本体のソースとか見ちゃうと多分Scala嫌いになると思うので、最初はこれくらいのライトなライブラリから眺めるのも良いんじゃないですかね。(^^;;;


PlayのCacheインターフェースを使いたいなら、scala-redis版はないんですけどこれは自分で作っても良いと思います。
多分、

  •  Sedis版のソースをコピって、
  •  import置き換えて、
  •  コンパイルエラーがでなくなるまで直す!


とするだけで動くと思う。(^^;
もっとも、無理してCacheに合わせなくても直でRedisを使えば良いと思いますけどね。


★HerokuのRedis事情

ついでにHerokuのRedisアドオンについて。
自分でRedisサーバたてても良いんだけどそれすらもめんどくさいので、localhostから使うテストサーバもHerokuAddon(無料版)で済ませています。(^^;;;

HerokuのAddonページで「redis」を検索するとなんと5つものAddonが引っ掛かります。(2014年1月現在)
うち、4つがCloudでのRedisサーバ提供者。。。
どこに差別化ポイントがあるのか全くわかりませんが、多分見るべきポイントはそれほど多くはないです。

今回の場合

  •  価格
  •  コネクション数(最大接続数)
  •  メモリサイズ


の3つだけを見て、無料で一番メモリサイズが大きかったRedisCloud-25MBを選択しました。
各社Webコンソールの使いやすさとかが違うんだろうと予想しますけど、コマンドライン(CLI)でたいていのことはできるので多分そんなに重要ではないです。(Webコンソールを見たのはRedisCloudだけだけど、まぁ十分です。)

最重要なのは実はメモリサイズよりもコネクション数です。前述の通りPub/Subは購読者の数だけコネクションを消費するので、これが少ないとすぐに接続エラーになってしまいます。
無料で使えるAddonはいくつかありますが、どれもコネクション数は10なので実運用には耐えない。。。というか1人でテストしてるだけでも割と簡単に上限に達します。

まぁ、これがきっかけでリソースリークを気にするようになったので結果オーライではあるんですけどね。(^^;

これが許容できないなら自前でRedisサーバを動かすのも良いでしょう。
CLIを使うために結局のところRedisのインストールは必要ですし、インストールさえすれば設定なしでいきなり起動させることができます。

逆にHerokuアドオン(Paasサービス)を使うことのメリットはWebコンソールがあるということにつきます。
先にWebコンソールは重要ではないと書きましたが、それは80点と85点の差には意味が無いということであって、まったく無いのとあるのとでは大違いなわけです。

今回の場合特にコネクション数がリアルタイムにわかったのが非常にありがたかったです。

□□□□
PlayでのRedis Pub/Subの実装についても書くつもりですけど、全然そこまでたどりつかないですね。。。(^^;
続きはまた次回

JSON Schemaというのがあるらしい

あけましておめでとうございます。(遅い!)

新年一発目のブログ。昨日途中まで書いたかなり渾身のネタがあるんだけど先に今朝飛び込んできたHeroku関連のニュースを。

なんかHerokuのPlatform APIのスキーマ定義がJSONフォーマットで公開されたらしい。

https://blog.heroku.com/archives/2014/1/8/json_schema_for_heroku_platform_api

実際にどういう定義になっているかは上記ブログ内にあるcurlコマンドを叩けば確認できます。(こんなの認証不要のテキストコンテンツとして普通にhttpで公開しちゃえば良いと思うんだけど)

ていうか「JSON Schema」っていうJSONのスキーマ定義言語があるんですね。
どのくらい流行っているのかは謎ですけど。。。

こういうのって必要だとは思うけど元々XML界隈にいた身としてはなんかXML SchemaとRelaxの不毛な戦争を思いだします。(^^;

JSON Schemaについてなんの予備知識なしで定義インスタンスを見てもなんとなく意味がわかるので、仕様自体はとてもシンプルなんだと思います。少なくともXML Schemaよりは100倍読みやすいです。

ただ、何か違和感があります。。。。

。。。。

ちょっと考えて、スキーマを定義するためのキーと定義されるスキーマのキーがひとつのJSONの中に混在していて見分けがつかないせいだと気がつきました。

definitions以下の定義が、

 

"definitions": {
  "created_at": {...},
  "description": {...},
  ...
}



のようになっていて、「type」とか「description」とかの一般的なキー、かつJSON Schema自体も使用しているものが現れた場合に混乱するんですね。

構造化を優先するなら

 

"definitions": [
  { "name" : "created_at", ...},
  { "name" : "description", ...},
  ...
]



の方が良いんじゃないかなぁという気もしますが、その思考を進めていくとだんだんXML Schemaのようになっていく気もするのであんまり深入りしない方が良いでしょう。(^^;
(JSONがXMLに取って代わった理由の一つに「名前空間が無い」というのがあると思っています。)

このスキーマをベースに各種言語のモデルを自動生成することはできると思いますが、それをやる場合僕ならやっぱりXSLTが使いたいですね。

JSONに対してXSLTを適用する方法って何かあるんでしたっけ???


採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart