特集・コラム

メニュー

Ruby & Rails

vol.12 ‐ 『サーバプッシュ番外編 ~ActionController::Live~』

投稿日時:2015/02/09 10:02

カテゴリ: Ruby & Rails

 5/6にRails3.2.18、4.0.5および4.1.1がリリースされました(*1)。
このリリースはディレクトリトラバーサル脆弱性に対するセキュリティフィックスですので、
できるだけ早くアップデートすることをお勧めいたします。
 

さて前回までサーバプッシュを行うための技術として、WebSocketをご紹介してまいりました。
今回はその番外編として、Rails4から導入されたActionController::Liveによるサーバプッシュについて
書いていきたいと思います。
 
なお、動作環境は以下の通りです(*2)。
 

• Windows 7
• Chrome 35
• Ruby 2.1.2
• Rails 4.1.1
 

Server-Sent Events
 

第9回「WebSocketでサーバプッシュ」の中でもお伝えしましたが、
これまでのWebの仕組みではサーバプッシュを行うことができませんでした。
このサーバプッシュを行うための技術として、2つの仕組みが規格化されることとなります。
 
1つは前回までご紹介したWebSocketです。WebSocketはHTTPではない独自のプロトコル
(WebSocketプロトコル)を用いるという方法でサーバプッシュを実現しました。
 
これに対し、HTTPのみでサーバプッシュを実現する方法も規格化されます。
それが「Server-Sent Events(以下、SSE)」です(*3)。
 

SSEはCometを標準化したような技術で、以下のような特徴を持ちます。
 

• HTTP上で動作
• 必要な分だけコネクションを維持し、イベントに応じて都度データを送信
(サーバがレスポンスヘッダに「Transfer-Encoding: chunked」を指定すると、
クライアントでは送られたデータを都度処理する)
• クライアントサイドでは、コネクションが切断されると自動で再接続
 

Vol.12_画像①
HTTP上で動作するため、サーバを含めた既存のWeb環境で動作することは大きなポイントです(*4)。
 
反面注意点としては、HTTPの制約を受けることや、SSEのコネクションを用いて
クライアントからリクエストが送れない(*5)ことなどが挙げられます。
 

ActionController::LiveはSSEにおけるサーバサイドを実装するAPIとして、Railsに組み込まれています。
 

チャット機能の実装
 

それでは、実際に使ってみましょう。今回も芸なく前回までと同様にチャット機能を実装してみたいと思います。
作成の流れは以下の通りです。
 

1. コントローラの実装
2. routes.rbの設定
3. クライアントサイドの実装
4. development.rbの編集
5. pumaのインストール
 

なお、ActionCotroller::LiveそのものはRailsに標準で組み込まれていますので、
Gemなどによるインストールの必要はありません。
 

1. コントローラの実装
 

まずはコントローラを実装します。ジェネレータを使い、コントローラと画面表示用のビューを合わせて生成します。
 

Vol.12_画像⑧
 

生成後、「app/controllers/chat_controller.rb」を以下のように編集します。
 

Vol.12_画像②
 

ここでのポイントを順に説明していきます。まずはstreamアクションについてです。
このアクションはSSE用のコネクションを確立・保持するためのものです。
 

[Line 15]
 

response.headers[‘Content-Type’] = ‘text/event-stream’
 

SSEの仕様では、レスポンスのMIMEタイプを「text/event-stream」にしなければならないと定められています。
ここではレスポンスヘッダのContent-Typeに「text/event-stream」を設定しています。
なお後述のwriteメソッドやcloseメソッドが呼ばれる前にレスポンスヘッダを指定しないと、
エラーになるので注意してください。
 

[Line19~22]
 

  loop do
      response.stream.write(“:ping ¥n¥n”)
      sleep 15
    end
 

response.stream(*6)に対してwriteメソッドを呼ぶことで、クライアントにデータを送ることができます。
送信データは、例えば以下のような形式になります(*7)。
 

data: This is the first message.
 
data: This is the second message, it
data: has two lines.
 
data: This is the third message.
 

送信データは各行「(要素名): (値)」の形となり、また空行の直前までを一つの送信単位とします。
この例では1行目のデータ、3行目+4行目のデータ、6行目のデータという単位で3回クライアントに送信されます(*8)。
また、指定できる要素名は以下の通りです。
 

Vol.12_画像③
 

上記以外の要素名は、全て無視されます。また、先頭に「:(コロン)」を付与するとコメント行と解釈されます。
 
このソースコードでは15秒ごとにコメントの送信を行い、コネクションが切断しないようにしています。
 

[Line 27]
 

response.stream.close
 

response.streamに対してcloseメソッドを呼ぶことで、コネクションを切断します。
この処理は当該アクション内で必ず行う必要があります(*9)。このソースコードではIOError発生時(*10)、
loopメソッドを抜けた後にensure節で実行されるようにしています。
 

