2013-03-24
Ruby を使って Fitbit API で遊んでみた
ハイテク万歩計の Fitbit One を購入して遊んでいる。
ゲーミフィケーション的な要素があり、日頃の運動不足解消に一役かっている。
Fitbit 自体は、体重や体脂肪、睡眠時間や摂取カロリーなど健康に関する数値のデータベースを目指しているように見える。
最近の Web サービスらしく RESTful API を提供しており、データを流し込むことができるため、別サービスに貯めていた体重と体脂肪を Fitbit にインポートしてみた。
インポータ
実行には、Ruby と Bundler が必要。
OAuth の登録
dev.fitbit.com にアクセスして、consumer キーと consumer secret を取得しておく。
準備
$ git clone git://github.com/mallowlabs/fitbit-weight-importer.git $ cd fitbit-weight-importer $ bundle install --path .bundle $ cp config/fitbit.yml.example config/fitbit.yml $ vim config/fitbit.yml consumer / consumer key を記述する
CSV のフォーマット
以下のフォーマットで体重と体脂肪を準備する。
"Date","Weight","Fat" "2012-09-09","54.80","16.90" "2012-12-09","56.30","17.00"
実行
以下のコマンドで実行をする。
途中で URL が表示されるので、ブラウザでアクセスし、表示される PIN コードを入力する。
$ bundle exec ruby bin/main.rb data.csv
技術的な話
Fitbit API は素直な OAuth + RESTful API なので難しいことは何もない。
Fitbit API - API - Confluence
とはいえ、クライアントライブラリがあると便利である。
公式で紹介されているクライアントライブラリは、Java / PHP / .NET しかないが、 Ruby の 3rd パーティのライブラリとして Fitgem がある。
ドキュメントも充実しているため、Fitgem Reference Client を読めば困ることはないだろう。
この記事を読んで興味を持った方は Fitgem を使って何かサービスを作ってみてください!
2013-03-15
Tomcat7 でゼロダウンタイムデプロイ
「Web アプリのバージョンアップ時に Tomcat を再起動してもいいのは小学生までだよねー」
ということで、Tomcat でダウンタイム無しで Web アプリのバージョンアップをする方法についてまとめてみる。
Parallel Deployment
Tomcat 7 から Parallel Deployment という機能が追加され、同一 Web アプリの複数バージョンを同時にデプロイができるようになった。
war のファイル名を somewebapp##001.war 等にしておくことで、
- $CATALINA_BASE/
- webapps/
- somewebapp##001.war
- somewebapp##002.war
のように配備をすると、 http://localhost:8080/somewebapp/ でアクセスした場合に、セッションが継続している場合には古い方(001)を、そうでなければ新しい方(002)を Tomcat が勝手に使ってくれる。
この機能により、バージョンアップ時には連番をインクリメントした war ファイルを置くだけで、ダウンタイム無しにバージョンアップができるというカラクリになっている。
Tomcat の Web アプリケーションマネージャを見ると、デプロイされているバージョンや、どちらのバージョンの Web アプリのセッションが生きているかなどを確認することができる。
古い方の Web アプリのセッション数が0であれば、アンデプロイしてしまっても問題ない。
連番は Jenkins のビルド番号をつけると便利である。
私のチームの場合は、M2 Extra Steps Plugin でハードリンクを作成している。
画像内のコマンドは以下
$ ln "${WORKSPACE}/target/somewebapp.war" "${WORKSPACE}/target/somewebapp##${BUILD_NUMBER}.war"
VirtualWebappLoader
ここまでで、ゼロダウンタイムデプロイは実現できそうなのだが、私のチームではそのままでは使えなかった。
というのも、webapps/somewebapp/WEB-INF/lib/ の下にプラグインと称して、追加の jar をデプロイ後に置く運用になっていたためだ。
また、環境による細かな設定変更も webapps/somewebapp/WEB-INF/classes/ にプロパティを置くこともしている。
この運用だと、war を置くだけではデプロイが完了しない。
そこで目をつけたのが Tomcat 7 から正しく動くようになった VirtualWebappLoader というクラスローダである。
Web アプリの src/main/webapps/META-INF/context.xml に以下のような内容を書いて置く。
<?xml version="1.0" encoding="UTF-8"?> <Context> <Loader className="org.apache.catalina.loader.VirtualWebappLoader" searchExternalFirst="true" virtualClasspath="${catalina.base}/extensions/somewebapp/WEB-INF/classes; ${catalina.base}/extensions/somewebapp/WEB-INF/lib/*.jar" /> </Context>
context.xml の設定により $CATALINA_BASE/extensions というディレクトリを作成し、その下に追加で読み込ませたいファイルや jar ファイルを適切に配置すれば、デプロイ時に読みこんでくれるようになる。
重要なのは searchExternalFirst 属性で、これを true にしないと extensions 以下のファイルが優先されず、意図通りに動作しない。
注意事項
ここまでで、ゼロダウンタイムが実現できるが、メモリ周りは注意が必要である。
具体的には、内部的には二個のアプリをデプロイしているのと同じなので、Permanent 領域もやはり2倍使う。
例えば、2回のデプロイをした時に PermGen の使用量の遷移は以下のようになる。(Java VisualVM で確認)
そのため、Tomcat の起動オプションの -XX:MaxPermSize を十分に大きく取っておく必要がある。(アンデプロイしても PermGen の使用量は減らない?詳しい人教えてプリーズ)
2013.03.21 追記
id:bati11 さんからコメントを頂きました。
Tomcat の起動オプションに以下のオプションを追加すると PermGen スペースも開放されるそうです。
- -XX:+CMSPermGenSweepingEnabled
- -XX:+CMSClassUnloadingEnabled
ただし、メモリリークの問題が無いというのが前提です。
(私のプロジェクトで試したら開放されなかった…)
まとめ
2013-02-18
Jenkins Bitbucket OAuth Plugin 書いた
Jenkins に Bitbucket のアカウントでログインしたいなーと思ったので書いてみた。
mallowlabs/bitbucket-oauth-plugin ? GitHub
設定画面はこんな感じ。
Bitbucket の権限設定とは全くリンクしないので、誰か対応して Pull Request 下さい。アップデートサイトに登録する気もあんまり無いので、万が一必要な人がいたらアピールしてください。
(2013/05/30 追記) まさかのアピールを受けたのでアップデートサイトに公開しました。
プラグインの機能については想像通りだと思うので、 以下に Bitbucket の OAuth を使う時に注意すべき点などをメモしておく。
参照すべきドキュメント
- OAuth on Bitbucket - Bitbucket - Atlassian Documentation
- oauth Endpoint - Bitbucket - Atlassian Documentation
- user Endpoint - Bitbucket - Atlassian Documentation
注意点1: Consumer ID/Secret の取得
ドキュメントによっては Consumer ID/Secret を「メールしてくれたら送るよ」と
書いてあるが、現在はアカウント設定から自由に取得できるの注意。
名前だけの入力で発行できるので Github よりお手軽なのは Good.
注意点2: OAuth エンドポイントの URL
ドキュメントによって OAuth の API の URL が異なっている。
https://bitbucket.org/!api/1.0/oauth/request_token
とか
https://bitbucket.org/api/1.0/oauth/request_token
などと書いてあるが
BitbucketのOAuth:access token取得まで #Python #bitbucket - Qiita [キータ]
を参考に!無しの URL でうまくいった。
他の RESTful API は
https://api.bitbucket.org/1.0/
とかだったりするのでわかりにくい。
注意点3: callback の verifier
ドキュメントにははっきりと書いておらず、画像の中にこっそりと書いてあるが、
oauth_callback を指定した時に返ってくる時の verifier が入っているパラメータは oauth_verifier である。
OAuth 1.0a の仕様通りと言えば仕様通りなのだけど、他のパラメータはちゃんと書いてあるのにここだけ抜けてるので不親切な感じはする。
2013-02-04
Treasure Data を使って開発者向けチャットアプリ AsakusaSatellite メッセージログの解析をしてみた
HerokuからTreasure Dataに標準出力でデータインポートできるようになって多幸感がすごい - ハードコイルド・ワンダーランド というエントリを見て、Treasure Data が導入の手間をこれだけ下げてくれているのに使わないのは失礼にあたる、ということで使ってみました。
また前提として、プロダクションコードには一切手を入れないということも意識しました。
構成
今回の構成は、すでに Heroku 上で動作している AsakusaSatellite に対して、Heroku 上に立てた bot (as-treasure-data-logger) が AsakusaSatellite API を定期的にキックして、データを JSON で標準出力に吐き続ける、という形にしました。
AsakusaSatellite の裏にいる MongoDB (MongoLab) を直接触ってもよかったのですが、API の使い勝手のテストも兼ねて API 経由にしています。
as-treasure-data-logger
as-treasure-data-logger は Heroku 上で動く bot として実装されています。
Treasure Data Hadoop と Redis To Go の2つの Heroku アドオンを追加しています。
Redis は最後に取得したメッセージの ID を覚えておくためだけに使っています。
この bot が定期的に AsakusaSatellite にメッセージ一覧を取得しに行き、標準出力に JSON を吐きます。
Treasure Data アドオンが有効になっていれば、標準出力が拾われてどんどんデータが溜まっていくという形です。
bot のコードが短いので貼っておきます。
#! /user/bin/env ruby # -*- mode:ruby; coding:utf-8 -*- require 'json' require 'open-uri' require 'redis' $: << "." ASAKUSA_SATELLITE_ENTRY_POINT = ENV["ASAKUSA_SATELLITE_ENTRY_POINT"] ASAKUSA_SATELLITE_API_KEY = ENV["ASAKUSA_SATELLITE_API_KEY"] ASAKUSA_SATELLITE_ROOM_ID = ENV["ASAKUSA_SATELLITE_ROOM_ID"] REDIS_URL = ENV["REDISTOGO_URL"] COUNT = 50 uri = URI.parse(REDIS_URL) redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password) loop do since_id = redis.get("since_id") url = "#{ASAKUSA_SATELLITE_ENTRY_POINT}/message/list.json" url += "?api_key=#{ASAKUSA_SATELLITE_API_KEY}" url += "&room_id=#{ASAKUSA_SATELLITE_ROOM_ID}" url += "&count=#{COUNT}" url += "&since_id=#{since_id}" if since_id messages = [] begin open(url) do |f| messages = JSON.load(f).map { |m| m["view"] = nil; m } end rescue => e warn e end messages.shift messages.each do |m| puts "@[heroku.message] " + m.to_json.gsub("\n", "") since_id = m["id"] end redis.set("since_id", since_id) sleep 300 end
必要な下準備などは README.md を参照してください。
Treasure Data でクエリを実行してみる
heroku td コマンドを使ってもいいのですが、
Heroku のアドオンページから Web インタフェースが使えるのでそちらを使ってみます。
アドオンページの 「Treasure Data」をクリックします。(コストが $0.00 なのも注目です。)
Heroku アドオンから Treasure Data を使った場合は、本家の Pricing よりかなりお得に使えます。
Treasure Data 社に感謝です。
ログインした直後の画面です。
Query をクリックして、クエリを打ち込み、Submit ボタンをクリックします。
クエリの実行には時間がかかるので、しばらく待ちます。
結果は TXT をクリックします。
banjun,8 suer,101 shimomura1004,64 mzp,68 atsumin,1 mallowlabs,107
Heroku 好きが誰かが一目瞭然です。
簡単ですね。
アクティビティ解析
AsakusaSatellite は開発者向けチャットアプリなので、開発のアクティビティが保存されています。
例えば Jenkins AsakusaSatellite Plugin を使っていれば、Jenkins のビルド結果がメッセージログに残っているはずです。
時間ごとのビルド回数を解析してみましょう。
Jenkins のビルド結果を拾うクエリを実行します。
SELECT SUBSTRING( v[ 'created_at' ] ,12 ,2 ) ,COUNT( 1 ) FROM message WHERE v[ 'body' ] LIKE '%SUCCESS%' OR v[ 'body' ] LIKE '%FAILURE%' OR v[ 'body' ] LIKE '%ABORTED%' OR v[ 'body' ] LIKE '%UNSTABLE%' GROUP BY SUBSTRING( v[ 'created_at' ] ,12 ,2 )
結果はこうなりました。
00,33 15,12 01,44 16,10 02,16 17,13 20,9 03,10 18,16 21,41 04,3 19,7 22,53 23,40 06,2 07,15 10,12 08,10 11,15 09,16 12,7 13,8 14,13
ソートして整形したものが以下です。
| 時 | ビルド回数 |
|---|---|
| 00 | 33 |
| 01 | 44 |
| 02 | 16 |
| 03 | 10 |
| 04 | 3 |
| 06 | 2 |
| 07 | 15 |
| 08 | 10 |
| 09 | 16 |
| 10 | 12 |
| 11 | 15 |
| 12 | 7 |
| 13 | 8 |
| 14 | 13 |
| 15 | 12 |
| 16 | 10 |
| 17 | 13 |
| 18 | 16 |
| 19 | 7 |
| 20 | 9 |
| 21 | 41 |
| 22 | 53 |
| 23 | 40 |
深夜に偏った非常に不健康なチームであることがわかりましたねw
(言い訳をしておくと、メンバは全員本職が別にあるのです)
時間ごとだけでなく、曜日ごとに分析してみたり、
ビルドの結果ごとに分析してみても面白いかもしれません。
まとめ
Treasure Data の Heroku アドオンがとにかく手軽です。
今回は約 60,000 レコードと、Treasure Data 社さんには申し訳ないくらいの小さなデータで試しましたが、
データ件数が増えても実行時間がほとんど変わらないのを見ると、その威力に驚きます。
また、AsakusaSatellite に通知していたビルド結果も、使い方によっては面白いことが出来そうだということがわかって、夢が広がります。
AsakusaSatellite と Treasure Data はどちらも面白いことができるアプリですので、ぜひ試してみてください。
2012-11-27
New Relic を Tomcat 7 (jsvc) で使ってみた
環境
- OS : CentOS 5.7
- JDK : 1.6.0_27
- Tomcat : 7.0.23
- Tomcat のインストールパス : /home/tomcat/
- New Relic Agent (Java) : 2.10.1
インストール
基本的には、New Relic の setup ページの通りに操作する。
newrelic_agent2.10.1.zip を展開し、
/home/tomcat/newrelic
となるように配置する。
# cd /home/tomcat/newrelic # mkdir logs # chmod +w logs # java -jar newrelic.jar install Nov 27, 2012 17:58:22 +0900 NewRelic 1 INFO: Agent is using Log4j ***** ( ( o)) New Relic Java Agent Installer ***** Installing version 2.10.1 ... Backed up start script to /home/tomcat/bin/catalina.sh.20121127_175822 Added agent switch to start script /home/tomcat/bin/catalina.sh No need to create New Relic configuration file because: .:. A config file already exists: /home/tomcat/newrelic/newrelic.yml ***** Install successful ***** Next steps: You're almost done! To see performance data for your app: .:. Restart your app server .:. Exercise your app .:. Log into http://rpm.newrelic.com Within two minutes, your app should show up, ready to monitor and troubleshoot. If app data doesn't appear, check newrelic/logs/newrelic_agent.log for errors.
インストーラの動きは、catalina.sh に以下の内容を書き込むだけらしい。
# diff /home/tomcat/bin/catalina.sh.20121127_175822 /home/tomcat/bin/catalina.sh 99a100,104 > > # ---- New Relic switch automatically added to start command on 2012 Nov 27, 17:58:22 > NR_JAR=/home/tomcat/newrelic/newrelic.jar; export NR_JAR > JAVA_OPTS="$JAVA_OPTS -javaagent:$NR_JAR"; export JAVA_OPTS >
通常はこれで OK なはずだが、Tomcat をデーモン化するために jsvc を使っている場合には、catalina.sh を読まないらしいのでうまくいかないっぽい。
今回試した環境は jsvc に付属している Tomcat7.sh というスクリプトで Tomcat を起動しているので、中身を読むと「JAVA_OPTS を上書きしたい場合は setenv.sh に書け」と書いてある。ので、その通りにする。
# vim /home/tomcat/bin/setenv.sh # cat /home/tomcat/bin/setenv.sh #!/bin/sh NR_JAR=/home/tomcat/newrelic/newrelic.jar; export NR_JAR JAVA_OPTS="$JAVA_OPTS -javaagent:$NR_JAR"; export JAVA_OPTS
あとは、Tomcat を再起動して、適当に Java アプリを動してしばらくすると New Relic 側でデータを見ることができるようになる。

