プログラム

ccchartでグラフを描画する

ccchartというグラフ描画ライブラリがとても使いやすくて便利だったのでメモ。もし実際に利用する場合は、サンプルやドキュメントが充実しているのでそちらを参照した方が良いと思います。

最近、REMPで運営者用の管理画面を作っていることは以前書きましたがその中で利用状況等を示すグラフを作りたくなったので今回利用してみました。

使い方

使い方はとてもシンプルで、まずライブラリの読み込みとグラフを描画する領域をHTML上に定義します。

<html>
<head>
  <script src="http://ccchart.com/js/ccchart.js" charset="utf-8"></script>
</head>
<body>
<canvas id="graph-area"></canvas>
</body>
</html>

これで準備は完了。で、この領域(=canvasタグ内)に今回はREMP で利用しているMongoDBの情報を参照してグラフを描画させたかったので、グラフのみ非同期で描画させてAPI経由でグラフに必要な情報を参照して描画させてみました。
まずAPIの準備。ccchartは、JSONオブジェクトをパラメータとしてグラフを描画する仕様になっているので、API側で描画に必要なパラメータを全て作って渡す様にしました。Padrino側でAPIをこしらえてあげます。

# app/controllers/foo.rb
get :graphvalues do
timeScale = ['']
fooNum = ['foo']
minYRange = 0
maxYRange = 1000
Score.where(:created_at => 10.day.ago..1.day.ago).each do |oneScore|
timeScale << "#{oneScore.created_at.day}"
fooNum   << oneScore.foo
end
title = 'foo'
data = [timeScale, fooNum]
colorSet = ["red"]
# JSON
graphValues = {
config: {
titleFont: "100 18px 'Arial'",
title: title,
type: "line",
lineWidth: 4,
minY: minYRange,
maxY: maxYRange,
width: 480,
height: 400,
colorSet: colorSet,
bgGradient: {direction:"vertical",from:"#687478",to:"#222222"}
},
data: data
}
content_type :json
graphValues.to_json
end

なんかごにょごよしてますが、要はccchartで定められているグラフ定義用のJSONをサーバサイドで作っているだけです。
次にフロント側(JS)で、APIから取得したJSONを利用してグラフを実際に描画します。

<script>
$(document).ready(function(){
$.ajax({
type: "GET",
dataType:'json',
scriptCharset: 'utf-8',
url: "/foo/graphvalues",
success: function(res){
ccchart.init('graph-area', res);
}
});
});
</script>

そうすると以下の様なグラフが描画されます。

グラフの種類も多いですし、必要に応じて使ってみようと思います。

ActiveRecordで結果を単純な配列を得る(特定のカラムの)

特定のカラムを指定して検索した結果は素直にすると、

