キモブロ

Please spy check please, Fucking retard

Peercastでどんな配信してるか可視化してくれるサイト作った。

http://peercast.kymt.me/

Peercastって、Peercastをインストールしないと見れないのでどんな配信やってんだよって思うこと多いし、実際そのせいでPeercastリスナーの大部分が失われてる気がする。これは機会損失だ。そこで各配信の映像情報をキャプチャして一覧してくれるサイト作ってみた。

仕組み

4つのスレッドで最大4つの配信にPeercastのプロトコルでアクセスし、先頭8秒後の1フレームを取得する。こうしたのは最初の8秒ぐらいはバッファのせいで映像情報が乱れていることが多かったから。

実装

Rubyのparallelライブラリがすごく便利だった。こんな感じにするだけで並列実行できる。

require 'parallel'

Parallel.each([1,2,3,4,5], :in_threads => 2){ |number|
  # no operation
}

また、自作ライブラリなんだけどretry-handlerってのが便利で、このライブラリは失敗した時にN回リトライするってのを簡単にかける

Proc.new{
  # なんかエラーの起こるコード
}.retry(:max => 3, :accept_exception => StandardError)

とか書いておくとなんかProc内で例外(この場合はStandardErrorなので、ほとんどの例外が対象)が発生した時最大3回まで、
Procのコードをリトライしてくれるというのが簡単にかける。前の会社こういう仕事多かったのでこういうライブラリ作っておいた。勤務時間外に作って仕事でも使っていただけなのでコンプライアンス的に問題ない。


あと:waitってオプションがあってこれ指定するとエラーがあったとき、3秒間waitのあと、次の試行してくれるようになる。
retry-handler自体の処理の流れを可視化したいときは:loggerオプションを付加すると良い。

lambda{}.retry(:max => 3, :wait => 3)
lambda{}.retry(:max => 3, :wait => 3, :logger => Logger.new(STDERR)

ちなみにProc.newと書くのがダサいときは、lambdaでもbegin-endでも使える

lambda{}.retry(:max => 3)

begin
end.retry(:max => 3)

このライブラリはgithubでlurker時代からおいてあるけど誰も興味を持ってない。
http://github.com/kimoto/retry-handler


今後機能拡張も考えていて、たとえばTCP/IPプロトコルみたいに、失敗したときはどんどん次のリトライまでの時間を遅延させるみたいなのも書けるようにしようかなと思ってる。これあるとだいぶネットワークリソースを独占しにくくなって良い。たとえば初回は1秒後にリトライ、2回目は3秒後にリトライ、3回目は6秒後にリトライ、最終的には30秒ぐらいで逓減っていうか、安定するみたいな感じのコードがさくっと書けるようにしようかなと。もちろんこの値は配列かなんかで指定できるようにしたい。


実際にこのParallelと、retry-handler使うと、こういった複雑?な処理がかなり綺麗にかけて、たとえば今回作ったこのPeercastスクリーンショット取るコードはこんな感じ。

    Parallel.each(last_infos, :in_threads => 4){ |last_info|
      begin
        Proc.new do
          timeout(20) do
            Peercast::Screenshot.new(last_info.url).download_to(download_path)
          end

          last_info.capture = download_path
          last_info.save
        end.retry(:max => 3, :accept_exception => Timeout::Error, :logger => Logger.new(STDERR))
      rescue RetryOverError
        STDERR.puts "reach retry max count"
      rescue => ex
        STDERR.puts "other error"
        STDERR.puts ex
      end
    }

4つのスレッドで、最大4つの配信の画面を同時キャプチャして、20秒で反応がなかったらタイムアウトさせて、タイムアウトしたときは3回までリトライ。それ以外の例外の時は失敗としてログを取り、一切リトライしない。完全にretryに失敗したときはRetryOverErrorが発生するのでそれをキャッチして、無視、みたいな。

なんかソースコードハイライティングがうまくされてないな、俺の書き方が悪いんだろうけどカラフルで見れるようにgistに貼っといた
https://gist.github.com/1975905