railsでwebsocketを使用する場合、websocket_railsという便利なgemがあります。
これを使ってみましたが、awsのcentosの本番環境での通信設定に半日ぐらい要してしまいました。
少しややっこしいので、手順等、残しておきます。
ローカル接続確認
まずはローカルでのwebsocket_railsの接続確認。
Gemfileに gem ‘websocket-rails’を追記して、bundle installを実行。
gem 'thin'
gem 'websocket-rails', github: 'moaa/websocket-rails', branch: 'sync_fixes'
gem 'redis', '3.2.0'
次にwebsocket-railsに必要なファイルをgenerate。
rails g websocket_rails:install
以下のファイルがgenerateされる。
-
config/events.rb
railsのroutes.rbような物。websocketのイベントのマッピングで使用します。 -
config/initializers/websocket_raiils.rb
websocket_raiilsのセットアップ用。 -
app/assets/javascripts/application.js
websocket_railsに必要な
//= require websocket_rails/main
が追記される
websocket確認用のサンプルで下記のような簡単なをhtml作成
<div ng-view="" class="ng-scope">
<textarea rows="5" style="width: 50%;" ng-model="testMsg" class="ng-valid ng-scope ng-dirty ng-valid-parse ng-touched"></textarea>
<br class="ng-scope">
<button type="button" class="btn btn-default ng-scope" ng-click="testSend()">送 信/button>
<br class="ng-scope">
<label class="ng-scope">受信メッセージ</label>
<br class="ng-scope">
<p ng-bind="reciveModel.reciveMsg" class="ng-binding ng-scope"></p>
<label class="ng-scope">入力内容</label>
<br class="ng-scope">
<p ng-bind="testMsg" class="ng-binding ng-scope"></p>
</div>
javascriptにWebSocketRailsの接続、送信、受信処理を記述
// WebSocketRailsが初期化されてないければ初期化して接続
if ($rootScope.wsRails == undefined ||
$rootScope.wsRails == null) {
try {
$rootScope.wsRails = new WebSocketRails("127.0.0.1:3001/websocket");
} catch (e) {
alert("error");
}
};
// websocketの受信イベントのバインド
$rootScope.wsRails.unbind();
$rootScope.wsRails.bind("websocket_chat", function(message){
$scope.reciveModel.reciveMsg = message;
$scope.$apply();
});
// テスト送信処理
$scope.testSend = function() {
$rootScope.wsRails.trigger("websocket_chat", $scope.testMsg);
};
config/events.rbに「websocket_chat」イベントのマッピングを設定。
subscribe :websocket_chat, to: WebsocketChatController, with_method: :message_recieve
railsのコントローラーにwebsocket_chat_controller.rbを追加
class WebsocketChatController < WebsocketRails::BaseController
def message_recieve
# クライアントからのメッセージを取得
recieve_message = message()
# websocket_chatイベントで接続しているクライアントにブロードキャスト
broadcast_message(:websocket_chat, recieve_message)
end
end
ここまでやってrailsを起動しても、下記のようなエラーが出る。
/Users/qq/Documents/workspace/fesny/vendor/bundle/ruby/2.0.0/gems/redis-3.2.0/lib/redis/client.rb:320:in `rescue in establish_connection': Error con
necting to Redis on localhost:6379 (ECONNREFUSED) (Redis::CannotConnectError)
Redisに繋がらないといっているので、osxにRedisを入れます。
Homebrewを利用してインストール
brew install redis
起動
redis-server /usr/local/etc/redis.conf
接続確認
redis-cli
Redisをインストール、起動した後にrailsを起動すると、正常に起動。
だけど、websocketの接続に失敗している。
WebSocket connection to 'ws://127.0.0.1:3001/websocket' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
なぜかなあと調べていたところ、githubのwebsocket-railsにStandalone-Server-Modeで起動せよという記述を発見。
https://github.com/websocket-rails/websocket-rails/wiki/Standalone-Server-Mode
thinで開発環境でやる場合でもStandalone-Server-Modeでやらないといけないのだろうか?
よく分からないが実行してみる事に。。
config/initializers/websocket_rails.rbを下記設定に変更。
config.standalone = true
config.standalone_port = 3001
config.redis_options = {driver: :ruby}
config.synchronize = false
config.redis_options = {:host => 'localhost', :port => '6379'}
websocket_railsの起動
sudo rake websocket_rails:start_server
websocket_railsの停止
sudo rake websocket_rails:stop_server
websocket_railsの起動確認
ps aux | grep websocket
root 6703 2.4 2.2 3125404 93444 ?? S 10:09AM 0:04.89 thin server (0.0.0.0:3001) [websocket_rails]
websocket_railsが起動している状態で、動かしてみると、WebSocketが正常に動いた。
この後、本番環境でも確認してみる事に。
本番でうまく環境構築できなければ、開発が無駄になってしまうので。。
本番接続確認
本番環境はaws ec2でcentos用のディストリビューションを使用し、OSバージョンは7.1で、webサーバーはnginxです。
すでにrailsの別のプロダクトで運用中の為、バーチャルドメインで配置して、railsもデプロイします。
その後、redisが入っていない為、yumインストール
yum install redis
redisの起動
systemctl start redis
接続確認
redis-cli
railsの起動(unicorn)、websocket_railsの起動を行う。
特にエラーは発生せず、ログの「log/websocket_rails_server.log」、「log/websocket_rails.log」にもエラーははかれていない。
しかし、javascriptのwebscoketの接続部分でエラーが発生している。
ここから半日ぐらいハマりました。
まずjavascriptのサーバーのIPが127.0.0.1のままになっていたので、修正。
$rootScope.wsRails = new WebSocketRails("サーバーIP:3001/websocket");
次にawsのインバウンドポートの3001を開ける。
これでもまだ接続エラー。
ポートをサーバーサイドで確認すると3001はthinが待ち受けしている模様。
netstat -tanp
tcp 0 0 0.0.0.0:3001 0.0.0.0:* LISTEN 21787/thin server (
クライアントからとサーバーからでtelnetで確認してみるが接続できる。
telnet 127.0.0.1 3001
telnet サーバーIP 3001
ここで、もしかしてnginxでproxyしないといけないのかと思い、nginxのconfに下記を追記。
upstream yeomanserver {
server 127.0.0.1:3001;
}
location /websocket {
proxy_pass http://yeomanserver;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
nginxは80番ポートで待ち受けているので、javascriptの3001ポートの使用部分を削除。
$rootScope.wsRails = new WebSocketRails("サーバーIP/websocket");
ここまでやっても接続できない。
log/production.logを見ても何もログは出力されていないもよう。
3001ポートでの待ち受けはしているはず。
websocket_railを止めて、実行するとエラー内容が変わる為、nginxはthinと連携でなんらかのエラーが起こっている?
その後、いくらくぐってもそれらしい情報は出てこず。
途方にくれそうになった時にブラウザに何の気無しに下記を入力。
http://サーバーIP:3001
するとMySQLとの接続が失敗したとメッセージが出る。
rails(unicorn)の起動は成功しているし、実行ページのブラウザ表示も問題ない。
接続失敗のユーザー名をみるとどうやらdevelopmentモードで起動しているもよう。
websocket_railsの起動もproductionモードの指定ができるようなので、下記コマンドで起動。
sudo rake websocket_rails:start_server export RAILS_ENV="production"
最後に
その後、本番環境でもWebSocketは正常につながり、送受信も正常に動きました。
websocket_railsでもhttpで接続したら、普通のrailsと同じような動作をするんですね。
しょうもない問題ですが、半日ぐらいはまったので、備忘録として残しておきます。
参考
Railsで複数グループ対応のチャットアプリを作ってみた
github websocket-rails
github websocket-rails issues 211
MacにRedisをインストールする