続いてmessageアクションについてです。前述の通りSSEにて確立したコネクションを使って、
クライアントからサーバへデータを送信することができません。そこでクライアントからのデータ送信は
別途Ajaxリクエストで行うこととします。
 

[Line32~34]
 

  @@streams.each do |stream|
     stream.write(“data: #{params[:comment]}¥n¥n”) rescue nil
  end
 

messageアクションではリクエストを受け、SSEにて接続しているクライアント全てに
リクエストパラメータとして飛んできたデータを送信しています(*11)。
 

2. routes.rbの設定
 
続いてルーティングを定義します。「config/routes.rb」を以下のように編集します。
 
Vol.12_画像⑨

 

3. クライアントサイドの設定
 

次はクライアントサイドの実装です。SSEをクライアントで扱うには、
EventSourceと呼ばれるオブジェクトを使用します。以下にそのメソッドなどの一覧を示します。
 

Vol.12_画像④
 

 なおイベントに関しては前述の通り、サーバ側でeventが指定された場合にはそのイベント名でも発火します。
 

「app/views/chat/index.html.erb」を以下のように編集します。
 

Vol.12_画像⑦

 SSEによる接続の開始処理の後、messageイベントの購読を行っています。再接続処理を書いていませんが、
前述の通りコネクションが切断した場合はEventSourceが自動で再接続を行ってくれます(*12)。
 

4. development.rbの編集
 

 Websocket-Railsと同様にdevelopmentモードでActionController::Liveを
使用する際はRack::Lockを無効にする必要があります。
また、合わせてクラスのeager_loadをtrueに設定しておきましょう(*13)。
 

「config/enviroments/development.rb」を以下のように編集します。
 

Vol.12_画像⑩
 

5. pumaのインストール
 

さてサーバを起動して動作確認といきたいところですが、デフォルトで起動するWEBrickでは
ActionController::Liveを動作させることができません。そこでここではpuma(*14)をインストールします。
以下をGemfileに以下を追記し、「bundle install」を実行します。
 

gem “puma”
 

インストールが完了したら「rails s」でサーバを起動し、
「http://localhost:3000/chat/index」にアクセスしてみましょう。
WebSocketの時と同様の動きをすることが確認できるかと思います。
 

Vol.12_画像⑤
 

またChromeの開発者ツールにてNetworkタブを見てみると、
「stream」のコネクションが切断されずにいるのを確認することができます。
 

Vol.12_画像⑥
 

まとめ
 

ここまでActionController::Liveについてご紹介してまいりました。
SSEとWebSocketでは、正直 WebSocketの方が使い勝手のいい印象です。
ですが、状況によっては選択できないケースもあると思いますので、
そのような際は是非 ActionController::Liveを検討してみてください。
 

それでは、Enjoy Ruby!
 

注釈
*1 : http://weblog.rubyonrails.org/2014/5/6/Rails_3_2_18_4_0_5_and_4_1_1_have_been_released/
*2 : Mac環境(Mac OS X 10.9.3)でも動作確認をしています。
*3 : http://www.w3.org/TR/eventsource/
*4 : しかしながら執筆時点ではIEがSSEをサポートしていません。
*5 : HTTPのコネクションを張り続けているだけなので、当たり前といえば当たり前ですが。
*6 : 戻り値はActionController::Live::Bufferオブジェクトです。
*7 : 注釈*3のURLより抜粋
*8 : クライアントではmessageイベントが3回発火します。
*9 : 仮に当該アクション内でエラーが発生しても、です。ActionController::Liveのdocにはcloseメソッドを
呼ばない場合、「the socket may be left open forever」であると書かれています。そのため、ensure節の中に書いておくのが一般的なようです。
*10 : ブラウザを閉じるなど、切断されたクライアントに対してwriteメソッドでデータ送信を試みると、
IOErrorが発生します。
*11 : クライアントへのデータ送信はSSEのコネクションにて行っています。
そのため、このアクション自体のレスポンスボディは空(render text: nil)にしています。
*12 : なお、コネクションを切断したい場合には意図的にcloseメソッドを呼ぶ必要があります。
*13 : eager_loadをtrueにすることで、アプリケーションをスレッドセーフにすることができます(Rails4以降)。
*14 : https://github.com/puma/puma

 

転載元(CTC教育サービス・2014年6月掲載記事)
(WEB転載許諾取得済)

バックナンバーはこちら

IT系のお仕事特集

お仕事のご紹介には、まずヒューマンリソシアへの登録が必要です。
ヒューマンリソシア人材派遣サイトの便利な機能 ・お気に入りの派遣求人のブックマーク ・登録会への予約 ・有給休暇の管理 ・WEB給与明細の確認 ・お気に入りの情報をメール受信
登録会の入力手続きをあらかじめおこなえます。

ヒューマンリソシア派遣サービスに 登録する 無料

ページトップへ戻る
ヒューマンリソシア派遣サービスに 登録する 無料
ページトップへ戻る