月が綺麗でした。

帰り道、空を見ると月が綺麗でした。

何気ないことを綺麗だと思うことを忘れない様にしたい。というひとりごと。

bit.lyで短縮されたURLのクリック数をRuby(Nokogiri)でスクレイピングする

f:id:hideack:20120504212312p:image

bit.lyで短縮されたURLのクリック状況は短縮されたURLの末尾に'+'をつけると知ることができるので、そこからクリック数をスクレイピングしてみようという話。*1

# bitlycun.rb
require 'nokogiri'
require 'open-uri'
url = "https://bit.ly/#{ARGV[0]}+"
page = open(url)
html = Nokogiri::HTML(page.read, nil, 'UTF-8')
html_a = html.search('//span[@class="count"]')
tmp = html_a[0].children.text
clickcun = tmp.gsub(/(\r\n|\r|\n)/, "")
puts "http://bit.ly/#{ARGV[0]} : click count = #{clickcun}"

スクリプトを実行する際、引数にbit.lyで短縮した際の可変部分(http://bit.ly/***** の ***** ) を第一引数に渡せば現在までのクリック数を取得することができる。

% ruby bitlycun.rb IxW4jJ
http://bit.ly/IxW4jJ : click count = 3

*1:ネタとして探したらあちこちにありそうだったけど…(略

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アカウントが必要です)

http://remp.rockf.es/hello/

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

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

Hi!

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

thinサーバをプロセス別に再起動させる方法

自分用メモ。
サーバ上で起動させているThinサーバを再起動させたい場合、

$ thin -C thin.yaml restart

と行うと、yamlでthinのプロセスが常駐する様に設定していた場合、一度全部のプロセスを閉じた後、設定数分のプロセスの起動が行われるため、接続が不可能な状態が一瞬生じます。
せっかく複数プロセスが稼働しているのであれば、一つずつのプロセスで再起動がかけられればよいので、方法を調べたところ --onebyone というオプションを付与することで実現できました。

$ thin -C thin.yaml --onebyone restart

といった具合。
こうすると、以下の様に個々のプロセスの停止と起動が繰り返されます。

$ thin -C thin.yaml --onebyone restart
Stopping server on 0.0.0.0:4040 ...
Sending QUIT signal to process 12835 ...
>> Waiting for 1 connection(s) to finish, can take up to 30 sec, CTRL+C to stop now
>> Exiting!
Starting server on 0.0.0.0:4040 ...
Waiting for server to start ...
Stopping server on 0.0.0.0:4041 ...
Sending QUIT signal to process 12846 ...
Starting server on 0.0.0.0:4041 ...
Waiting for server to start ...
(以下略)

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

「Titanium Mobile」で作るiphone/Androidアプリ勉強会に参加してきました。

Web CAT Studio (株式会社リクルートエージェント)主催のTitanium Mobileの勉強会に参加してきました。
200名近く参加されており、やはり注目の技術なのだなと実感しました。(7割近くがエンジニアの方, あとはディレクターやデザイナーの方)

ひと通り聴き終わっての感想は…。

  • Titanium mobile = Android & iPhoneアプリが一気にできると思ってしまうけど現実は甘くない
    • 現実はソースコードの中でOS別に実装分けたり、最初から単一の環境をターゲットにしたりしている
  • ただ、iPhoneAndroidどちらか一方をターゲットにしたアプリを作るのであっても、Titanium mobileで作るメリットは大きそう (実装スピードの面で)
    • ゴリゴリとした描画や処理が必要無いアプリ(既存のWebアプリに数字を送るとか、既存のスマホ向けの画面を引っ張ってくるとか)であれば、むしろ書きやすそう
  • 灰色ハイジさん(普段はデザイナー)がアプリを作られた経験の話を聞くと、アプリ開発者のマスが広がりそう
    • スケッチから簡単な動きのあるモックをデザイナーの方が作って、細かいイベントの呼び出しやサーバ間通信、サーバサイドの実装をするといったことができたりしないだろうか(妄想)

といった具合です。
何はともあれ面白そうなので、早速何かつくってみようかな…。

以下、雑ではありますが、参加した際に作成したメモです。

Titanium Mobile intro 増井 雄一郎さん

  • HTML5をモバイルアプリに使えるのか?
    • 進化途中。Websocketで2年半…。
    • そもそもストア, Androidマーケットに載せられない
    • Java, Objective C の学習コストもある
  • HTML5 vs Titanium ?
    • 競合はしない
      • グラフとかの描画系のところだけHTML5を使うことと使い分けができる
  • 実際に使われているの?
    • 開発者数 = 30万, 3万5000件のアプリ(=iPhone & Androidアプリ)
    • niftyのココログエディタ, サイボウズLive, amadanaケータイの組み込みアプリ
    • Cybirdのアプリ(AKBのアプリ)
    • モバゲーで公開中のゲーム (ライトゲーム, 指のジェスチャーでキャラクターを切るとか)
    • MogSnap (食べ物の写真共有)
  • モバイルアプリに大事なこと = User Experience
    • Titaniumであれば、「タブを作る」という実装はそれぞれのOSにあわせた変換が行われる
  • どこまで書けるのか?
    • 大体OSがサポートする全機能使えます
  • できないことはないのか?
    • 画面描写系は弱い (線を描画する等)
    • 写真の撮影はできるが、画像のフィルタリングは厳しい
    • シューティングゲームとか更新の激しいものは厳しい

ただし、ObjCとかJavaで拡張できる。(但し両方OSに対応する必要あり)
有償のモジュールも使える

  • Titanium → Write once run anywhereではなくて、モジュールで柔軟に対応できる
    • (モジュールで柔軟に対応できることを表したキーワードを増井さんが言われたのだけどメモできなかった…)
  • やっぱり得意なのは...
    • フロントエンド系モバイルアプリの開発
  • おすすめする本・Webサイト等の話
    • Titanium Mobileアプリ開発入門
    • Titanium Mobileで開発するiPhone/Androidアプリ
    • gihyo titanium (倉井さんの連載)
    • ti.masuidrive.jp (増井さん個人のページ)
  • ところでAppcelerator社のビジネス
    • Titanium用追加Native Module (グラフ描画とか) の販売
    • サポート
    • 認定トレーニング(12万円, 2日間)→トレーニングの最後のテストにパスするとロゴ等が利用できるらしい。

Titanium Mobileで作るアプリ事例紹介1【Zaim】 閑歳 孝子さん

  • 自己紹介(ユーザーローカル所属)
    • IT系ニュースサイトの記者からWebディレクター、そしてWebエンジニアへ
  • サービスの紹介
    • zaim(ザイム)
      • ソーシャル家計簿アプリ (半年前にリリース)
        • 簡単に入力できて他の人と比較ができる!
      • 日本語&英語対応, 20貨幣対応 (円から南アフリカの通貨まで), 8万ダウンロード
      • お金のAPIが面白そうだったのでOAuth APIも提供しています。
        • レストランに食事へ行ったときに写真を取って写真は別サービス、会計情報はzaimへ!
        • OCN家計簿は連携例
      • Android版は現在準備中…。
  • サービスのリリーススケジュール
    • 2010年末に思いつく
      • まずPHPでサーバサイドを開発→Titanium mobileでiOS対応版開発→リリース
  • なぜTitanium mobile?
    • Objective C, Javaの経験がなかった
    • 昨年2月リリースされたMogSnapがTitanium mobileで作られたことを知った
    • Titanium mobileの素晴らしいところ
      • サンプルが多い
      • Javascriptがかければよい (エンジニアではない人ももっとやるべき)
      • コミュニティがしっかりしてる (質問したら回答してもらえる環境)
  • Titaniumの注意
    • 現実問題として両対応は一筋縄にはいかない。
    • リッチなアプリは厳しい感じ (※ここは増井さんも言われていた通り、やはり
  • 実装
    • Common JS・シングルコンテキストで実装するスタイルが推奨されている(らしい)
      • Android対応アプリを作るためにはこのスタイルが必須なのでは?
      • Githubにサンプルが多く上がっているので見てみるとよい
    • CoffeeScriptのサンプルも参考になる
  • 実際のソースコード
    • 共通モジュールを読み込む → 設定 → 部品のイベント実装

問題事例集… (悩めるAndroid)

  • デザイン問題
    • Androidグラデーション効かない
    • 画面の比率がひとつじゃない
    • JSSは少し使いづらい (JSS=Titaniumが提供するスタイル設定)
      • なので独自でスタイル設定用のライブラリを作っている
  • スプラッシュ画像問題
    • iPhone, iPadは規定のサイズで済むが、Androidは比率が一定でないのでボヤケる…。
    • Andoirdは単色塗りつぶしがオススメ
  • TableView問題
    • iOS向けだとgetChildrenでテーブル内の要素にアクセスできたけど、Androidだとアクセスできない!
      • 行情報をアプリ上で保持していて、修正が発生した場合は全てテーブルを作り替える様な形で対応した…。
  • OAuthAdapter問題
    • oauth_adapter.js (1種類のOAuth providerが前提になってる)
    • 変えたものをgithubで公開しますー。 (http://github.com/unicco )
  • ボタン&タブバー&ピッカー問題
    • Android OSとiOSで全く違う
    • OS別に呼び出すオブジェクトを分けている
  • フォーカス問題
    • Androidだと親ウインドウにフォーカスが効かない…。
    • Androidだとfocus, blur が同時に発行される…。
  • 多言語対応問題
    • Androidだとうまくいかないケース多数 (スペースやカンマなどが入るとNG)
      • 多言語対応が便利なスクリプトもgithubに公開しますー。

情報源

  • github
  • 最新版ビルド

絶賛募集中

Titanium Mobileで作るアプリ事例紹介2【積ん読本】 灰色 ハイジさん

  • 開発したアプリ
    • "積ん読本"
      • 社内プロジェクトで始まったけど、時間確保が困難になる… → 自分で企画+デザイン+実装することに! (開発期間2週間くらい)
      • 買って読んでいない本を管理する (デザイナだから見やすいUIで管理したい)
  • Titaniumだと簡単にテーブルが作れた!→デザイナーでも実装できそうだと思えた
  • デザインを忠実にするために
    • Titanium標準のナビゲーションバーを使うと自分の想定した色を再現できない…。→ナビゲーションバーを自作して解決。
    • スクロール
      • 通常は上から下へだけど「積ん読」をさせるには下から上へスクロールするできるUIが欲しい
      • ScrollViewに高さ4400pxの背景画像を入れて、積ん読の画像をTable viewで重ねて .scrollTo メソッドでスクロールさせた
        • そうするとスクロールしている最中の様子がみえてしまう…
        • interval入れてスクロール状況を監視させて、スクロールが完了したら表示させる様にした。
    • アイコンの話
      • 細かいところまでこってます。
        • f:id:hideack:20120214205103p:image
        • 実は背表紙の文字にそれぞれ意味ある言葉が入れてある。アイコンだけど拡大されたときに読める様に。
        • iPhone用にアイコンを作るときには「角丸処理」重要。(Apple側で角取り処理されてもきれいな縁が出る様にしておく)
      • アイコンを作るまで…。
        • レイヤーをたくさん重ねて作ってます。
        • デザイナーがどうやってアイコンとか作ってるかを知ってもらいたかった