Tech Sketch Bucket of Technical Chips by TIS Inc.

PaaS と iOS で Socket.IO (PaaS側 ~ eXcale & dotCloud)

Pocket

WebSocketやComet等のリアルタイムWeb技術は昨今、モダンブラウザだけでなくAndroidやiOSのネイティブ上でも動作するようになっています。本記事では、PaaS( eXcale や Heroku )上の Node.js とiOS間で Socket.IO を用いたリアルタイムな双方向通信をする方法について解説します。


CometとWebSocket

CometWebSocket は、サーバーとクライアントがリアルタイムに双方向通信を行うための手法です。「クライアントからのPULL」しかできなかったこれまでのWebページやAjaxを越え、「サーバーからのPUSH」も可能にするために考えだされました。擬似的なサーバーPUSHも含め、これらの技術にはそれぞれ特色があります。

iframeやXMLHttpRequestを用いたPollingによる擬似サーバーPUSH

iframeやXMLHttpRequestなどを用いて頻繁にサーバへPollingを行い、更新データを取得する擬似的なサーバPUSHです。実装は楽ですが、完全なリアルタイムとはいかず、ネットワークとサーバのリソースを消費します。

Comet

「クライアントから送信されてきたリクエストをサーバが維持し、サーバーからPUSHすべきイベントの発生(もしくはリクエストのタイムアウト)を契機ににレスポンスを返す」 というWeb アプリケーション・モデルの総称がCometです。サーバがクライアントとのコネクションを維持しているため、イベント発生時に即座にサーバからクライアントへデータをPUSHすることができます。

Cometは概念ですので、AJAX multipart streamingやAJAX long pollingなど、様々な実現方式があります。サーバ側やクライアント側の実装難易度や通信経路の都合、パフォーマンス等の観点から適切な実装を選んでください。

xhr-polling.png

WebSocket

Cometは「従来のWeb技術」という制約の中で無理やりリアルタイム双方向通信を実現しています。そのため重いHTTPプロトコルを使わざるを得ず、HTTPタイムアウト時やサーバからのデータPUSH後などに再接続を行わねばならない場合があるため、パフォーマンスに難がありました。
そこで出現したのがWebSocketです。WebSocketは(HTTPプロトコル同様)TCP上で動作するプロトコルの一つであり、 RFC6455 として規格化されています。WebSocketプロトコルは軽く、コネクションを維持して全二重通信を行えるため、サーバ、通信経路、クライアントが対応しているならばWebSocketを選択するのが良いでしょう。

websocket.png

PaaSで利用できる方式は?

しかし、ことPaaSを利用してサーバを構築する場合、PaaS自身のネットワークやルーティングの仕組みがハンドリングできる方式を選択する必要があります。

例えば dotCloudNodejitsu はWebSocketプロトコルに対応しているため、クライアントが対応しているならばWebSocketを選択するべきでしょう。
一方 Heroku や弊社のPaaSである eXcale はまだWebSocketプロトコルに対応しておらず、リアルタイム双方向通信を行うためにはAJAX long pollingを用いなければなりません。

@自分戦略研究所 Rails Hub情報局 によれば、HerokuはTCPルーティングの実装も模索しているとのことです。早晩WebSocketプロトコルへ公式対応してくることでしょう。

このようにPaaSごとに対応できる実装方式が異なるため、リアルタイム双方向通信を個別に実装するよりは、実装方式を隠蔽できるライブラリでラップしたほうがポータビリティが高くなります。そこで Socket.IO の登場です。

Socket.IOとは

Socket.IOは Node.js のパッケージの一つです。「トランスポート層の実装方式を隠蔽し、全てのブラウザ・モバイルデバイスでリアルタイム通信を可能とすること」を目指して開発されています。
現バージョンの0.9系では、WebSocketやAJAX long pollingだけでなく、Adobe® Flash® のSocketやiframeによるPolling等も利用可能になっているため、IE5.5といった古いブラウザからiPhone/Androidのブラウザまで、幅広いブラウザでリアルタイム双方向通信を可能としています。(詳細は Socket.IO browser-support を参照)

今回はこのSocket.IOのサーバ部分を実装します。

サーバ側の準備

今回はPaaSとしてeXcaleとdotCloud上でNode.jsを利用します。検証は下記のバージョンで行いました。

Node 0.8.19
express 3.1.0
ejs 0.8.3
socket.io 0.9.13

(以降のソースコードは省略されている部分があります。完全なソースコードは ココ を参照してください)

package.json

index.js

eXcaleの都合上、エントリポイントとなるスクリプト名は"index.js"にしてあります。Node.js+express+socket.ioによるサーバ側実装の典型例です。

chat.js

chat.jsのindex関数では、定数を設定してchat.ejsへHTMLレンダリングを委譲するのみです。

chat.ejs

chat.ejsには、socket.ioが提供するJavascriptライブラリを利用してクライアント側の実装を行っています。接続すべきSocket.IOサーバの情報は、chat.js(がrequireしているcommon.js)から得ています。

commons.js

サーバ側のNode.jsアプリケーションがデプロイされたURL、PaaSが指定するポート、利用するTransport層の実装など、PaaSごとに異なる定数を定義します。

モダンブラウザから接続して動作確認

上記のアプリケーションをPaaSにデプロイすれば、サーバ側の準備は完了です。モダンブラウザから接続して、動作確認をしてみましょう。

AJAX long polling (eXcale)

eXcale用のcommon.jsのprotocolには、"xhr-polling"を設定します。AJAX long pollingでの接続確認をしましょう。

eXcale.png

HTTPプロトコルで頻繁に再接続していることが見て取れます。

WebSocket (dotCloud)

dotCloud用のcommon.jsのprotocolには、"websocket"を設定します。WebSocketでの接続確認をしましょう。

dotCloud.png

WebSocketにプロトコルがスイッチしていることがわかります。

なおdotCloudを用いる場合、eXcaleとは異なり以下の設定ファイルを追加する必要があります。詳細はdotCloudのマニュアルを確認してください。

dotcloud.yml

dotCloudのNodeはデフォルトでは0.4系のため、Socket.IO 0.9系は動作しません。node_versionで0.8系を指定してください。

supervisord.conf

eXcaleの都合にあわせて、エントリポイントのスクリプト名を"index.js"にしているため、起動コマンドを"node index.js"にしてください。

次回は

次回 は、iOS側を実装を解説します。iOS用のSocket.IOライブラリは複数公開されていますが、その中でも Objective-C用のライブラリ管理システム CocoaPods に対応した AZSocketIO を利用します。お楽しみに!

エンジニア採用中!私たちと一緒に働いてみませんか?