Visitlog.select([:normalize_count]).where(:group_id => 3, :visitor => '123')
=> [#<Visitlog normalize_count: 0.0833333>, #<Visitlog normalize_count: 0.0833333>,...]

となるのだけど、これを [0.08333, 0.0833, ...] といった具合の配列で得たい場合、ActiveRecordにpluckというメソッドがあるのでそれを利用すればよかったことをやっと知った...。

Visitlog.where(:group_id => 3, :visitor => '2457892').pluck(:normalize_count)
=> [0.0833333, 0.0833333, ...]

mapしたりする方法とか散見したのだけど、この方法がスマートですね。

Ruby gem 'parallel' で並列処理

ふたまわり位遅れてますが、gemライブラリのparallelを使うと並列処理が簡単に書けて便利。
例として、とあるグループ別に記録されている訪問ログに対して各訪問者別の処理をする場合、以下の様な具合でかける。

require 'parallel'
processor_count = Parallel.processor_count #CPUコア数取得
Group.select(:id).each do |group|
visitors = Visitlog.select(:visitor).uniq.where(:group_id => group.id)
Parallel.each(visitors, in_threads: processor_count) do |visitor|
visitor_process(group.id, visitor.id)
end
end

Parallel.each の箇所で引数の in_threads で与えられた数だけスレッドが作られて適宜実行される。この場合、各訪問者に対して行われる処理は独立なのでこの書き方で大丈夫ですね。

"Redis To Go"でRedisを使う

RedisをSqaleで動かしている自作サービスから利用したいと思ったのですが、今、手元にすぐ利用できるサーバが無いので"Redis To Go"というRedisのホスティングサービスを利用してみました。
無償のプラン(Nanoプラン)であれば、

  • 5MbyteまでのDBが作成可能
  • ホスティング側でバックアップは取られない
  • 同時10接続まで

といった条件はありますが、お試しレベルであれば手軽に利用できそうなので試してみました。

https://redistogo.com/signup からサインアップして、ログインすると下の様な画面になるので、画面右上の"New Instance"をクリック。

新規インスタンス

いくつかプランが選択されるので無償プランの"Nano"を選択。

プラン選択

インスタンスを作成すると接続に必要なURIの情報が表示されるのでここをコピーしておきます。(redis://〜:〜@〜.redistogo.com:9007/ といった形式の文字列)

接続情報

で、実際にRubyから作成したインスタンスに接続してみます。

$ gem install redis

先に控えた接続用のURI情報を使って以下の様に接続。

require 'URI'
require 'redis'
uri = URI.parse("redis://〜:〜@〜
.redistogo.com:9007/")
redis = Redis.new(:host => uri.host, :port => uri.port, :password => uri.password)

実際にKey-Valueをストアしてみると。

irb(main):008:0> redis.set "foo", "bar"
=> "OK"
irb(main):009:0> redis.get "foo"
=> "bar"

といった具合で利用できています。
インスタンス自体はAWSのUS-EASTリージョンで動いている様なので日本国内から利用すると多少レスポンスまでにラグがありますが、超お手軽にRedisの利用を試すことができます。

Mongoidで更新日時の範囲で絞込み

2回調べたので記録しておく。

Status.where(:updated_at => 25.minute.ago..15.minute.ago)

どう見てもそのまんまだろうという話なのだが、情けないかな頭にパッと出てこない。
普段から常にARとかMongoid使っていないと駄目なんだろうなぁ...。

ImpasというAPIサービスを作った

個人でささやかにWebサービスを運営されている方向けにImpasというAPIサービスを作りました。

このサービスは「twitter上でのツイート数、facebook上でのいいね数、はてなブックマークでのブックマーク数で簡単にランキングを生成する」ことができるAPIサービスです。
以下、概要図。

About of Impas

Webアプリケーション開発者はImpasのAPIを使ってImpasにランキング対象にしたい(各種ソーシャルパラメータを計測したいURL)を登録することで定期的にImpasがURLをクロールしてパラメータを取得して記録します。その記録を利用してImpasはランキングを生成するので、再びWebアプリケーション開発者はAPIを通じてランキングを取得することができます。
RubyクライアントをGem(impas-client)で用意していますので、以下の様な形で利用することができます。

URLの登録例

# クライアントの設定
require 'impas-client'
client = Impas::Client.new()
# URLを集計対象へ追加
IMPAS_GRP_KEY = ******
client.add_url(
IMPAS_GRP_KEY,
"http://www.youtube.com/watch?v=2HQkugdXyHY"
)
# ランキング取得
fbRank = client.ranking IMPAS_GRP_KEY, "fb"
twRank = client.ranking IMPAS_GRP_KEY, "twitter"
hatenaRank = client.ranking IMPAS_GRP_KEY, "hatena"

あるいは、以下の様に直接POSTやGETリクエストでURLの登録やランキング情報の取得もできます。以下ははてなブックマークのトップ10ランキングを取得する場合。

http://impas-hideack.sqale.jp/api/ranking/IMPAS_GRP_KEY/hatena/10

ランキングはJSONで取得されます。

{
"result": "ok",
"explain": "",
"description": {
"ranking": [
{
"callcount": 16,
"fb": 0,
"hatena": 809,
"tw": 17643,
"url": "http://www.youtube.com/watch?v=MGt25mv4-2Q"
},
{
"callcount": 12,
"fb": 0,
"hatena": 368,
"tw": 10591,
"url": "http://www.youtube.com/watch?v=cfpX8lkaSdk"
},

ドキュメント等々不足している感はありますが、もし興味がある方が居られたら是非ご利用くださいませ。

ロゴ

REMPチームでお馴染み(?)の@があっという間にロゴ画像を作ってくれましたが、何故か大王イカなのかは昨今の話題からお察しください。*1
あと、サービス名の"Impas"はドラゴンクエストの魔法のインパスから拝借しました。

Impas

*1:あっという間に作ってくれたのですが、それでこのクオリティというのがすごい…

PadrinoアプリでNewrelic

New relic

New relicはパフォーマンス監視サービスでRackアプリケーションの場合、用意されたnewrelic_rpmというGemをインストールするのみでNew relicが用意するダッシュボードでページが表示されるまでのパフォーマンスの詳細な内訳(Ruby自体の処理時間、DBが要した処理時間やスロークエリ等)を知ることができます。
アカウント作成は無償でもできて、無償の場合、1アプリケーションの直近30分のパフォーマンスを知ることができます。
今回はSqaleで動かしているPadrinoアプリのパフォーマンス監視を行うまでの手順を整理します。

アカウントを作成する

Newrelicを開くとトップページに "Create Your Free Account!" のリンクがあるのでここからアカウントを作成します。

newrelic.yml をダウンロードする

アカウントを作成してログインするとnewrelic用の設定ファイル newrelic.yml をダウンロードできるのでダウンロードしてPadrinoプロジェクト下のconfigディレクトリに配置します。

Gemfileにnewrelic用のライブラリ追加

Padrinoアプリで利用しているGemfileにnewrelic用のクライアントをインストールするための2行を追加。

# Padrino Stable Gem
gem 'padrino', '0.10.7'
# for newrelic
gem 'padrino-rpm', :git => "https://github.com/Asquera/padrino-rpm.git"
gem 'newrelic_rpm'

2013.2.24追記

  • ここでポイントは gem 'padrino'〜 の下にnewrelic用のgemを追加することです
  • padrino読む前に追加するとエラーになります。

Unicorn用の設定追記

Sqaleでは標準でアプリケーションサーバunicornなのでこちらを参考にして起動時の設定ファイルに追記します。

# config/boot.rb
::NewRelic::Agent.after_fork(:force_reconnect => true)  # Unicorn用に追加
Padrino.load!

実行結果

以上の設定を行ってSqaleへデプロイします。
そうすると正常に稼働すれば、数分後にnew relicのダッシュボードにパフォーマンス情報が表示されます。

また、組み込んだPadrinoアプリ上の log/newrelic_agent.log からも確認できます。

sqale@impas-hideack-1:~$ tail -f current/log/newrelic_agent.log
[02/10/13 17:13:15 +0900 impas-hideack-1 (24017)] INFO : Connected to NewRelic Service at collector-5.newrelic.com:80
[02/10/13 17:13:15 +0900 impas-hideack-1 (24017)] INFO : Reporting performance data every 60 seconds.
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Dispatcher: sinatra
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Application: Impas
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : New Relic Ruby Agent 3.3.5 Initialized: pid = 24175
[02/10/13 18:13:03 +0900 impas-hideack-1 (24175)] INFO : Agent Log at /home/sqale/current/log/newrelic_agent.log
[02/10/13 18:13:13 +0900 impas-hideack-1 (24175)] INFO : Resolved collector.newrelic.com to 204.93.223.153
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Resolved collector-5.newrelic.com to 204.93.223.139
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Connected to NewRelic Service at collector-5.newrelic.com:80
[02/10/13 18:13:14 +0900 impas-hideack-1 (24175)] INFO : Reporting performance data every 60 seconds.

機能を追加した際等々の折にアプリケーション側のパフォーマンス変化を気にしたい場合などに気楽に確認できるため便利です。

ストーリーボードのバックエンド等々のお話

ストーリーボードをSqale(http://sqale.jp )へ運用を移管したのは以前簡単にここにも書きましたが、一度どういった技術を使っているのかまとめがてら整理してみます。

実装

現在、実装周りでは、

などを利用しています。

Youtubeの動画を連続して再生することができるYoutubeプレイヤー REMP(http://remp.rockf.es/ )のころからのスタイルでサーバ再度側は基本的にAPIが主体となる様な設計になっており、WebUI上からの操作をJSが吸収して、サーバサイドのAPIを叩き、その応答をJSが再びUIへ反映させるといった構成になっています。
私が担当したサーバサイド側の実装では、大きく分けて、

  • 最低限必要なページの生成
  • 編集されたプレゼンテーションドキュメントの保管
  • プレゼンテーションに利用する画像の保管
  • プレゼンテーション操作(ページめくり等)が行われた際のページめくり情報のWebsocket配信

等を行なっています。

プレゼンテーション自体のデータ(=Markdown記述)は現在Sqaleでは共用MySQLサーバは提供されていますが、今回はMongoDBを利用しているため、ここのみ外出ししたMongoDBに接続し保管しています。
また、プレゼンテーション用にアップロードされた画像はヘテムル(http://heteml.jp )へAmazonS3互換の動きをするfS3を導入してアップロードされた画像をストーリーボードのサーバーサイドプログラム側からS3プロトコルで画像をストアしています。

更にストーリーボードの一つの特徴であるページめくり等を同じ時間にプレゼンテーションページを閲覧しているユーザにページめくり操作をマルチキャストしたり、閲覧中のプレゼンテーションに対してリアルタイムで"いいね!"をすることができる機能を実現するためにWebsocketを導入していますが、ここは現在Websocket ASPサービスであるPUSHERを使っています*1

監視サービス

監視サービスですが、Pingdomを使っています。
基本有償サービスですが、1サイトのみであれば無償で利用できます。

HTTP疎通の死活監視を行なってくれるので、最低限の状況は把握できるので役になっています。

利用状況解析

お約束感がありますが、mixpanelgoogle analyticsを使っています。
RubygemsmixpanelというGemがあるので、これを利用してストーリーボード上のAPIが呼び出される度にログを記録する様にしています。
基本、ストーリーボード上で何らかの操作が行われるとストーリボードのサーバサイドAPIが叩かれるので、ページめくりAPIが多いとどこかのプレゼンテーションの発表に利用されたかな。とか利用状況がわかったりします。

こんなかんじです

以上大雑把ではありますが、ストーリーボードの実装概要をまとめてみました。
今後も機能をどんどん足していくし(と思うし)、新しい技術も今後積極的に入れていきたいのでまたある程度溜まったら記事を書こうかと思います。

*1:いつか自前で実装するといつつ、すごく気楽に利用できるので使い続けてます…