APNsGatewayServer を書こうの巻
えー。APNsとのやりとり部分を書くのはなにげにとても面倒です。面倒な理由として
- プロトコルがアップルのオレオレプロトコルであり、なおかつバイナリでやりとりしている
- APNsサーバーに対してリクエストを送っても、基本的にレスポンスは帰ってこない
- でもなんかエラーがあった場合はレスポンスが帰ってくる
- 基本的にコネクションをつなぎっぱなしにしておかなければならない
- でもときどき何も言わずに向こうからブチっと接続を切ってくる
というあたりが挙げられますね。
1番の理由については単にめんどくさいだけで何も問題はありません。自分で書かなくてもライブラリとか結構あるし、そもそもこれくらいは書けよ感もある。
でもその他の問題がなかなかくせ者なのです。
まず、エラーの時だけレスポンスが帰ってくるということで、リクエストを投げたらレスポンスが来るまでブロックして、というような同期型のアプローチが使えません(readにtimeout設定してとかいろいろ頑張れば使えるけどけっこうだるい)。また、コネクションは保持し続ける必要があり、でも切られたら即再接続する必要があります。この時点で非同期スタイルでの IO が適切だ、となるでしょう。
非同期スタイルのIO自体は問題ではないのですが、これを例えば web アプリサーバー内でやろうとすると、いろいろと面倒です。たとえばアプリケーションサーバーが unicorn で動いているみたいな場合、unicorn のプリフォークモデルの中でナイーブにスレッドを動かしたりするのはちょっとやりたくないですし、じゃあイベントモデルだっつって unicorn を thin に変えて、アプリケーションコードの中に rack サーバー依存のコードが書かれるのは、たかが push 通知を実現するためにはやりたくないな、という感じがしますね。
というわけで、それならばアプリケーションサーバーとは別に APNs とのやりとりをしてくれる APNsGatewayServer (要するにオレオレ im.kayac みたいなやつ)を立てよう、となるのは結構合理的な判断なのではないでしょうか。
APNsGatewayServer が 9 割がた完成したの巻
そんなわけで、APNsGatewayServer を書き始めたのです。Ruby で EventMachine 使うってことも考えたんですけど、ちょっと EM で堅牢なサーバーを書く自信がなかったので(優秀なひとなら EM で堅牢なサーバー書くのできるんだろうけど、私にはつらい)、じゃあScala + Akkaでやればいいんじゃない? となり、Scala + Akkaで書き始めました。
APNs とのコネクションを管理する APNsActor と アプリケーションサーバーからのリクエストの窓口になる ServerActor、アプリケーションサーバー(APNsGatewayServerから見たらクライアントとなる)とのコネクションを管理する ClientActor が主要な登場人物で、ClientActorがアプリケーションサーバーから push 通知のリクエストを受け取ったらそれをパースして APNsActor に対して処理を依頼、APNsActor は依頼されたpush通知の内容を自らが管理してるコネクションを通じてAPNs に送りつけるという仕組みになっています。
実際に書いてみるとこういう非同期スタイルの要件にはぴったりな感じで、かなり素直にすっきりと書けました。いつどこでどんなイベントが起こるかわからなくて、なおかつ複数のアクターのメッセージパッシングにうまくモデリングできそうみたいな要件には Scala + Akka はかなりいい選択肢になるんじゃないかなと思います。そもそも Akka がクラスタリングに向いているので少しいじればスケールするようにもなるし。 Twitter の UserStreams みたいなの実現するのにもこれは良さそうな選択だなと思った。
そういうの、Erlang でもいいじゃん? という声もありそうですが、Erlang わたしが書けないので Scala にしました。そういえば Elixir の話題追いかけてないけどどうなんでしょうね Elixir。
AWSに全部持って行かれた
で、「よーしこれで今後はこのサーバー使えば APNs の push 通知は楽になるぞーと喜んでいたところでですね。AWS がモバイル push 通知サービスを始めると。しかもめっちゃ安いと。しかも信頼と実績の AWS であると。
「ガシッ!ボカッ!」アタシは死んだ。クラウド(笑)