REMP

twitter botに機能追加 – REMPその後(その3)

REMPには@ というtwitterbotがあって定期的に今までもREMP上で直近再生されたYoutube動画をツイートさせていたのですが、最近昨日を足して以下の様な内容をツイートさせる様にしました。

内容的には直近再生された動画ではてなブックマーク数が大きいものでソートしてランキングとしてツイートさせています。例えば2月6日だと以下の様な具合。


眺めていると最近注目されている曲が上がって面白かったりします。 興味がある方は是非、@のフォローを!
あと単純な様ですが、ランキングを作るためのパラメータ収集をする仕組みは別途作ったので紹介はまた今度。。。

REMPのドメイン変更

今日の定時後に @ とREMPのサービスドメインを http://www.remp.jp/ に変更する作業を一緒に行いました。
今までは、アプリケーションサーバ(Sqale)にremp.rockf.esという独自ドメインを充てて動いており、remp.jpドメインからはリダイレクトされていましたが、今度からは逆でremp.jpが主ドメインになります!

f:id:hideack:20130201235232j:plain

実はステッカーは以前から www.remp.jp だったのでステッカーに揃いました。

ステッカーを作りました! – REMPその後(その2)

機能等の改良の話題ではありませんが、REMP(http://www.remp.jp/hello )のステッカーを作成しました。
REMPチーム関係者に声かけていただければ差し上げます!

http://instagr.am/p/U7iFVnydYh/

@ がデザインから入稿まで全部やってくれて感謝感謝…。
REMPチームのプロダクトに関してデザイン周りのこと全く貢献できてないので多少は勉強しよう。

REMPその後(その1)

REMPとは?

そもそもREMPとはなんぞや?という話もあるので簡単にご説明。
REMPは@Chromeアプリとして作成していたYoutube動画でプレイリストを作成して連続再生することができるアプリをfacebook連携する様にしたWebサービスです。
Youtube動画をプレイヤー上から検索することが出来て、その結果から好きな動画だけを自分のプレイリストとして作成することもできます。

(1) http://remp.rockf.es/facebookログインしてプレイヤーを開く

(2) プレイヤー画面右上にある検索窓で検索したい動画のキーワードを入力

(3) 検索結果が「ライブラリ」の列に追加

このままでも十分使えるのですが、自分の好きな動画だけを集めたプレイリストを作ることもできます。

(4) プレイリストを作成

(5) 検索結果のプレイリストから自分がオリジナルで作成したいプレイリストに動画をドラッグ&ドロップ

こういった形で「検索」→「プレイリストへの追加」を繰り返すことで自分のお気に入り動画だけを集めたプレイリストが作成できます。
その他機能いろいろあるのですが詳しくは http://remp.rockf.es/hello をご参照ください。

最近の更新内容

そんなREMPですが、昨年暮れから新年早々いくつか機能追加しましたのでご案内。

プレイリスト名の編集対応

作成したプレイリストの名称編集ができる様になりました!!
(いままでプレイリスト名が直せなかったのです…)

facebookページを作りました

今更ながらfacebookページを作成しました。

サーバをSqaleに移行&フレームワークをPadrinoに移行

ストーリーボードと同様に今まではVPS上で独自に運用していましたが、サービス本体をpaperboy&co.Sqaleに移行しました。
そのタイミングに併せてREMPの実装を行ったフレームワークSinatraからPadrinoへ移行しました。このあたりはまた詳しく…。

といった具合で、年末年始からいろいろやってますよ。というお話でした。

REMP Ver.2.5 リリースしました(してました)

と、twitterではお知らせしたのですが、改めて。
REMP Ver.2.5をリリースしました。*1
見た目上の大きな変更や派手な機能追加は無いのですが、以下の様な点が改善されています。

  • サーバサイドの設定をチューニングして処理の高速化
  • Friendsのログイン状態表示対応
  • デスクトップ通知対応

機能的に新しくなったのはログイン状態表示とデスクトップ通知になります。

ログイン状態表示

REMPの操作画面の左手にfacebookのfriendsが表示されますが、そのリスト表示部分の名前とプレイリスト数の横にログインしていれば緑色のランプアイコンが表示されます。

f:id:hideack:20120408175751p:image

デスクトップ通知

REMPでは、自分のfriendsに対して自分が見つけたYoutube動画を送ることができるのですが、もし誰から動画が送られた瞬間にログインしている状態であればChromeのデスクトップ通知の機能を利用して画面上にポップアップして通知させることができます。ちょうどイメージ的には、メールソフトでメールを受信したときに表示されるNotifyのポップアップの様なイメージです。
また、自分の友達がREMPにログインして操作開始したことも同様にデスクトップ通知で知ることができます。
設定は、画面左下の歯車のアイコンで設定をすることができます。
f:id:hideack:20120408175752p:image

あと、細かいところではブラウザサイズを小さくした際にREMPのプレイヤー上部に表示される再生ボタン等のサイズが絶妙に調整される様になっています。細かいところで@のコダワリが出ています。*2

自分が担当したサーバ側の技術的な部分では、通知系にはPUSHERを利用したWebsocket経由でのフックを使っていたり、ログアウト検出用に独自のお手軽メッセージキューを使ったりしていますが、そのあたりはまたブログに書いていきたいと思います。
(PUSHERの話題は、このあたりを参照)

ここまで読んでREMPを使いたくなった人はこちらから。
(facebookアカウントが必要です)

*1:そういえば、Webアプリなのにバージョン番号つけるの珍しい気がする

*2:毎回さすがと思うところ

unicornを使ってみた(2) – REMPにおけるメモリ利用量の変化

では、実際に現在のREMPの場合において、どの程度メモリ使用量が変化したのかを確認してみました。
Apacheをフロントのリバースプロキシとして、背後にRack用のWebサーバ(Thin or unicorn)を動かすという挙動はかわらないので、Thinとunicornの両方のサーバを立てて、リバースプロキシ(mod_proxy)で均等に複数のプロセスで起動させているthinサーバとunicornにアクセスする様にして3分毎に実メモリ使用量を監視してグラフにしてみました。
mod_proxyの設定は以下の様な形にしています。以下の様に設定することでthinの1プロセスとunicornへ均等にアクセスがされる様にしています。

<IfModule mod_proxy.c>
(中略)
<Proxy balancer://***>
BalancerMember http://127.0.0.1:4040 loadfactor=10  #thinのプロセス(1/2)
BalancerMember http://127.0.0.1:4041 loadfactor=10  #thinのプロセス(2/2)
BalancerMember http://127.0.0.1:4044 loadfactor=20  #unicornのプロセス
</Proxy>
</IfModule>

で、この様な設定にした状態でREMPの本番環境にしてみたところ、得られた各プロセスが消費しているメモリ使用量の変化は以下の様な具合です。
(赤色の線がThinの1プロセス vs 緑色の線がunicornのマスタープロセス+水色の線がunicornのワーカープロセス で比較すればよいのかな。と。)

f:id:hideack:20120318221048p:image

少なくてもREMPのAPIサーバ側のSinatraで実装したアプリをThinの上で稼働させた場合とunicornで稼働させた場合を比較するケースでは、単純に見ると半分程度にメモリ使用量を押さえることができる様です。

もう少し更に検証したいと思います。

unicornを使ってみた(1) – 導入

REMPで今までApacheのリバースプロキシを通してthinサーバでAPIを稼働させていたのですが、稼働時間が長くなるとメモリの利用割合が増える状況が続いていたため、どうしようかと悩んでいたところ会社でmizzyさん(@)からunicornだとメモリ利用量が押さえられるという話を伺ったので早速切り替えてみました。

unicornは、Rack 環境をロードした master プロセスが fork して子の worker を作る仕組みになっているため、安定性とメモリ利用量を小さくすることができることが期待されます。
unicornのインストールはgemを使うことで簡単に行えます。

$ sudo gem install unicorn

現在、REMPのAPIサーバはthinで動いているのでアプリケーションのルートにunicornの設定ファイルを書き足すことでunicorn上でアプリをすぐに動かすことができます。
ここでは、

  • Apacheのリバースプロキシから内部の4044番ポートにフォワード
  • unicornのワーカーは4本作る
  • preload_appを有効にして、workerをforkする前のアプリケーションの先読みを有効にする

設定例を書いています。

# unicorn.conf
listen "/tmp/unicorn.sock", :backlog => 64
preload_app true
port = 4044
listen port
worker_processes 4
pid "tmp/pids/unicorn.pid"
stderr_path "log/unicorn_error.log"
stdout_path "log/unicorn_out.log"

この設定ファイルを書き足せば以下のコマンドですぐに動かすことができます。
(-Dでデーモン化)

$ unicorn -c unicorn.conf -D

設定ファイル内のログに正常にログが吐かれていれば、稼働していることを確認することができます。

$ cat log/unicorn_error.log
125.***.***.*** - - [18/Mar/2012 18:56:11] "GET /status/info HTTP/1.1" 200 2 0.0045
125.***.***.*** - - [18/Mar/2012 18:56:11] "GET /status/info HTTP/1.1" 200 2 0.0531

mongo DBのバックアップファイルから特定のドキュメントをリストアする

REMP開発中に誤爆して本番環境上の開発者のプレイリストがうっかり意図しない内容に更新してしまうというケースが生じたため、デイリーでとっているmongo DBのバックアップから復旧させてみました。*1
行いたいのは、特定のユーザのプレイリストのみの復旧なので、単純にmongorestoreする訳にはいかないので、方法として以下の手順で復旧させました。

  1. バックアップファイルを展開する
  2. ローカル環境のmongo DBに一度まるごとリストアする
  3. ローカルにリストアしたmongo DBから該当ユーザのプレイリスト情報(特定のコレクション内の1ドキュメント)をJSON形式で保管する
  4. ローカルで出力したJSONをサーバ側のmongo DBでインポートする

以下、復旧した際のメモをば…。

バックアップファイルを展開する

まず、保管されていたバックアップファイルを展開します。

[ローカル環境]
% tar xvzf rempdb-20120228.tgz
rempdb-20120228/
rempdb-20120228/rempdb/
rempdb-20120228/rempdb/library.bson
(中略)

ローカル環境のmongo DBにリストアする

前段で展開されるバックアップファイルの中身のファイルがJSONであれば、直接テキストファイルなので編集して該当するユーザのコレクションのみの抽出も行えるのですが、BSONのため一度手元のmongo dbに取り込んで処理することにしました。
リストアはmongorestoreコマンドを使えばよいので、リファレンスに従って操作して、

[ローカル環境]
% mongorestore -d rempdb --drop rempdb-20120228/rempdb
connected to: 127.0.0.1
Wed Feb 29 17:43:12 rempdb-20120228/rempdb/library.bson
Wed Feb 29 17:43:12 going into namespace [rempdb.library]
Wed Feb 29 17:43:12 dropping
(中略)

これで手元のmongo dbにバックアップファイルの内容がリストアされました。
(コマンドの末尾のパスはバックアップファイルの展開先)

該当ユーザのプレイリスト(コレクション内のドキュメント)をJSON出力する

サーバ側で復旧させたいユーザのREMPのプレイリストを抽出するためmongoexportコマンドを利用して、該当ドキュメントのJSONファイルを出力させます。
mongoexportコマンドを使うとDBやコレクションを特定し且つ、クエリを与えてヒットしたドキュメントのJSONだけを出力することができます。

[ローカル環境]
% mongoexport -d rempdb -c library -q "{uid:'1234567'}" -o playlist.json
connected to: 127.0.0.1
exported 1 records

中身を見てみると。。。

[ローカル環境]
% cat playlist.json
{ 〜 , "uid" : "1234567" }

と、クエリで抽出されたドキュメントのJSONファイルがそのまま出力されているはずなので、その内容がリストア対象であることを確認します。

サーバ側で稼動しているmongo DBへローカル環境で出力したJSONをリストアする

ローカル環境でまるごとリストアしたDBから出力した特定のドキュメントのJSONをサーバ側でインポートします。
mongoimportコマンドを利用することでJSONファイルをmongo DBにインポートすることができるので、以下の様にコマンドを入力。

[サーバ環境(リストアしたいDB側)]
% mongoimport -d rempdb -c library playlist.json --upsert
connected to: 127.0.0.1
imported 1 objects

これでサーバ上のmongo DBへバックアップファイルから取得した特定のドキュメントのみを復旧させることができました。

上記の方法でリストアできない (あるいは、--upsertオプションが効かない場合)

mongoのバージョンが古い場合、mongoimportコマンドに --upsertオプションが無い場合は、一旦ドキュメントを消してインポートすると良い…のかな。

MongoDB shell version: 1.*.*
connecting to: ***
>db.**.remove({uid:"1234567"})
>

で一度消して、

% mongoimport -d rempdb -c library playlist.json
connected to: 127.0.0.1
imported 1 objects

といった具合。

*1:あ、念のためですが他のユーザの方に影響はありません。開発用のクライアントを操作している際に生じたケースです。