0

Sinatra RocketIOのcometの負荷が軽くなった

sinatraにSocketIOっぽいwebsocket/comet機能を追加できるプラグインを作っている。
今日はcometの負荷を軽くした。

shokai/sinatra-rocketio · GitHub


comet、複数回のサーバーへのpushをキューに溜めてまとめて送信するようにした。
cometioのpost_intervalで設定できる

設定例

set :cometio, :timeout => 120, :post_interval => 2
set :websocketio, :port => 8080
set :rocketio, :websocket => true, :comet => true # enable WebSocket and Comet
post_interval => 2 でタメたキューを2秒ごとにサーバーに送る。


websocketは1プロセスで1万5千ぐらい接続できるんだけど、cometが重くて足ひっぱってたのでこれで軽くなったと思う。

例えばこういうコードの時に今まで10回pushされてたけど、10回分まとめてajaxするので軽い。
for(var i = 0; i < 10; i++){
io.push("chat", "hoge("+i+")");
}

sinatra-cometio v0.5.1から有効になってる機能で、sinatra-rocketioとは別のgemなので
bundle updateしてアップデートしてください

0

Sinatra用のcometプラグインを作った

追記:RocketIOに統合されました → 橋本商会 » Sinatra RocketIOというプラグイン作った、これでWebSocketとCometが使える

******

作った

Sinatra Comet I/O

インストール

gem install sinatra-cometio



通信を意識せずに、サーバー側からクライアントの関数、クライアント側からサーバーの関数を呼び出せる。


サーバーからクライアントへプッシュする例

サーバー側


Ruby
require 'sinatra'
require 'sinatra/cometio'

CometIO.push :chat, :name => "shokai", :message=> "hello work!!"


クライアント側


HTML
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="<%= cometio_js %>"></script>

JS
var io = new CometIO().connect();
io.on("chat", function(data){
console.log(data.name + " : " + data.message); // -> "shokai : hello work!!"
});


クライアント→サーバーの例や、新規クライアント接続イベントやエラーイベント等についてもSinatra Comet I/Oに書きました


サンプルとしてHerokuでチャットを作った。 http://cometio-chat.herokuapp.com

チャットの場合同時に300クライアントぐらいしか接続できないっぽい。スケールさせる方法はよくわからない。
少人数で使うちょっとしたツールにリッチなUIを持たせる時などに便利だと思います。


*チャットサンプルですが1台のマシンから2つchrome開いても、交互に通信するみたいです。片方をsafariにするか、chromeをプライベートブラウズモードにすると動きます
色々やってたら大丈夫になりました

0

cometだけどsinatraからサーバープッシュできれば関係ないよね

前に書いたsinatra/streamingでcometするやつを使いやすいようにgemにして、sinatra-cometioというsinatraのpluginを作った。
https://github.com/shokai/sinatra-cometio

thin等のEventMachine上のサーバーで動く。Apache+Passenger等では動かない。


チャットを作った例 http://cometio-chat.herokuapp.com


インストール

gem install sinatra-cometio


なかなか簡単に使えるようになっている。socket.ioを参考にした。

サーバー側
onのイベント名でクライアント側からのアレを受け取って、クライアント側のイベント名を指定して送り返す
require 'sinatra'
require 'sinatra/cometio'

## echo
CometIO.on :chat do |data|
puts "#{data.name} : #{data.message}"
self.push :chat, data # server --> client
end

require ‘sinatra-cometio’すると /cometio 以下にcomet用のrouteやjsファイルが生成されるようになっている。


クライアント側
jQueryが必要。
cometio.jsを読み込むんだけど、cometio.jsのURLはcometio_jsというヘルパー関数が返してくれる
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="<%= cometio_js %>"></script>


on(イベント名, callback)でサーバーから来たデータを受け取る。push(イベント名, データ)でサーバーに送る。
var io = new CometIO().connect();

// client <-- server
io.on("chat", function(data){
consle.log(data.name+" : "+data.message);
});

// client --> server
io.push("chat", {name: "shokai", message: "hello"}); // 送信ボタン押したイベントとかから呼び出す


ちゃんと測ってないけど500ぐらいぶら下がってしばらくすると接続エラーが出始めた。300ぐらいなら問題ない。
大してメモリも食わないのでforkしておけばいいのではないか

