Ruby on Rails5の新機能ActionCableを試す!

※1 執筆時のRailsはバージョン5.0.0.1。
※2 Webサーバにはnginxを使用。
※3 リバースプロキシにnginxを使った構成も対応する。

Rails5.0.0.1はすでにインストールされているものとする。

定番のコマンドでプロジェクトを作る。

$ rails new sample-project

次にチャンネルを作る。
これにより作成されるファイルは下記の通り。
<hoge_channel.rb>はサーバサイドが行う処理が記述される。
<hoge.coffee>はクライアントサイドが行う処理が記述される。

$ rails g channel hoge piyo
(hogeがチャンネル名、piyoはアクション名)
      create  app/channels/hoge_channel.rb
   identical  app/assets/javascripts/cable.js
      create  app/assets/javascripts/channels/hoge.coffee

※ もしエラーが出たらGemfileのtherubyracerがコメントアウトされていると思うので有効にしてbundle installする。

<development.rb, production.rb>に「ソケットを許可するアクセス元」を追記。
つまり自分のドメインを書けば良い。

config.action_cable.allowed_request_origins = ['http://hogepiyo.com']

Railsの準備は以上。
次はnginxサーバの設定に移る。

!注意点!

rc版のRailsでは<route.rb>に

mount ActionCable.server => '/cable'

を書いたり、

<cable.coffee>の

@App ||= {}
App.cable = ActionCable.createConsumer()

をコメント外したりという作業が必要だった。
Ruby on Rails 5.0.0.1ではこれら作業は必要ない。

ここからNginxの設定。
RailsではWebSocketを使うのにPuma等が推奨されている。
Unicornはあいにく非推奨、これはシングルスレッドノンブロッキングI/Oという仕様のため。
そのためPumaに対してPIDないしSocketでアクセスを流してあげるのが好ましい(たぶん)
設定内容は単純に/cable(RailsのWebSocketの標準設定)へのアクセスをupgradeしてPumaに渡すだけである。

Nginxのconfファイルの参考

upstream puma {
  server unix:/var/run/puma.sock fail_timeout=0;
}

server {
中略
    location /cable {
        proxy_pass http://puma/cable;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
中略
}

これで晴れてActionCable(WebSocket)機能が動くようになる。

また、nginxのリバースプロキシがある場合は、リバースプロキシにも設定が必要になる。
アプリケーションサーバと同様に/cableに対して
upgradeしてアプリケーションサーバへアクセスを流すだけである。

server {
中略
    location /cable {
           proxy_pass http://backend;
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
           proxy_set_header Host $host;
    }
中略
}

もしこの記述をしなかった場合サーバは404を返すうえに、
アプリケーションサーバではこのようなエラーログが残る。

ブラウザのコンソールに表示されるログ
WebSocket connection to 'ws://hogepiyo.com/cable' failed: Error during WebSocket handshake: Unexpected response code: 404

Railsのログにはこのように出力される
Started GET "/cable" for *.*.*.* at YYYY-MM-DD 00:00:00 +0900
Started GET "/cable/"[non-WebSocket] for *.*.*.* at YYYY-MM-DD 00:00:00 +0900
Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
Finished "/cable/"[non-WebSocket] for *.*.*.* at YYYY-MM-DD 00:00:00  +0900
※ IP等は伏せてる

ここで注意しないといけないのがこの一行を忘れないこと。
> proxy_set_header Host $host;