まだsessionがないので、つないでいるクライアント全員に送るしかない、なんとかする

sinatra/streamingでcomet (long polling)する はコメントを受け付けていません。

sinatra/streamingでcomet (long polling)する

良い感じのgem sinatra-cometioを作ったのでこっちを使うと良いと思います




チャットを作ってみた。
http://shokai-comet-chat.herokuapp.com/
Chrome/Safari/Firefox/Android/iPod touchで動いた。


sinatra/streaming

sinatra-contribにsinatra/streamingというのがあった。eventmachineを使っているサーバー(thinなど)で動くらしい。herokuでも動いた。

Sinatra::Streaming (part of Sinatra::Contrib)


本気で使うには厳しいしnode.jsを使えばいいと思うけど、Rubyでちょっとしたツールを作るのには便利そう。
一応同時に1000接続までは耐えた。ただ、Cometだとクライアント側とサーバー側両方で1分ぐらいでタイムアウトさせて接続しなおしたりするわけだから、タイミングが悪かったり接続が残ったりする事があると思うので少なめに見積もったほうがいいと思う。
実装は楽だし、ポートを1つしか使わないのでイントラ内で使うようなちょっとしたツールにリッチなUI付けたい時とかに便利そう。
arduino_firmataをwebからゴリゴリ触れるとか。



getやpostの中でstreamブロックを宣言すると、任意のタイミングでレスポンスを返せる。IOっぽく扱える。

getするとpostでデータが入るまで待たされる簡単な例
https://gist.github.com/3989831
#!/usr/bin/env ruby
require 'rubygems'
require 'sinatra'
require 'thin'
require 'sinatra/streaming'

@@comet = Hash.new

post '/comet/:channel' do
channel = params['channel']
data = params[:data]
@@comet[channel] = data
data
end

get '/comet/:channel' do
channel = params['channel']
stream do |s|
data = nil
loop do
break if (data = @@comet.delete channel)
sleep 1
end
s.write data
s.flush
end
end

getすると待たされる
% curl http://localhost:4567/comet/test

postするとgetの方に”hello”がでる
% curl -d 'data=hello' http://localhost:4567/comet/test


chat


チャット
http://shokai-comet-chat.herokuapp.com/


ソースコード https://github.com/shokai/sinatra-comet-chat


/chatにgetするとlong pollingで待たされるので
https://github.com/shokai/sinatra-comet-chat/blob/master/controllers/comet.rb


ajaxしてレスポンスを待つ。受信したらすぐajaxしなおす。
https://github.com/shokai/sinatra-comet-chat/blob/master/public/js/chat.js

このchatには同時に1000接続までなら動いた。1200ぐらい接続したらthinごと死亡した。


FirefoxとChromeは、同じマシンで2つウィンドウを開いてチャットすると交互にしか受信できない。

Safariは問題ない。Chromeはシークレットモードだと両方受信できるらしい。

0

Rubyでcometサーバー作る

最近cometとかいう最新技術が流行っているらしいので、eventmachine_httpserverで作ってみた。


ここにサーバーとクライアントの例がある。どっちも50行ぐらいで実装できた。
comet at master from shokai/eventmachine-study – GitHub

サーバー起動して、タイムアウトを10秒に指定。
このサーバーは、POSTされた値を保持して、GETされたら返す。GETに対してはレスポンスを遅らせて返す。

ruby server.rb 8080 10


クライアントを起動。GETしてから25秒後にPOST
ruby client.rb 25
GET(comet) -> wait 25 sec -> POST
* GET
sleep 25 sec
404 ## 10秒経過、切断された

* GET ## 再接続
404

* GET ## 3回目
* POST kazusuke
POST success
200
kazusuke
200 ## 5秒待ってようやく値が返ってきた
kazusuke
* GET

サーバー側のログはこんなんなってた
http server start, port:8080, comet_timeout:10(sec)
load: 2.20 cmd: ruby 11481 waiting 0.45u 0.32s
request_method : GET
path_info : /message
query_str :
post_content :
request_method : GET
path_info : /message
query_str :
post_content :
request_method : POST
path_info : /message
query_str :
post_content : kazusuke
kazusuke
request_method : GET
path_info : /message
query_str :
post_content :

cometサーバー、接続が不安定なクライアントにpush通知するのに便利。